/cpw/tomkiewicz/http: ec4f343af107: Chunked file transfers support
Tomasz Wasilczyk
tomkiewicz at cpw.pidgin.im
Sat Oct 13 10:49:12 EDT 2012
Changeset: ec4f343af1073e194574d3045d2e8bdac0a6d1b6
Author: Tomasz Wasilczyk <tomkiewicz at cpw.pidgin.im>
Date: 2012-10-13 16:49 +0200
Branch: default
URL: http://hg.pidgin.im/cpw/tomkiewicz/http/rev/ec4f343af107
Description:
Chunked file transfers support
diffstat:
libpurple/http.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 128 insertions(+), 20 deletions(-)
diffs (240 lines):
diff --git a/libpurple/http.c b/libpurple/http.c
--- a/libpurple/http.c
+++ b/libpurple/http.c
@@ -68,6 +68,9 @@ struct _PurpleHttpConnection
GString *response_buffer;
int length_expected, length_got;
+
+ gboolean is_chunked, in_chunk, chunks_done;
+ int chunk_length, chunk_got;
};
struct _PurpleHttpResponse
@@ -118,6 +121,8 @@ static const gchar * purple_http_headers
const gchar *key);
static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs,
const gchar *key, int *dst);
+static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
+ const gchar *key, const gchar *value);
static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs);
static PurpleHttpHeaders * purple_http_headers_new(void)
@@ -178,11 +183,14 @@ static const gchar * purple_http_headers
const gchar *key)
{
GList *values;
+ gchar *key_low;
g_return_val_if_fail(hdrs != NULL, NULL);
g_return_val_if_fail(key != NULL, NULL);
- values = g_hash_table_lookup(hdrs->by_name, key);
+ key_low = g_ascii_strdown(key, -1);
+ values = g_hash_table_lookup(hdrs->by_name, key_low);
+ g_free(key_low);
if (!values)
return NULL;
@@ -206,6 +214,18 @@ static gboolean purple_http_headers_get_
return TRUE;
}
+static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs,
+ const gchar *key, const gchar *value)
+{
+ const gchar *str;
+
+ str = purple_http_headers_get(hdrs, key);
+ if (str == NULL || value == NULL)
+ return str == value;
+
+ return (g_ascii_strcasecmp(str, value) == 0);
+}
+
static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs)
{
const GList *hdr;
@@ -364,19 +384,99 @@ static gboolean _purple_http_recv_header
return TRUE;
}
-static void _purple_http_recv_body(PurpleHttpConnection *hc,
+static void _purple_http_recv_body_data(PurpleHttpConnection *hc,
+ const gchar *buf, int len)
+{
+ g_string_append_len(hc->response->contents, buf, len);
+}
+
+static gboolean _purple_http_recv_body_chunked(PurpleHttpConnection *hc,
+ const gchar *buf, int len)
+{
+ gchar *eol, *line;
+ int line_len;
+
+ if (hc->chunks_done)
+ return FALSE;
+ if (!hc->response_buffer)
+ hc->response_buffer = g_string_new("");
+ g_string_append_len(hc->response_buffer, buf, len); //TODO: check max buffer length, not to raise to infinity
+
+ while (hc->response_buffer->len > 0) {
+ if (hc->in_chunk) {
+ int got_now = hc->response_buffer->len;
+ if (hc->chunk_got + got_now > hc->chunk_length)
+ got_now = hc->chunk_length - hc->chunk_got;
+ hc->chunk_got += got_now;
+
+ _purple_http_recv_body_data(hc,
+ hc->response_buffer->str, got_now);
+
+ g_string_erase(hc->response_buffer, 0, got_now);
+ hc->in_chunk = (hc->chunk_got < hc->chunk_length);
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "Chunk (%d/%d)\n",
+ hc->chunk_got, hc->chunk_length);
+
+ continue;
+ }
+
+ line = hc->response_buffer->str;
+ eol = strstr(line, "\r\n");
+ if (eol == NULL) {
+ /* waiting for more data (unlikely, but possible) */
+ if (hc->response_buffer->len > 20) {
+ purple_debug_warning("http", "Chunk length not "
+ "found (buffer too large)\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ return TRUE;
+ }
+ line_len = eol - line;
+
+ if (1 != sscanf(line, "%x", &hc->chunk_length)) {
+ purple_debug_warning("http",
+ "Chunk length not found\n");
+ _purple_http_error(hc, _("Error parsing HTTP"));
+ return FALSE;
+ }
+ hc->chunk_got = 0;
+ hc->in_chunk = TRUE;
+
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "Found chunk of length %d\n", hc->chunk_length);
+
+ g_string_erase(hc->response_buffer, 0, line_len + 2);
+
+ if (hc->chunk_length == 0) {
+ hc->chunks_done = TRUE;
+ hc->in_chunk = FALSE;
+ return TRUE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean _purple_http_recv_body(PurpleHttpConnection *hc,
const gchar *buf, int len)
{
if (hc->response->contents == NULL)
hc->response->contents = g_string_new("");
- /* TODO: chunked data, body length etc */
+ if (hc->is_chunked)
+ return _purple_http_recv_body_chunked(hc, buf, len);
- if (len + hc->length_got > hc->length_expected)
+ if (hc->length_expected >= 0 &&
+ len + hc->length_got > hc->length_expected)
len = hc->length_expected - hc->length_got;
hc->length_got += len;
- g_string_append_len(hc->response->contents, buf, len);
+ _purple_http_recv_body_data(hc, buf, len);
+
+ return TRUE;
}
static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
@@ -386,8 +486,6 @@ static void _purple_http_recv(gpointer _
int len;
gchar buf[4096];
- purple_debug_misc("http", "[tmp] reading...\n");
-
if (hs->is_ssl)
len = purple_ssl_read(hs->ssl_connection, buf, sizeof(buf));
else
@@ -402,30 +500,39 @@ static void _purple_http_recv(gpointer _
return;
}
- if (len == 0 && hc->length_expected < 0 && hc->headers_got)
- hc->length_expected = hc->length_got;
+ if (len == 0 && hc->headers_got)
+ hc->length_expected = hc->length_got; /* TODO: error possible */
if (!hc->headers_got && len > 0) {
if (!_purple_http_recv_headers(hc, buf, len))
return;
- if (hc->headers_got && hc->response_buffer &&
- hc->response_buffer->len > 0) {
- _purple_http_recv_body(hc, hc->response_buffer->str,
- hc->response_buffer->len);
- g_string_truncate(hc->response_buffer, 0);
- }
if (hc->headers_got) {
if (!purple_http_headers_get_int(hc->response->headers,
"Content-Length", &hc->length_expected))
hc->length_expected = -1;
+ hc->is_chunked = (purple_http_headers_match(
+ hc->response->headers,
+ "Transfer-Encoding", "chunked"));
+ }
+ if (hc->headers_got && hc->response_buffer &&
+ hc->response_buffer->len > 0) {
+ int buffer_len = hc->response_buffer->len;
+ gchar *buffer = g_string_free(hc->response_buffer, FALSE);
+ hc->response_buffer = NULL;
+ _purple_http_recv_body(hc, buffer, buffer_len);
}
return;
}
- if (len > 0)
- _purple_http_recv_body(hc, buf, len);
+ if (len > 0) {
+ if (!_purple_http_recv_body(hc, buf, len))
+ return;
+ }
- if (hc->length_got >= hc->length_expected) {
+ if (hc->is_chunked && hc->chunks_done)
+ hc->length_expected = hc->length_got;
+
+ if (hc->length_expected >= 0 && hc->length_got >= hc->length_expected) {
if (!hc->headers_got) {
hc->response->code = 0;
purple_debug_warning("http", "No headers got\n");
@@ -462,8 +569,6 @@ static void _purple_http_send(gpointer _
_purple_http_gen_headers(hc);
- purple_debug_misc("http", "[tmp] sending...\n");
-
write_from = hc->request_header->str + hc->request_header_written;
write_len = hc->request_header->len - hc->request_header_written;
@@ -632,6 +737,9 @@ static gboolean _purple_http_reconnect(P
hc->response->contents = NULL;
hc->length_got = 0;
hc->length_expected = -1;
+ hc->is_chunked = FALSE;
+ hc->in_chunk = FALSE;
+ hc->chunks_done = FALSE;
return TRUE;
}
More information about the Commits
mailing list