Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.os.linux.networking > #2196 > unrolled thread

HTTP POST for file upload

Started byJosef Moellers <josef.moellers@invalid.invalid>
First post2013-06-05 19:40 +0200
Last post2013-06-06 21:51 +0000
Articles 5 — 4 participants

Back to article view | Back to comp.os.linux.networking


Contents

  HTTP POST for file upload Josef Moellers <josef.moellers@invalid.invalid> - 2013-06-05 19:40 +0200
    Re: HTTP POST for file upload Xavier Roche <xroche@free.fr.NOSPAM.invalid> - 2013-06-05 19:51 +0200
    Re: HTTP POST for file upload Richard Kettlewell <rjk@greenend.org.uk> - 2013-06-05 18:53 +0100
    Re: HTTP POST for file upload Josef Moellers <josef.moellers@invalid.invalid> - 2013-06-06 21:26 +0200
      Re: HTTP POST for file upload Jorgen Grahn <grahn+nntp@snipabacken.se> - 2013-06-06 21:51 +0000

#2196 — HTTP POST for file upload

FromJosef Moellers <josef.moellers@invalid.invalid>
Date2013-06-05 19:40 +0200
SubjectHTTP POST for file upload
Message-ID<b19bg3FpriuU1@mid.individual.net>
Hi,

I'm trying to extend an application to upload result files to a web
server using HTMP POST. By and large it works but the last couple of
bytes (the last line, to be precise) does not show up in the received
data. Maybe someone more knowing than I has an idea what I'm doing wrong.

Thanks in advance,

Josef


Here's my code:

# include	<sys/types.h>
# include	<sys/socket.h>
# include	<stdio.h>
# include	<string.h>
# include	<unistd.h>
# include	<netdb.h>
# include	<stdlib.h>
# include	<arpa/inet.h>

int upload(char *archive_name, char *local_name, char *remote_name, char
*servname);
static ssize_t writen(int sd, void *ptr, size_t nbytes);
static ssize_t readline(int sd, void *ptr, size_t maxlen);

char servname[] = "de2server";
char hostname[128];
/*
 * upload <archive> <file> <remote-name>
 */
int
main(int argc, char *argv[])
{
    if (argc != 4)
    {
        fprintf(stderr, "usage: %s <archive> <file> <remote-name>\n",
argv[0]);
	exit(1);
    }
    gethostname(hostname, sizeof(hostname));
    upload(argv[1], argv[2], argv[3], servname);
}

