From: Mohamed Abbas <mab...@linux.intel.com> Add support to handle chunk encoding in response data. --- gweb/gweb.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 152 insertions(+), 0 deletions(-)
diff --git a/gweb/gweb.c b/gweb/gweb.c index 6280c89..eea6276 100644 --- a/gweb/gweb.c +++ b/gweb/gweb.c @@ -37,6 +37,18 @@ #define WEB_FLAG_HEADER_READY (0x1) #define WEB_FLAG_DOWNLOAD_DONE (0x2) +#define WEB_FLAG_USE_CHUNK (0x4) + +#define MAX_CHUNK_LEN 17 + +enum chunk_state { + CHUNK_SIZE, + CHUNK_R, + CHUNK_R_BODY, + CHUNK_N, + CHUNK_N_BODY, + CHUNK_DATA, +}; struct web_session { GWeb *web; @@ -52,6 +64,10 @@ struct web_session { guint resolv_action; char *request; + enum chunk_state chunck_state; + int chunk_size; + int chunk_left; + GByteArray *line; long int total_len; @@ -222,9 +238,20 @@ gboolean g_web_add_nameserver(GWeb *web, const char *address) return TRUE; } +static void init_chunk_data(struct web_session *session) +{ + session->line->len = 0; + + session->chunck_state = CHUNK_SIZE; + session->chunk_size = 0; + session->chunk_left = 0; +} + static void init_download_data(struct web_session *session) { + init_chunk_data(session); + session->http_status = 0; session->total_len = 0; session->content_len = -1; @@ -240,6 +267,15 @@ static int append_to_line(struct web_session *session, return 0; } +static int append_to_chunk_size(struct web_session *session, + unsigned char *buf, int len) +{ + if ((session->line->len + len) >= MAX_CHUNK_LEN) + return -EXFULL; + + return append_to_line(session, buf, len); +} + static gboolean send_client_payload(struct web_session *session, unsigned char *buf, int len) { @@ -261,10 +297,113 @@ static gboolean send_client_header_line(struct web_session *session, return TRUE; } +static int decode_chunked(struct web_session *session, + unsigned char *buf, int len) +{ + int counter; + int err; + + while (len > 0) { + switch (session->chunck_state) { + case CHUNK_SIZE: + if (g_ascii_isxdigit(*buf) == TRUE) { + err = append_to_chunk_size(session, buf, 1); + if (err < 0) + return err; + } else { + char *end = NULL; + + g_byte_array_append(session->line, + (unsigned char *)"\0", 1); + counter = strtol((char *)session->line->data, + &end, 16); + if (end == NULL) + return -EILSEQ; + + session->chunk_size = counter; + session->chunk_left = counter; + + session->chunck_state = CHUNK_R; + break; + } + buf++; + len--; + break; + case CHUNK_R: + case CHUNK_R_BODY: + if (*buf == ' ') { + buf++; + len--; + break; + } + + if (*buf != '\r') + return -EILSEQ; + + if (session->chunck_state == CHUNK_R) + session->chunck_state = CHUNK_N; + else + session->chunck_state = CHUNK_N_BODY; + buf++; + len--; + break; + case CHUNK_N: + case CHUNK_N_BODY: + if (*buf != '\n') + return -EILSEQ; + + if (session->chunck_state == CHUNK_N) + session->chunck_state = CHUNK_DATA; + else + session->chunck_state = CHUNK_SIZE; + buf++; + len--; + break; + case CHUNK_DATA: + if (session->chunk_size == 0) { + set_flag(session, WEB_FLAG_DOWNLOAD_DONE); + debug(session->web, "Download Done in chunk"); + return 0; + } + + if (session->chunk_left <= len) { + if (send_client_payload(session, buf, + session->chunk_left) == FALSE) + return -1; + + session->chunck_state = CHUNK_R_BODY; + + len -= session->chunk_left; + buf += session->chunk_left; + + session->total_len += session->chunk_left; + + init_chunk_data(session); + session->chunck_state = CHUNK_R_BODY; + break; + } + /* more data */ + if (send_client_payload(session, buf, len) == FALSE) + return -1; + + session->chunk_left -= len; + + len -= len; + buf += len; + + session->total_len += len; + break; + } + } + + return 0; +} + static int decode_header(struct web_session *session, unsigned char *buf, int len) { unsigned char *ptr, *end_line; + gchar *str; int line_len; int err; @@ -316,6 +455,16 @@ static int decode_header(struct web_session *session, session->http_status); return -1; } + } else if (is_set(session, WEB_FLAG_USE_CHUNK) == FALSE && + g_ascii_strncasecmp("Transfer-Encoding:", + (char *)session->line->data, 18) == 0) { + + str = g_strrstr((char *)(session->line->data + 18), + "chunked"); + if (str != NULL) { + init_chunk_data(session); + set_flag(session, WEB_FLAG_USE_CHUNK); + } } else if (session->content_len == -1 && g_ascii_strncasecmp("Content-Length:", (char *)session->line->data, 15) == 0) { @@ -357,6 +506,9 @@ static int decode_function(struct web_session *session, len -= err; } + if (is_set(session, WEB_FLAG_USE_CHUNK) == TRUE) + return decode_chunked(session, buf, len); + session->total_len += len; if (len > 0) -- 1.7.2.3 _______________________________________________ connman mailing list connman@connman.net http://lists.connman.net/listinfo/connman