Add proxy and http header notification plus address some
comments from first patch.
---
 gweb/gweb.c |  417 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 388 insertions(+), 29 deletions(-)

diff --git a/gweb/gweb.c b/gweb/gweb.c
index 39f8ecf..7bee1e5 100644
--- a/gweb/gweb.c
+++ b/gweb/gweb.c
@@ -35,6 +35,17 @@
 #include "gresolv.h"
 #include "gweb.h"
 
+#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;
 
@@ -49,6 +60,21 @@ 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;
+       long int content_len;
+       unsigned long int http_status;
+
+       gboolean use_chunk;
+       gboolean header_ready;
+       gboolean done;
+
+       GWebReceivedFunc received_func;
        GWebResultFunc result_func;
        gpointer result_data;
 };
@@ -58,6 +84,10 @@ struct _GWeb {
 
        guint next_query_id;
 
+       char *proxy;
+       uint16_t proxy_port;
+
+       gboolean send_header;
        int index;
        GList *session_list;
 
@@ -101,6 +131,7 @@ static void free_session(struct web_session *session)
        if (session->transport_channel != NULL)
                g_io_channel_unref(session->transport_channel);
 
+       g_byte_array_free(session->line, TRUE);
        g_free(session->host);
        g_free(session->address);
        g_free(session);
@@ -118,33 +149,6 @@ static void flush_sessions(GWeb *web)
        web->session_list = NULL;
 }
 
-GWeb *g_web_new(int index)
-{
-       GWeb *web;
-
-       if (index < 0)
-               return NULL;
-
-       web = g_try_new0(GWeb, 1);
-       if (web == NULL)
-               return NULL;
-
-       web->ref_count = 1;
-
-       web->next_query_id = 1;
-
-       web->index = index;
-       web->session_list = NULL;
-
-       web->resolv = g_resolv_new(index);
-       if (web->resolv == NULL) {
-               g_free(web);
-               return NULL;
-       }
-
-       return web;
-}
-
 GWeb *g_web_ref(GWeb *web)
 {
        if (web == NULL)
@@ -170,6 +174,12 @@ void g_web_unref(GWeb *web)
        g_free(web);
 }
 
+void g_web_allow_deader(GWeb *web, gboolean allow)
+{
+
+       web->send_header = allow;
+}
+
 void g_web_set_debug(GWeb *web, GWebDebugFunc func, gpointer user_data)
 {
        if (web == NULL)
@@ -191,12 +201,294 @@ 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;
+
+       session->use_chunk = FALSE;
+       session->header_ready = FALSE;
+       session->done = FALSE;
+}
+
+static int append_to_line(struct web_session *session,
+                                       unsigned char *buf, int len)
+{
+       g_byte_array_append(session->line, buf, len);
+
+       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)
+{
+       if (session->received_func != NULL)
+               return session->received_func(buf, len, G_WEB_DATA_BODY,
+                                               session->result_data);
+
+       return TRUE;
+}
+
+static gboolean send_client_header_line(struct web_session *session,
+                                               unsigned char *buf, int len)
+{
+       if (session->web->send_header == TRUE &&
+                       session->received_func != NULL)
+               return session->received_func(buf, len, G_WEB_DATA_HEADER,
+                                               session->result_data);
+
+       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) {
+                               session->done = TRUE;
+                               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;
+
+       ptr = buf;
+       while (len > 0) {
+               end_line = memchr(ptr, '\n', len);
+               if (end_line == NULL) {
+                       if (append_to_line(session, ptr, len) < 0)
+                               return -EXFULL;
+                       return 0;
+               }
+
+               line_len = end_line - ptr;
+               line_len += 1;
+
+               if (append_to_line(session, ptr, line_len) < 0)
+                       return -EXFULL;
+
+               if (send_client_header_line(session, session->line->data,
+                                                               len) == FALSE)
+                       return -1;
+
+               /* we have full header line append \0 and process line */
+               g_byte_array_append(session->line, (unsigned char *)"\0", 1);
+
+               if (session->line->data[0] == '\r' ||
+                       session->line->data[0] == '\n') {
+                       /* empty line http header is done */
+                       session->line->len = 0;
+                       session->header_ready = TRUE;
+                       debug(session->web, "content len:%lu http status%lu",
+                                               session->content_len,
+                                               session->http_status);
+                       if (session->http_status != 0)
+                               return (end_line - buf) + 1;
+                       else
+                               return -1;
+               }
+               /* first line should be http status */
+               if (session->http_status == 0) {
+
+                       err = sscanf((char *)session->line->data,
+                                               "HTTP/1.%*d %lu",
+                                               &session->http_status);
+                       if (err != 1) {
+                               debug(session->web, "error status %lu",
+                                               session->http_status);
+                               return -1;
+                       }
+                } else if (session->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);
+                               session->use_chunk = TRUE;
+                       }
+               } else if (session->content_len == -1 &&
+                                       g_ascii_strncasecmp("Content-Length:",
+                                       (char *)session->line->data, 15) == 0) {
+
+                       char *end = NULL;
+
+                       session->content_len = strtol((char *)
+                                       (session->line->data + 15), &end, 10);
+
+                       debug(session->web, "content length: %d\n",
+                                                       session->content_len);
+               }
+
+               debug(session->web, (char *)session->line->data);
+               ptr += line_len;
+               len -= line_len;
+
+               /* done from curent http line header, reset for next line */
+               session->line->len = 0;
+       }
+
+       return 0;
+}
+
+static int decode_function(struct web_session *session,
+                                       unsigned char *buf, int len)
+{
+       int err;
+
+       /* check if we still reading HTTP header */
+       if (session->header_ready == FALSE) {
+               err = decode_header(session, buf, len);
+
+               if (err <= 0)
+                       return err;
+
+               buf += err;
+               len -= err;
+       }
+
+       if (session->use_chunk == TRUE)
+               return decode_chunked(session, buf, len);
+
+       session->total_len += len;
+
+       if (len > 0)
+               if (send_client_payload(session, buf, len) == FALSE)
+                       return -1;
+
+       if (session->content_len != -1 &&
+               session->total_len >= session->content_len) {
+               debug(session->web, "Downloan complete");
+               session->done = TRUE;
+       }
+
+       return 0;
+}
+
 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
                                                        gpointer user_data)
 {
        struct web_session *session = user_data;
        unsigned char buf[4096];
        int sk, len;
+       int err;
 
        if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
                session->transport_watch = 0;
@@ -216,7 +508,22 @@ static gboolean received_data(GIOChannel *channel, 
GIOCondition cond,
                        session->result_func(200, session->result_data);
                return FALSE;
        }
-       printf("%s", buf);
+
+       err = decode_function(session, buf, len);
+
+       if (err < 0) {
+               session->transport_watch = 0;
+               if (session->result_func != NULL)
+                       session->result_func(err, session->result_data);
+               return FALSE;
+       }
+       if (session->done == TRUE) {
+               session->transport_watch = 0;
+               if (session->result_func != NULL)
+                       session->result_func(session->http_status,
+                                                       session->result_data);
+               return FALSE;
+       }
 
        return TRUE;
 }
