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