summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Porzelt <tp@cryosphere.de>2009-09-30 23:31:03 +0200
committerThomas Porzelt <tp@cryosphere.de>2009-09-30 23:31:03 +0200
commitb9a679e2f51c8c0efafc97d27fe0e1116ec21969 (patch)
treedb6d409d5d182b41b495b95afb6c86c5daae3d9f
parent9d7e517a0d6beb7a3d93cb40e0ee811b0457f2bb (diff)
downloadweighttp-b9a679e2f51c8c0efafc97d27fe0e1116ec21969.tar.gz
weighttp-b9a679e2f51c8c0efafc97d27fe0e1116ec21969.zip
add chunked encoding support
-rw-r--r--TODO3
-rw-r--r--src/client.c124
-rw-r--r--src/client.h3
3 files changed, 116 insertions, 14 deletions
diff --git a/TODO b/TODO
index 0a52918..e6a001a 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,7 @@
+- handle responses without content-length but connection: close
- timing statistics
- generally better statistics
-- chunked encoding support
- ssl support
- better error reporting
- pipelining
+- fuzzer
diff --git a/src/client.c b/src/client.c
index d637ba0..0e8f868 100644
--- a/src/client.c
+++ b/src/client.c
@@ -10,7 +10,7 @@
#include "weighttp.h"
-static uint8_t client_parse(Client *client);
+static uint8_t client_parse(Client *client, int size);
static void client_io_cb(struct ev_loop *loop, ev_io *w, int revents);
static void client_set_events(Client *client, int events);
/*
@@ -66,6 +66,9 @@ Client *client_new(Worker *worker) {
client->buffer_offset = 0;
client->request_offset = 0;
client->keepalive = client->worker->config->keep_alive;
+ client->chunked = 0;
+ client->chunk_size = -1;
+ client->chunk_received = 0;
return client;
}
@@ -109,6 +112,9 @@ static void client_reset(Client *client) {
client->bytes_received = 0;
client->header_size = 0;
client->keepalive = client->worker->config->keep_alive;
+ client->chunked = 0;
+ client->chunk_size = -1;
+ client->chunk_received = 0;
}
static uint8_t client_connect(Client *client) {
@@ -220,7 +226,7 @@ void client_state_machine(Client *client) {
case CLIENT_READING:
while (1) {
r = read(client->sock_watcher.fd, &client->buffer[client->buffer_offset], sizeof(client->buffer) - client->buffer_offset - 1);
- //printf("read(): %d\n", r);
+ //printf("read(): %d, offset was: %d\n", r, client->buffer_offset);
if (r == -1) {
/* error */
if (errno == EINTR)
@@ -241,7 +247,7 @@ void client_state_machine(Client *client) {
}
client->buffer[client->buffer_offset] = '\0';
//printf("buffer:\n==========\n%s\n==========\n", client->buffer);
- if (!client_parse(client)) {
+ if (!client_parse(client, r)) {
client->state = CLIENT_ERROR;
//printf("parser failed\n");
break;
@@ -299,7 +305,7 @@ void client_state_machine(Client *client) {
}
-static uint8_t client_parse(Client *client) {
+static uint8_t client_parse(Client *client, int size) {
char *end, *str;
switch (client->parser_state) {
@@ -336,7 +342,7 @@ static uint8_t client_parse(Client *client) {
client->header_size = end + 2 - client->buffer;
//printf("body reached\n");
- return client_parse(client);
+ return client_parse(client, size - client->header_size);
}
*end = '\0';
@@ -358,15 +364,25 @@ static uint8_t client_parse(Client *client) {
client->keepalive = client->worker->config->keep_alive;
else
return 0;
+ } else if (strncmp(str, "Transfer-Encoding: ", sizeof("Transfer-Encoding: ")-1) == 0) {
+ /* transfer encoding header */
+ str += sizeof("Transfer-Encoding: ") - 1;
+
+ if (strncmp(str, "chunked", sizeof("chunked")-1) == 0)
+ client->chunked = 1;
+ else
+ return 0;
}
+
if (*(end+2) == '\r' && *(end+3) == '\n') {
/* body reached */
client->parser_state = PARSER_BODY;
client->header_size = end + 4 - client->buffer;
+ client->parser_offset = client->header_size;
//printf("body reached\n");
- return client_parse(client);
+ return client_parse(client, size - client->header_size);
}
client->parser_offset = end - client->buffer + 2;
@@ -378,15 +394,97 @@ static uint8_t client_parse(Client *client) {
/* do nothing, just consume the data */
/*printf("content-l: %"PRIu64", header: %d, recevied: %"PRIu64"\n",
client->content_length, client->header_size, client->bytes_received);*/
- client->buffer_offset = 0;
- if (client->content_length == -1)
- return 0;
+ if (client->chunked) {
+ int consume_max;
+
+ str = &client->buffer[client->parser_offset];
+ /*printf("parsing chunk: '%s'\n(%"PRIi64" received, %"PRIi64" size, %d parser offset)\n",
+ str, client->chunk_received, client->chunk_size, client->parser_offset
+ );*/
+
+ if (client->chunk_size == -1) {
+ /* read chunk size */
+ client->chunk_size = 0;
+ client->chunk_received = 0;
+ end = str + size;
+
+ for (; str < end; str++) {
+ if (*str == ';' || *str == '\r')
+ break;
+
+ client->chunk_size *= 16;
+ if (*str >= '0' && *str <= '9')
+ client->chunk_size += *str - '0';
+ else if (*str >= 'A' && *str <= 'Z')
+ client->chunk_size += 10 + *str - 'A';
+ else if (*str >= 'a' && *str <= 'z')
+ client->chunk_size += 10 + *str - 'a';
+ else
+ return 0;
+ }
+
+ str = strstr(str, "\r\n");
+ if (!str)
+ return 0;
+ str += 2;
+
+ //printf("---------- chunk size: %"PRIi64", %d read, %d offset, data: '%s'\n", client->chunk_size, size, client->parser_offset, str);
+
+ if (client->chunk_size == 0) {
+ /* chunk of size 0 marks end of content body */
+ client->state = CLIENT_END;
+ client->success = client->status_200 ? 1 : 0;
+ return 1;
+ }
+
+ size -= str - &client->buffer[client->parser_offset];
+ client->parser_offset = str - client->buffer;
+ }
+
+ /* consume chunk till chunk_size is reached */
+ consume_max = client->chunk_size - client->chunk_received;
- if (client->bytes_received == (uint64_t) (client->header_size + client->content_length)) {
- /* full response received */
- client->state = CLIENT_END;
- client->success = client->status_200 ? 1 : 0;
+ if (size < consume_max)
+ consume_max = size;
+
+ client->chunk_received += consume_max;
+ client->parser_offset += consume_max;
+
+ //printf("---------- chunk consuming: %d, received: %"PRIi64" of %"PRIi64", offset: %d\n", consume_max, client->chunk_received, client->chunk_size, client->parser_offset);
+
+ if (client->chunk_received == client->chunk_size) {
+ if (client->buffer[client->parser_offset] != '\r' || client->buffer[client->parser_offset+1] != '\n')
+ return 0;
+
+ /* got whole chunk, next! */
+ //printf("---------- got whole chunk!!\n");
+ client->chunk_size = -1;
+ client->chunk_received = 0;
+ client->parser_offset += 2;
+ consume_max += 2;
+
+ /* there is stuff left to parse */
+ if (size - consume_max > 0)
+ return client_parse(client, size - consume_max);
+ }
+
+ client->parser_offset = 0;
+ client->buffer_offset = 0;
+
+ return 1;
+ } else {
+ /* not chunked, just consume all data till content-length is reached */
+ client->buffer_offset = 0;
+
+ if (client->content_length == -1)
+ return 0;
+
+ if (client->bytes_received == (uint64_t) (client->header_size + client->content_length)) {
+ /* full response received */
+ client->state = CLIENT_END;
+ client->success = client->status_200 ? 1 : 0;
+ }
}
return 1;
diff --git a/src/client.h b/src/client.h
index 29c37b0..54ee5a7 100644
--- a/src/client.h
+++ b/src/client.h
@@ -34,6 +34,9 @@ struct Client {
uint8_t keepalive;
uint8_t success;
uint8_t status_200;
+ uint8_t chunked;
+ int64_t chunk_size;
+ int64_t chunk_received;
int64_t content_length;
uint64_t bytes_received; /* including http header */
uint16_t header_size;