summaryrefslogtreecommitdiff
path: root/gweb/gweb.c
diff options
context:
space:
mode:
authorMohamed Abbas <mabbas@linux.intel.com>2010-11-05 18:20:45 -0700
committerMarcel Holtmann <marcel@holtmann.org>2010-11-06 13:33:45 +0100
commit76df37ca47b372b1bf1164c9c4d82d687e47099e (patch)
treeba4a21c0d06a267b1cd0e947adad2564a57bb301 /gweb/gweb.c
parent8f124fea0748a0b05e39a461ee04e2ecdb979c6c (diff)
downloadconnman-76df37ca47b372b1bf1164c9c4d82d687e47099e.tar.gz
connman-76df37ca47b372b1bf1164c9c4d82d687e47099e.tar.bz2
connman-76df37ca47b372b1bf1164c9c4d82d687e47099e.zip
Add chunk encode support when doing HTTP request.
Allow user to send chunked HTTP post request by calling the input callback function on write until the user indicate no more data then we send the 0 length chunk to indicate end of request body. Now we use a new watch for sending data. This watch is only active if more data need to be send.
Diffstat (limited to 'gweb/gweb.c')
-rw-r--r--gweb/gweb.c228
1 files changed, 156 insertions, 72 deletions
diff --git a/gweb/gweb.c b/gweb/gweb.c
index 295b93f2..fe579ec8 100644
--- a/gweb/gweb.c
+++ b/gweb/gweb.c
@@ -58,6 +58,7 @@ struct web_session {
GIOChannel *transport_channel;
guint transport_watch;
+ guint send_watch;
guint resolv_action;
char *request;
@@ -66,6 +67,8 @@ struct web_session {
gsize receive_space;
GString *current_header;
gboolean header_done;
+ gboolean more_data;
+ gboolean request_started;
GWebResult result;
@@ -122,6 +125,9 @@ static void free_session(struct web_session *session)
if (session->transport_watch > 0)
g_source_remove(session->transport_watch);
+ if (session->send_watch > 0)
+ g_source_remove(session->send_watch);
+
if (session->transport_channel != NULL)
g_io_channel_unref(session->transport_channel);
@@ -313,6 +319,143 @@ static inline void call_result_func(struct web_session *session, guint16 status)
session->result_func(&session->result, session->user_data);
}
+static void process_next_chunk(struct web_session *session)
+{
+ GString *buf;
+ gchar *str;
+ const guint8 *body;
+ gsize length;
+ gsize count, bytes_written;
+ GIOStatus status;
+
+ if (session->input_func == NULL) {
+ session->more_data = FALSE;
+ return;
+ }
+
+ session->more_data = session->input_func(&body, &length,
+ session->user_data);
+
+ buf = g_string_new(NULL);
+
+ if (length > 0) {
+ g_string_append_printf(buf, "%zx\r\n", length);
+ g_string_append_len(buf, (char *) body, length);
+ g_string_append(buf, "\r\n");
+ }
+
+ if (session->more_data == FALSE)
+ g_string_append(buf, "0\r\n\r\n");
+
+ count = buf->len;
+ str = g_string_free(buf, FALSE);
+
+ if (count > 0) {
+ status = g_io_channel_write_chars(session->transport_channel,
+ str, count, &bytes_written, NULL);
+
+ debug(session->web, "status %u bytes written %zu",
+ status, bytes_written);
+ }
+
+ g_free(str);
+}
+
+static void start_request(struct web_session *session)
+{
+ GString *buf;
+ gchar *str;
+ const guint8 *body;
+ gsize length;
+ gsize count, bytes_written;
+ GIOStatus status;
+
+ debug(session->web, "request %s from %s",
+ session->request, session->host);
+
+ buf = g_string_new(NULL);
+
+ if (session->content_type == NULL)
+ g_string_append_printf(buf, "GET %s HTTP/1.1\r\n",
+ session->request);
+ else
+ g_string_append_printf(buf, "POST %s HTTP/1.1\r\n",
+ session->request);
+ g_string_append_printf(buf, "Host: %s\r\n", session->host);
+ if (session->web->user_agent != NULL)
+ g_string_append_printf(buf, "User-Agent: %s\r\n",
+ session->web->user_agent);
+ if (session->web->accept_option != NULL)
+ g_string_append_printf(buf, "Accept: %s\r\n",
+ session->web->accept_option);
+ if (session->content_type != NULL) {
+ g_string_append_printf(buf, "Content-Type: %s\r\n",
+ session->content_type);
+ if (session->input_func == NULL) {
+ session->more_data = FALSE;
+ length = 0;
+ } else
+ session->more_data = session->input_func(&body, &length,
+ session->user_data);
+ if (session->more_data == FALSE)
+ g_string_append_printf(buf, "Content-Length: %zu\r\n",
+ length);
+ else
+ g_string_append(buf, "Transfer-Encoding: chunked\r\n");
+ }
+ if (session->web->close_connection == TRUE)
+ g_string_append(buf, "Connection: close\r\n");
+ g_string_append(buf, "\r\n");
+
+ if (session->content_type != NULL && length > 0) {
+ if (session->more_data == TRUE) {
+ g_string_append_printf(buf, "%zx\r\n", length);
+ g_string_append_len(buf, (char *) body, length);
+ g_string_append(buf, "\r\n");
+ } else
+ g_string_append_len(buf, (char *) body, length);
+ }
+
+ count = buf->len;
+ str = g_string_free(buf, FALSE);
+
+ debug(session->web, "bytes to write %zu", count);
+
+ status = g_io_channel_write_chars(session->transport_channel,
+ str, count, &bytes_written, NULL);
+
+ debug(session->web, "status %u bytes written %zu",
+ status, bytes_written);
+
+ //printf("%s", str);
+
+ g_free(str);
+}
+
+static gboolean send_data(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ struct web_session *session = user_data;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ session->send_watch = 0;
+ return FALSE;
+ }
+
+ if (session->request_started == FALSE) {
+ session->request_started = TRUE;
+ start_request(session);
+ } else if (session->more_data == TRUE)
+ process_next_chunk(session);
+
+ if (session->more_data == TRUE)
+ return TRUE;
+
+ session->send_watch = 0;
+
+ return FALSE;
+}
+
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
@@ -416,11 +559,6 @@ static int connect_session_transport(struct web_session *session)
sin.sin_port = htons(session->port);
sin.sin_addr.s_addr = inet_addr(session->address);
- if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
- close(sk);
- return -EIO;
- }
-
if (session->flags & SESSION_FLAG_USE_TLS)
session->transport_channel = g_io_channel_gnutls_new(sk);
else
@@ -431,15 +569,28 @@ static int connect_session_transport(struct web_session *session)
return -ENOMEM;
}
+ g_io_channel_set_flags(session->transport_channel,
+ G_IO_FLAG_NONBLOCK, NULL);
g_io_channel_set_encoding(session->transport_channel, NULL, NULL);
g_io_channel_set_buffered(session->transport_channel, FALSE);
g_io_channel_set_close_on_unref(session->transport_channel, TRUE);
+ if (connect(sk, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ if (errno != EINPROGRESS) {
+ close(sk);
+ return -EIO;
+ }
+ }
+
session->transport_watch = g_io_add_watch(session->transport_channel,
G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
received_data, session);
+ session->send_watch = g_io_add_watch(session->transport_channel,
+ G_IO_OUT | G_IO_HUP | G_IO_NVAL | G_IO_ERR,
+ send_data, session);
+
return 0;
}
@@ -457,69 +608,6 @@ static int create_transport(struct web_session *session)
return 0;
}
-static void start_request(struct web_session *session)
-{
- GString *buf;
- gchar *str;
- const guint8 *body;
- gsize length;
- gsize count, bytes_written;
- GIOStatus status;
-
- debug(session->web, "request %s from %s",
- session->request, session->host);
-
- buf = g_string_new(NULL);
-
- if (session->content_type == NULL)
- g_string_append_printf(buf, "GET %s HTTP/1.1\r\n",
- session->request);
- else
- g_string_append_printf(buf, "POST %s HTTP/1.1\r\n",
- session->request);
- g_string_append_printf(buf, "Host: %s\r\n", session->host);
- if (session->web->user_agent != NULL)
- g_string_append_printf(buf, "User-Agent: %s\r\n",
- session->web->user_agent);
- if (session->web->accept_option != NULL)
- g_string_append_printf(buf, "Accept: %s\r\n",
- session->web->accept_option);
- if (session->content_type != NULL) {
- g_string_append_printf(buf, "Content-Type: %s\r\n",
- session->content_type);
- if (session->input_func != NULL)
- session->input_func(&body, &length, session->user_data);
- else
- length = 0;
-
- g_string_append_printf(buf, "Content-Length: %zu\r\n", length);
- }
- if (session->web->close_connection == TRUE)
- g_string_append(buf, "Connection: close\r\n");
- g_string_append(buf, "\r\n");
-
- count = buf->len;
-
- if (session->content_type != NULL && length > 0) {
- g_string_append_len(buf, (char *) body, length);
- count += length;
- }
-
- str = g_string_free(buf, FALSE);
-
- debug(session->web, "bytes to write %zu", count);
-
- status = g_io_channel_write_chars(session->transport_channel,
- str, count, &bytes_written, NULL);
-
- debug(session->web, "status %u bytes written %zu",
- status, bytes_written);
-
- //printf("%s", str);
-
- g_free(str);
-}
-
static int parse_url(struct web_session *session, const char *url)
{
char *scheme, *host, *port, *path;
@@ -594,8 +682,6 @@ static void resolv_result(GResolvResultStatus status,
call_result_func(session, 409);
return;
}
-
- start_request(session);
}
static guint do_request(GWeb *web, const char *url,
@@ -657,8 +743,6 @@ static guint do_request(GWeb *web, const char *url,
free_session(session);
return 0;
}
-
- start_request(session);
}
web->session_list = g_list_append(web->session_list, session);