summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2010-10-30 23:25:15 +0200
committerMarcel Holtmann <marcel@holtmann.org>2010-10-30 23:25:15 +0200
commit000a60dfed237e3d00584f0c3b475aa777be1ae0 (patch)
tree31e35e666ecd82f07b4431cb14592a5c753d3367
parentf8373635c6dbd7dbe00c92943cbccfdd428c171e (diff)
downloadconnman-000a60dfed237e3d00584f0c3b475aa777be1ae0.tar.gz
connman-000a60dfed237e3d00584f0c3b475aa777be1ae0.tar.bz2
connman-000a60dfed237e3d00584f0c3b475aa777be1ae0.zip
Add support for HTTP header parsing and body content
-rw-r--r--gweb/gweb.c139
-rw-r--r--gweb/gweb.h3
2 files changed, 124 insertions, 18 deletions
diff --git a/gweb/gweb.c b/gweb/gweb.c
index a3519786..1add7106 100644
--- a/gweb/gweb.c
+++ b/gweb/gweb.c
@@ -36,9 +36,13 @@
#include "gresolv.h"
#include "gweb.h"
+#define LINE_CHUNK_SIZE 2048
+
#define SESSION_FLAG_USE_TLS (1 << 0)
struct _GWebResult {
+ const guint8 *buffer;
+ gsize length;
};
struct web_session {
@@ -55,7 +59,13 @@ struct web_session {
guint resolv_action;
char *request;
- GWebResult *result;
+ char *line_buffer;
+ char *line_offset;
+ unsigned int line_space;
+ char *current_line;
+ gboolean header_done;
+
+ GWebResult result;
GWebResultFunc result_func;
gpointer result_data;
@@ -112,6 +122,8 @@ static void free_session(struct web_session *session)
if (session->transport_channel != NULL)
g_io_channel_unref(session->transport_channel);
+ g_free(session->line_buffer);
+
g_free(session->host);
g_free(session->address);
g_free(session);
@@ -284,35 +296,106 @@ gboolean g_web_get_close_connection(GWeb *web)
return web->close_connection;
}
+static inline void call_result_func(struct web_session *session, guint status)
+{
+ if (session->result_func == NULL)
+ return;
+
+ session->result_func(status, &session->result, session->result_data);
+}
+
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
struct web_session *session = user_data;
- gchar buf[4096];
- gsize bytes_read;
+ gsize bytes_read, consumed = 0;
GIOStatus status;
+ size_t count;
+ char *str;
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
session->transport_watch = 0;
- if (session->result_func != NULL)
- session->result_func(400, NULL, session->result_data);
+ call_result_func(session, 400);
return FALSE;
}
- memset(buf, 0, sizeof(buf));
- status = g_io_channel_read_chars(channel, buf, sizeof(buf) - 1,
- &bytes_read, NULL);
+ status = g_io_channel_read_chars(channel, session->line_offset,
+ session->line_space, &bytes_read, NULL);
debug(session->web, "status %u bytes read %zu", status, bytes_read);
if (status != G_IO_STATUS_NORMAL) {
session->transport_watch = 0;
- if (session->result_func != NULL)
- session->result_func(200, NULL, session->result_data);
+ call_result_func(session, 200);
return FALSE;
}
- printf("%s", buf);
+ if (session->header_done == TRUE) {
+ session->result.length = bytes_read;
+ call_result_func(session, 100);
+ return TRUE;
+ }
+
+ str = memchr(session->line_offset, '\n', bytes_read);
+
+ while (str != NULL) {
+ char *start = session->current_line;
+
+ *str = '\0';
+ count = strlen(start);
+ if (count > 0 && start[count - 1] == '\r') {
+ start[--count] = '\0';
+ consumed++;
+ }
+
+ session->current_line = str + 1;
+ consumed += count + 1;
+
+ if (count == 0) {
+ const void *ptr = session->current_line;
+ session->header_done = TRUE;
+ session->result.buffer = ptr;
+ session->result.length = bytes_read - consumed;
+ call_result_func(session, 100);
+ break;
+ }
+
+ //printf("[ %s ]\n", start);
+
+ str = memchr(session->current_line, '\n',
+ bytes_read - consumed);
+ }
+
+ if (session->header_done == TRUE) {
+ gsize size = session->line_offset - session->line_buffer;
+
+ session->line_offset = session->line_buffer;
+ session->line_space += size;
+
+ session->result.buffer = (const guint8 *) session->line_offset;
+ return TRUE;
+ }
+
+ session->line_offset += bytes_read;
+ session->line_space -= bytes_read;
+
+ if (session->line_space < 32) {
+ gsize size = session->line_offset - session->line_buffer;
+ gsize pos = session->current_line - session->line_buffer;
+ char *buf;
+
+ printf("realloc, space %u size %zu\n",
+ session->line_space, size);
+
+ buf = g_try_realloc(session->line_buffer,
+ size + LINE_CHUNK_SIZE);
+ if (buf != NULL) {
+ session->line_buffer = buf;
+ session->line_offset = buf + size;
+ session->line_space = LINE_CHUNK_SIZE;
+ session->current_line = buf + pos;
+ }
+ }
return TRUE;
}
@@ -406,7 +489,7 @@ static void start_request(struct web_session *session)
debug(session->web, "status %u bytes written %zu",
status, bytes_written);
- printf("%s", str);
+ //printf("%s", str);
g_free(str);
}
@@ -468,24 +551,21 @@ static void resolv_result(GResolvResultStatus status,
struct web_session *session = user_data;
if (results == NULL || results[0] == NULL) {
- if (session->result_func != NULL)
- session->result_func(404, NULL, session->result_data);
+ call_result_func(session, 404);
return;
}
debug(session->web, "address %s", results[0]);
if (inet_aton(results[0], NULL) == 0) {
- if (session->result_func != NULL)
- session->result_func(400, NULL, session->result_data);
+ call_result_func(session, 400);
return;
}
session->address = g_strdup(results[0]);
if (create_transport(session) < 0) {
- if (session->result_func != NULL)
- session->result_func(409, NULL, session->result_data);
+ call_result_func(session, 409);
return;
}
@@ -519,6 +599,12 @@ guint g_web_request(GWeb *web, GWebMethod method, const char *url,
session->result_func = func;
session->result_data = user_data;
+ session->line_buffer = g_try_malloc(LINE_CHUNK_SIZE);
+ session->line_offset = session->line_buffer;
+ session->line_space = LINE_CHUNK_SIZE;
+ session->current_line = session->line_buffer;
+ session->header_done = FALSE;
+
if (inet_aton(session->host, NULL) == 0) {
session->resolv_action = g_resolv_lookup_hostname(web->resolv,
session->host, resolv_result, session);
@@ -541,3 +627,20 @@ guint g_web_request(GWeb *web, GWebMethod method, const char *url,
return web->next_query_id++;
}
+
+gboolean g_web_result_get_chunk(GWebResult *result,
+ const guint8 **chunk, gsize *length)
+{
+ if (result == NULL)
+ return FALSE;
+
+ if (chunk == NULL)
+ return FALSE;
+
+ *chunk = result->buffer;
+
+ if (length != NULL)
+ *length = result->length;
+
+ return TRUE;
+}
diff --git a/gweb/gweb.h b/gweb/gweb.h
index 3fc54f89..daeb262d 100644
--- a/gweb/gweb.h
+++ b/gweb/gweb.h
@@ -67,6 +67,9 @@ guint g_web_request(GWeb *web, GWebMethod method, const char *url,
gboolean g_web_cancel(GWeb *web, guint id);
+gboolean g_web_result_get_chunk(GWebResult *result,
+ const guint8 **chunk, gsize *length);
+
#ifdef __cplusplus
}
#endif