From: Mohamed Abbas <[email protected]>
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
[email protected]
http://lists.connman.net/listinfo/connman