diff options
Diffstat (limited to 'gweb')
-rw-r--r-- | gweb/gweb.c | 228 |
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); |