chttpserverchunked-encodinghttp-chunked

Chunked transfer encoding - Page is not displayed properly


My task is to implement simple HTTP server. I should support chunked transfer encoding when I send response. Here is my function that send response to client.

static int serve_request(int sock, struct conf_arg *arg, char version[])
{
    FILE *html = NULL;
    char buf[MAX_MSG];

    strcat(arg->root, arg->defdoc);
    html = fopen(arg->root, "r");
    if (!html) {
        not_found(sock, version);
        return 0;
    }
    good_responce(sock, version);
    do {
        fgets(buf, sizeof(buf), html);
        const unsigned chunk = CHUNK_SIZE;
        char *pbuf = buf;
        char tempbuf[chunk + 10];

        while (strlen(pbuf) >= chunk) {
            sprintf(tempbuf, "%x\r\n", chunk);
            write(sock, tempbuf, strlen(tempbuf));
            write(sock, pbuf, chunk);
            pbuf += chunk;
            strcpy(tempbuf, "\r\n");
            write(sock, tempbuf, strlen(tempbuf));
        }
        if (strlen(pbuf) == 0) {
            sprintf(tempbuf, "%x\r\n", 0);
            write(sock, tempbuf, strlen(tempbuf));
        }
        if (strlen(pbuf) > 0) {
            sprintf(tempbuf, "%x\r\n", (unsigned)strlen(pbuf));
            write(sock, tempbuf, strlen(tempbuf));
            write(sock, pbuf, strlen(pbuf));
            sprintf(tempbuf, "%x\r\n", 0);
            write(sock, tempbuf, strlen(tempbuf));
        }
        strcpy(tempbuf, "\r\n");
        write(sock, tempbuf, strlen(tempbuf));
    } while (!feof(html));
    fclose(html);
    return 0;
}

CHUNK_SIZE is defined as 1024 because I want to sent chunks with 1KB size. The problem occurs when I open page enter image description here The page is not displayed properly. Also I set Transfer-Encoding: chunked

strcpy(buf, ENCODING);
send(sock, buf, strlen(buf), 0);

ENCODING is defined as "Transfer-Encoding: chunked\r\n"


Solution

  • I think I know where the problem is, but am not entirely sure.

    In your do loop, you get a buf full of data and you send that. Then you get another buffer full of data and send that. But after having sent each buffer of data, you terminate the transfer by sending a 0\r\n. For example:

    1024    // send first chunk
    1024    // send second chunk
    256     // last part of first bufer
    0       // terminate transfer
    1024    // send first chunk of second buffer
    1024    //...
    256
    0
    

    Although it may be better to fill your buffer again before sending the last chunk (use memmove to move the last part down, then call fgets to fill the remainder), you may be "saved" by sending the 0\r\n only after the do ... while loop, e.g.:

            if (strlen(pbuf) > 0) {
                sprintf(tempbuf, "%x\r\n", (unsigned)strlen(pbuf));
                write(sock, tempbuf, strlen(tempbuf));
                write(sock, pbuf, strlen(pbuf));
                //sprintf(tempbuf, "%x\r\n", 0);
                //write(sock, tempbuf, strlen(tempbuf));
            }
            //strcpy(tempbuf, "\r\n");
            //write(sock, tempbuf, strlen(tempbuf));
        } while (!feof(html));
        sprintf(tempbuf, "%x\r\n", 0);
        write(sock, tempbuf, strlen(tempbuf));
        strcpy(tempbuf, "\r\n");
        write(sock, tempbuf, strlen(tempbuf));
    

    Note also that you must check the result of fgets as it can return zero upon eof; the buffer will not have been refreshed and you will send the last part again:

        if (fgets(buf, sizeof(buf), html)==NULL) break;
    

    See also the comments about your unnecessary use of tempbuf.