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

Reply via email to