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"
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
.