void
pr_inet(char **listptr, int length)
{
    struct in_addr *ptr;
    while ((ptr = (struct in_addr *) *listptr++) != NULL)
        printf("    %s\n", inet_ntoa(*ptr));
}
int
upload(char *archive_name, char *local_name, char *remote_name, char
*servname)
{
    int sd;
    struct sockaddr_in servaddr;
    struct hostent *he;
    FILE *src, *tmpfp;
    char buffer[BUFSIZ];
    char boundary[128];
    char line[256];
    size_t nread, content_length;

    if ((he = gethostbyname(servname)) == NULL)
    {
        fprintf(stderr, "Cannot find %s\n", servname);
	return 0;
    }
    pr_inet(he->h_addr_list, he->h_length);

    if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket");
	return 0;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(80);	/* Web port */
    memcpy(&servaddr.sin_addr, he->h_addr, he->h_length);

    if (connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    {
        perror("connect");
	close(sd);
	return 0;
    }

    /* Let's assemble the body in a file so we can determine the
Content-Length */
    if ((src = fopen(local_name, "r")) == NULL)
    {
        perror(local_name);
        close(sd);
	return 0;
    }
    if ((tmpfp = tmpfile()) == NULL)
    {
        perror("tmpfile");
	close(sd);
	fclose(src);
	return 0;
    }

    snprintf(boundary, sizeof(boundary),
"WebKitFormBoundaryePkpFF7tjBAqx29L");
    content_length = 0;
    content_length += fprintf(tmpfp, "\r\n");	/* This belongs to the
content */
    content_length += fprintf(tmpfp, "------%s\r\n", boundary);
    content_length += fprintf(tmpfp, "Content-Disposition: form-data;
name=\"archive_name\"\r\n");
    content_length += fprintf(tmpfp, "\r\n");
    content_length += fprintf(tmpfp, "%s\r\n", archive_name);
    content_length += fprintf(tmpfp, "------%s\r\n", boundary);
    content_length += fprintf(tmpfp, "Content-Disposition: form-data;
name=\"destination_name\"\r\n");
    content_length += fprintf(tmpfp, "\r\n");
    content_length += fprintf(tmpfp, "%s\r\n", remote_name);
    content_length += fprintf(tmpfp, "------%s\r\n", boundary);
    content_length += fprintf(tmpfp, "Content-Disposition: form-data;
name=\"file_contents\"; filename=\"%s\"\r\n", local_name);
    content_length += fprintf(tmpfp, "Content-Type:
application/x-object\r\n");
    content_length += fprintf(tmpfp, "\r\n");
    while ((nread = fread(buffer, 1, sizeof(buffer), src)) > 0)
    {
    	fwrite(buffer, 1, nread, tmpfp);
	content_length += nread;
    }
    content_length += fprintf(tmpfp, "------%s--\r\n", boundary);
    fprintf(stderr, "content_length=%u, file length=%u\n",
content_length, ftell(tmpfp));
    fclose(src);	/* We don't need that any more */

    /* Now send request */
    snprintf(line, sizeof(line), "POST
/cgi-bin/zip.pl?upload_progress_id=12344 HTTP/1.1\r\n");
    writen(sd, line, strlen(line));
    snprintf(line, sizeof(line), "Host: %s\r\n", hostname);
    writen(sd, line, strlen(line));
    snprintf(line, sizeof(line), "Content-Length: %u\r\n", content_length);
    writen(sd, line, strlen(line));
    snprintf(line, sizeof(line), "Origin: http://%s\r\n", hostname);
    writen(sd, line, strlen(line));
    snprintf(line, sizeof(line), "Content-Type: multipart/form-data;
boundary=----%s\r\n", boundary);
    writen(sd, line, strlen(line));
    /* Copy file to sd */
    rewind(tmpfp);
    while ((nread = fread(buffer, 1, sizeof(buffer), tmpfp)) > 0)
    	writen(sd, buffer, nread);
    /*
     * Now await response
     */
    while (readline(sd, line, sizeof(line)))
	fprintf(stderr, ">>%s", line);

    close(sd);
    fclose(tmpfp);

    return 1;
}

static ssize_t
writen(int sd, void *ptr, size_t nbytes)
{
    ssize_t nleft, nwritten;
    fd_set writefds;
    struct timeval tensec;

    for (nleft = nbytes; nleft > 0; )
    {
        /* Wait up to 10s for permission to write, then give up */

        FD_ZERO(&writefds);
        FD_SET(sd, &writefds);
        tensec.tv_sec = 10;
        tensec.tv_usec = 0;
        if (select(sd+1, NULL, &writefds, NULL, &tensec) != 1)
            nwritten = -1;
        else
            nwritten = write(sd, ptr, nleft);

        if (nwritten == -1)
            return -1;
        nleft -= nwritten;
        ptr += nwritten;
    }

    return nbytes - nleft;
}

static ssize_t
readline(int sd, void *ptr, size_t maxlen)
{
    char *sp = ptr;
    ssize_t rc;
    int n;
    char c;
    fd_set readfds;
    struct timeval tensec;

    for (n = 1; n < maxlen; n++)
    {
        /* Wait up to 10s for data, then give up */

        FD_ZERO(&readfds);
        FD_SET(sd, &readfds);
        tensec.tv_sec = 10;
        tensec.tv_usec = 0;
        if (select(sd+1, &readfds, NULL, NULL, &tensec) != 1)
            rc = 0;
        else
            rc = read(sd, &c, 1);

        if (rc == 1)
        {
            *sp++ = c;
            if (c == '\n')
                break;
        }
        else if (rc == 0)       /* EOF */
        {
            if (n == 1)
                return 0;
            else
                break;
        }
        else
            return -1;
    }

    *sp = '\0';

    return n;
}
-eoc-

[toc] | [next] | [standalone]


#2197

FromXavier Roche <xroche@free.fr.NOSPAM.invalid>
Date2013-06-05 19:51 +0200
Message-ID<kontr2$nln$1@news.httrack.net>
In reply to#2196
Le 05/06/2013 19:40, Josef Moellers a écrit :
>     content_length = 0;
>     content_length += fprintf(tmpfp, "\r\n");	/* This belongs to the
> content */

No, this belongs to the HTTP headers.

<http://www.ietf.org/rfc/rfc2616.txt>
(...)
4 HTTP Message

4.1 Message Types
(...)
   Request (section 5) and Response (section 6) messages use the generic
   message format of RFC 822 [9] for transferring entities (the payload
   of the message). Both types of message consist of a start-line, zero
   or more header fields (also known as "headers"), an empty line (i.e.,
   a line with nothing preceding the CRLF) indicating the end of the
   header fields, and possibly a message-body.

[toc] | [prev] | [next] | [standalone]


#2198

FromRichard Kettlewell <rjk@greenend.org.uk>
Date2013-06-05 18:53 +0100
Message-ID<871u8gqw9i.fsf@araminta.anjou.terraraq.org.uk>
In reply to#2196
Josef Moellers <josef.moellers@invalid.invalid> writes:
> I'm trying to extend an application to upload result files to a web
> server using HTMP POST. By and large it works but the last couple of
> bytes (the last line, to be precise) does not show up in the received
> data. Maybe someone more knowing than I has an idea what I'm doing wrong.

Have you considered using a library such as cURL?

>     content_length += fprintf(tmpfp, "\r\n");	/* This belongs to the
> content */

No it doesn’t...

-- 
http://www.greenend.org.uk/rjk/

[toc] | [prev] | [next] | [standalone]


#2199

FromJosef Moellers <josef.moellers@invalid.invalid>
Date2013-06-06 21:26 +0200
Message-ID<b1c63vFedj3U1@mid.individual.net>
In reply to#2196
On 06/05/2013 07:40 PM, Josef Moellers wrote:
> Hi,
> 
> I'm trying to extend an application to upload result files to a web
> server using HTMP POST. By and large it works but the last couple of
> bytes (the last line, to be precise) does not show up in the received
> data. Maybe someone more knowing than I has an idea what I'm doing wrong.

I found the solution in RFC 1341
(<http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html>:

7.2.1 Multipart: The common syntax
:
Note that the encapsulation boundary must occur at the beginning of a
line, i.e., following a CRLF, and that that initial CRLF is considered
to be part of the encapsulation boundary rather than part of the
preceding part.
:

So I changed my code to

>     snprintf(boundary, sizeof(boundary),
> "WebKitFormBoundaryePkpFF7tjBAqx29L");
>     content_length = 0;
>     content_length += fprintf(tmpfp, "\r\n------%s\r\n", boundary);
>     content_length += fprintf(tmpfp, "Content-Disposition: form-data;
> name=\"archive_name\"\r\n");
>     content_length += fprintf(tmpfp, "\r\n");
>     content_length += fprintf(tmpfp, "%s", archive_name);
>     content_length += fprintf(tmpfp, "\r\n------%s\r\n", boundary);
>     content_length += fprintf(tmpfp, "Content-Disposition: form-data;
> name=\"destination_name\"\r\n");
>     content_length += fprintf(tmpfp, "\r\n");
>     content_length += fprintf(tmpfp, "%s", remote_name);
>     content_length += fprintf(tmpfp, "\r\n------%s\r\n", boundary);
>     content_length += fprintf(tmpfp, "Content-Disposition: form-data;
> name=\"file_contents\"; filename=\"%s\"\r\n", local_name);
>     content_length += fprintf(tmpfp, "Content-Type:
> application/x-object\r\n");
>     content_length += fprintf(tmpfp, "\r\n");
>     while ((nread = fread(buffer, 1, sizeof(buffer), src)) > 0)
>     {
>     	fwrite(buffer, 1, nread, tmpfp);
> 	content_length += nread;
>     }
>     content_length += fprintf(tmpfp, "\r\n------%s--\r\n", boundary);

Apparently the Perl CGI code was tolerant and took the "}\n" as the CRLF
preceding the last boundary and dropped that!

Another problem, not related to this one, was that I had to explicitly
close the write side of the socket in order to reliably receive the
response.

Thanks for both who helped (and all who thought about the issue).

Josef

[toc] | [prev] | [next] | [standalone]


#2200

FromJorgen Grahn <grahn+nntp@snipabacken.se>
Date2013-06-06 21:51 +0000
Message-ID<slrnkr212j.3jn.grahn+nntp@frailea.sa.invalid>
In reply to#2199
On Thu, 2013-06-06, Josef Moellers wrote:
> On 06/05/2013 07:40 PM, Josef Moellers wrote:
>> Hi,
>> 
>> I'm trying to extend an application to upload result files to a web
>> server using HTMP POST. By and large it works but the last couple of
...
> Thanks for both who helped (and all who thought about the issue).

I thought briefly about it, but ...

It's easier (for others) to look at this kind of problem in terms of
what is actually transmitted and received on the TCP stream level in
an example run: any mistake in your code will end up as a protocol
violation there -- and we don't have to decipher your C code.

You can use tcpdump, tcpflow and similar tools to capture that data.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .

[toc] | [prev] | [standalone]


Back to top | Article view | comp.os.linux.networking


csiph-web