diff options
author | Mohamed Abbas <mabbas@linux.intel.com> | 2010-11-05 18:20:46 -0700 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2010-11-06 13:34:36 +0100 |
commit | 88c50711977c0106739a35913d9606007b759910 (patch) | |
tree | dace236fe79f61aa61d8e3f4c4e392d0eeb8f091 /gweb/gweb.c | |
parent | 76df37ca47b372b1bf1164c9c4d82d687e47099e (diff) | |
download | connman-88c50711977c0106739a35913d9606007b759910.tar.gz connman-88c50711977c0106739a35913d9606007b759910.tar.bz2 connman-88c50711977c0106739a35913d9606007b759910.zip |
Support chunk encode in HTTP response.
If chunk encode is used them make sure we strip the chunk
body only.
Diffstat (limited to 'gweb/gweb.c')
-rw-r--r-- | gweb/gweb.c | 166 |
1 files changed, 159 insertions, 7 deletions
diff --git a/gweb/gweb.c b/gweb/gweb.c index fe579ec8..b0bad593 100644 --- a/gweb/gweb.c +++ b/gweb/gweb.c @@ -40,10 +40,18 @@ #define SESSION_FLAG_USE_TLS (1 << 0) +enum chunk_state { + CHUNK_SIZE, + CHUNK_R_BODY, + CHUNK_N_BODY, + CHUNK_DATA, +}; + struct _GWebResult { guint16 status; const guint8 *buffer; gsize length; + gboolean use_chunk; }; struct web_session { @@ -70,6 +78,11 @@ struct web_session { gboolean more_data; gboolean request_started; + enum chunk_state chunck_state; + gsize chunk_size; + gsize chunk_left; + gsize total_len; + GWebResult result; GWebResultFunc result_func; @@ -456,6 +469,125 @@ static gboolean send_data(GIOChannel *channel, GIOCondition cond, return FALSE; } +static int decode_chunked(struct web_session *session, + const guint8 *buf, gsize len) +{ + const guint8 *ptr = buf; + gsize counter; + + while (len > 0) { + guint8 *pos; + gsize count; + char *str; + + switch (session->chunck_state) { + case CHUNK_SIZE: + pos = memchr(ptr, '\n', len); + if (pos == NULL) { + g_string_append_len(session->current_header, + (gchar *) ptr, len); + return 0; + } + + count = pos - ptr; + if (count < 1 || ptr[count - 1] != '\r') + return -EILSEQ; + + g_string_append_len(session->current_header, + (gchar *) ptr, count); + + len -= count + 1; + ptr = pos + 1; + + str = session->current_header->str; + + counter = strtoul(str, NULL, 16); + if ((counter == 0 && errno == EINVAL) || + counter == ULONG_MAX) + return -EILSEQ; + + session->chunk_size = counter; + session->chunk_left = counter; + + session->chunck_state = CHUNK_DATA; + break; + case CHUNK_R_BODY: + if (*ptr != '\r') + return -EILSEQ; + ptr++; + len--; + session->chunck_state = CHUNK_N_BODY; + break; + case CHUNK_N_BODY: + if (*ptr != '\n') + return -EILSEQ; + ptr++; + len--; + session->chunck_state = CHUNK_SIZE; + break; + case CHUNK_DATA: + if (session->chunk_size == 0) { + debug(session->web, "Download Done in chunk"); + g_string_truncate(session->current_header, 0); + return 0; + } + + if (session->chunk_left <= len) { + session->result.buffer = ptr; + session->result.length = session->chunk_left; + call_result_func(session, 0); + + len -= session->chunk_left; + ptr += session->chunk_left; + + session->total_len += session->chunk_left; + session->chunk_left = 0; + + g_string_truncate(session->current_header, 0); + session->chunck_state = CHUNK_R_BODY; + break; + } + /* more data */ + session->result.buffer = ptr; + session->result.length = len; + call_result_func(session, 0); + + session->chunk_left -= len; + session->total_len += len; + + len -= len; + ptr += len; + break; + } + } + + return 0; +} + +static int handle_body(struct web_session *session, + const guint8 *buf, gsize len) +{ + int err; + + if (session->result.use_chunk == FALSE) { + session->result.buffer = buf; + session->result.length = len; + call_result_func(session, 0); + return 0; + } + + err = decode_chunked(session, buf, len); + if (err < 0) { + debug(session->web, "Error in chunk decode %d", err); + + session->result.buffer = NULL; + session->result.length = 0; + call_result_func(session, 400); + } + + return err; +} + static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { @@ -489,9 +621,11 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, session->receive_buffer[bytes_read] = '\0'; if (session->header_done == TRUE) { - session->result.buffer = session->receive_buffer; - session->result.length = bytes_read; - call_result_func(session, 0); + if (handle_body(session, + session->receive_buffer, bytes_read) < 0) { + session->transport_watch = 0; + return FALSE; + } return TRUE; } @@ -518,13 +652,17 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, (gchar *) ptr, count); bytes_read -= count + 1; - ptr = pos + 1; + if (bytes_read > 0) + ptr = pos + 1; + else + ptr = NULL; if (session->current_header->len == 0) { session->header_done = TRUE; - session->result.buffer = pos + 1; - session->result.length = bytes_read; - call_result_func(session, 0); + if (handle_body(session, ptr, bytes_read) < 0) { + session->transport_watch = 0; + return FALSE; + } break; } @@ -535,6 +673,20 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, if (sscanf(str, "HTTP/%*s %u %*s", &code) == 1) session->result.status = code; + } else if (session->result.use_chunk == FALSE && + g_ascii_strncasecmp("Transfer-Encoding:", + str, 18) == 0) { + char *val; + + val = g_strrstr(str + 18, "chunked"); + if (val != NULL) { + session->result.use_chunk = TRUE; + + session->chunck_state = CHUNK_SIZE; + session->chunk_left = 0; + session->chunk_left = 0; + session->total_len = 0; + } } debug(session->web, "[header] %s", str); |