@@ -265,6 +572,8 @@ static void start_request(struct web_session *session)
        debug(session->web, "request %s from %s",
                                        session->request, session->host);
 
+       init_download_data(session);
+
        sk = g_io_channel_unix_get_fd(session->transport_channel);
 
        buf = g_string_new(NULL);
@@ -332,6 +641,43 @@ static int parse_url(struct web_session *session, const 
char *url)
        return 0;
 }
 
+GWeb *g_web_new(int index, const char *proxy)
+{
+       GWeb *web;
+
+       if (index < 0)
+               return NULL;
+
+       web = g_try_new0(GWeb, 1);
+       if (web == NULL)
+               return NULL;
+
+       web->ref_count = 1;
+
+       web->next_query_id = 1;
+
+       web->index = index;
+       web->session_list = NULL;
+
+       web->resolv = g_resolv_new(index);
+       if (web->resolv == NULL) {
+               g_free(web);
+               return NULL;
+       }
+
+       if (proxy != NULL) {
+               struct web_session session;
+
+               if (parse_url(&session, proxy) == 0) {
+                       web->proxy = session.host;
+                       web->proxy_port = session.port;
+                       g_free(session.request);
+               }
+       }
+
+       return web;
+}
+
 static void resolv_result(GResolvResultStatus status,
                                        char **results, gpointer user_data)
 {
@@ -366,6 +712,7 @@ static void resolv_result(GResolvResultStatus status,
 }
 
 guint g_web_request(GWeb *web, GWebMethod method, const char *url,
+                               GWebReceivedFunc rec_func,
                                GWebResultFunc func, gpointer user_data)
 {
        struct web_session *session;
@@ -390,9 +737,21 @@ guint g_web_request(GWeb *web, GWebMethod method, const 
char *url,
 
        session->result_func = func;
        session->result_data = user_data;
+       session->received_func = rec_func;
 
-       session->resolv_action = g_resolv_lookup_hostname(web->resolv,
+       session->line = g_byte_array_new();
+       if (session->line == NULL) {
+               free_session(session);
+               return 0;
+       }
+       if (web->proxy != NULL) {
+               session->port = web->proxy_port;
+               session->resolv_action = g_resolv_lookup_hostname(web->resolv,
+                                       web->proxy, resolv_result, session);
+       } else
+               session->resolv_action = g_resolv_lookup_hostname(web->resolv,
                                        session->host, resolv_result, session);
+
        if (session->resolv_action == 0) {
                free_session(session);
                return 0;
-- 
1.7.2.3

_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman

Reply via email to