/pidgin/main: 7a55eaa31da6: HTTP: implement keep-alive connections
Tomasz Wasilczyk
tomkiewicz at cpw.pidgin.im
Sun Jul 21 14:38:55 EDT 2013
Changeset: 7a55eaa31da6ae2bb566f9854584b5df25917e5d
Author: Tomasz Wasilczyk <tomkiewicz at cpw.pidgin.im>
Date: 2013-07-21 20:38 +0200
Branch: default
URL: https://hg.pidgin.im/pidgin/main/rev/7a55eaa31da6
Description:
HTTP: implement keep-alive connections
diffstat:
libpurple/http.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 207 insertions(+), 15 deletions(-)
diffs (truncated from 423 to 300 lines):
diff --git a/libpurple/http.c b/libpurple/http.c
--- a/libpurple/http.c
+++ b/libpurple/http.c
@@ -50,6 +50,11 @@ typedef void (*PurpleHttpSocketConnectCb
struct _PurpleHttpSocket
{
gboolean is_ssl;
+ gboolean is_busy;
+ uint use_count;
+ PurpleHttpKeepalivePool *pool;
+ gchar *hash;
+
PurpleSslConnection *ssl_connection;
PurpleProxyConnectData *raw_connection;
int fd;
@@ -164,6 +169,9 @@ struct _PurpleHttpCookieJar
struct _PurpleHttpKeepalivePool
{
int ref_count;
+
+ /* key: purple_http_socket_hash, value: GSList of PurpleHttpSocket */
+ GHashTable *by_hash;
};
static time_t purple_http_rfc1123_to_time(const gchar *str);
@@ -175,6 +183,8 @@ static PurpleHttpConnection * purple_htt
PurpleHttpRequest *request, PurpleConnection *gc);
static void purple_http_connection_terminate(PurpleHttpConnection *hc);
static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection *hc);
+static void
+purple_http_conn_retry(PurpleHttpConnection *http_conn);
static PurpleHttpResponse * purple_http_response_new(void);
static void purple_http_response_free(PurpleHttpResponse *response);
@@ -184,6 +194,13 @@ static void purple_http_cookie_jar_parse
static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar);
gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar);
+static PurpleHttpSocket *
+purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
+ PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
+ PurpleHttpSocketConnectCb cb, gpointer user_data);
+static void
+purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate);
+
static GRegex *purple_http_re_url, *purple_http_re_url_host,
*purple_http_re_rfc1123;
@@ -206,6 +223,23 @@ static GHashTable *purple_http_hc_by_ptr
/*** Helper functions *********************************************************/
+/* destroys the key and steals the value */
+static void
+g_hash_table_steal_value(GHashTable *hash_table, const gpointer key,
+ GDestroyNotify key_destroy_func)
+{
+ gpointer orig_key;
+
+ g_return_if_fail(hash_table != NULL);
+
+ if (!g_hash_table_lookup_extended(hash_table, key, &orig_key, NULL))
+ return;
+
+ g_hash_table_steal(hash_table, key);
+
+ key_destroy_func(orig_key);
+}
+
static time_t purple_http_rfc1123_to_time(const gchar *str)
{
static const gchar *months[13] = {"jan", "feb", "mar", "apr", "may", "jun",
@@ -299,6 +333,12 @@ static void _purple_http_socket_connecte
hs->connect_cb(hs, purple_ssl_strerror(error), hs->cb_data);
}
+static gchar *
+purple_http_socket_hash(const gchar *host, int port, gboolean is_ssl)
+{
+ return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port);
+}
+
static PurpleHttpSocket *
purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl, PurpleHttpSocketConnectCb cb, gpointer user_data)
{
@@ -312,6 +352,7 @@ purple_http_socket_connect_new(PurpleCon
hs->connect_cb = cb;
hs->cb_data = user_data;
hs->fd = -1;
+ hs->hash = purple_http_socket_hash(host, port, is_ssl);
if (is_ssl) {
if (!purple_ssl_is_supported()) {
@@ -339,6 +380,9 @@ purple_http_socket_connect_new(PurpleCon
return NULL;
}
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "new socket created: %p\n", hs);
+
return hs;
}
@@ -397,11 +441,24 @@ purple_http_socket_watch(PurpleHttpSocke
}
static void
+purple_http_socket_dontwatch(PurpleHttpSocket *hs)
+{
+ g_return_if_fail(hs != NULL);
+
+ if (hs->inpa > 0)
+ purple_input_remove(hs->inpa);
+ hs->inpa = 0;
+}
+
+static void
purple_http_socket_close_free(PurpleHttpSocket *hs)
{
if (hs == NULL)
return;
+ if (purple_debug_is_verbose())
+ purple_debug_misc("http", "destroying socket: %p\n", hs);
+
if (hs->inpa != 0)
purple_input_remove(hs->inpa);
@@ -414,6 +471,8 @@ purple_http_socket_close_free(PurpleHttp
if (hs->fd > 0)
close(hs->fd);
}
+
+ g_free(hs->hash);
g_free(hs);
}
@@ -595,7 +654,8 @@ static gchar * purple_http_headers_dump(
/*** HTTP protocol backend ****************************************************/
-static void _purple_http_disconnect(PurpleHttpConnection *hc);
+static void _purple_http_disconnect(PurpleHttpConnection *hc,
+ gboolean is_graceful);
static void _purple_http_gen_headers(PurpleHttpConnection *hc);
static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd);
@@ -768,16 +828,25 @@ static gboolean _purple_http_recv_header
hdrline[hdrline_len] = '\0';
if (hdrline[0] == '\0') {
- if (!hc->main_header_got) {
+ if (!hc->main_header_got && hc->is_keepalive) {
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Got keep-"
+ "alive terminator from previous"
+ " request\n");
+ }
+ } else if (!hc->main_header_got) {
hc->response->code = 0;
purple_debug_warning("http",
"Main header not present\n");
_purple_http_error(hc, _("Error parsing HTTP"));
return FALSE;
+ } else /* hc->main_header_got */ {
+ hc->headers_got = TRUE;
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Got headers "
+ "end\n");
+ }
}
- hc->headers_got = TRUE;
- if (purple_debug_is_verbose())
- purple_debug_misc("http", "Got headers end\n");
} else if (!hc->main_header_got) {
hc->main_header_got = TRUE;
delim = strchr(hdrline, ' ');
@@ -989,7 +1058,12 @@ static gboolean _purple_http_recv_loopbo
}
if (hc->headers_got)
hc->length_expected = hc->length_got;
- else {
+ else if (hc->length_got == 0 && hc->socket->use_count > 1) {
+ purple_debug_info("http", "Keep-alive connection "
+ "expired, retrying...\n");
+ purple_http_conn_retry(hc);
+ return FALSE;
+ } else {
purple_debug_warning("http", "No more data while "
"parsing headers\n");
_purple_http_error(hc, _("Error parsing HTTP"));
@@ -1090,7 +1164,7 @@ static gboolean _purple_http_recv_loopbo
return FALSE;
}
- _purple_http_disconnect(hc);
+ _purple_http_disconnect(hc, TRUE);
purple_http_connection_terminate(hc);
return FALSE;
}
@@ -1218,18 +1292,20 @@ static void _purple_http_send(gpointer _
_purple_http_recv, hc);
}
-static void _purple_http_disconnect(PurpleHttpConnection *hc)
+static void _purple_http_disconnect(PurpleHttpConnection *hc,
+ gboolean is_graceful)
{
g_return_if_fail(hc != NULL);
if (hc->request_header)
g_string_free(hc->request_header, TRUE);
hc->request_header = NULL;
+
if (hc->response_buffer)
g_string_free(hc->response_buffer, TRUE);
hc->response_buffer = NULL;
- purple_http_socket_close_free(hc->socket);
+ purple_http_keepalive_pool_release(hc->socket, !is_graceful);
hc->socket = NULL;
}
@@ -1253,7 +1329,7 @@ static gboolean _purple_http_reconnect(P
g_return_val_if_fail(hc != NULL, FALSE);
g_return_val_if_fail(hc->url != NULL, FALSE);
- _purple_http_disconnect(hc);
+ _purple_http_disconnect(hc, TRUE);
if (purple_debug_is_verbose()) {
if (purple_debug_is_unsafe()) {
@@ -1284,7 +1360,8 @@ static gboolean _purple_http_reconnect(P
return FALSE;
}
- hc->socket = purple_http_socket_connect_new(hc->gc, url->host,
+ hc->socket = purple_http_keepalive_pool_request(
+ hc->request->keepalive_pool, hc->gc, url->host,
url->port, is_ssl, _purple_http_connected, hc);
if (hc->socket == NULL) {
@@ -1485,11 +1562,29 @@ void purple_http_conn_cancel(PurpleHttpC
if (http_conn == NULL)
return;
+ if (purple_debug_is_verbose()) {
+ purple_debug_misc("http", "Cancelling connection %p...\n",
+ http_conn);
+ }
+
http_conn->response->code = 0;
- _purple_http_disconnect(http_conn);
+ _purple_http_disconnect(http_conn, FALSE);
purple_http_connection_terminate(http_conn);
}
+static void
+purple_http_conn_retry(PurpleHttpConnection *http_conn)
+{
+ if (http_conn == NULL)
+ return;
+
+ purple_debug_info("http", "Retrying connection %p...\n", http_conn);
+
+ http_conn->response->code = 0;
+ _purple_http_disconnect(http_conn, FALSE);
+ _purple_http_reconnect(http_conn);
+}
+
void purple_http_conn_cancel_all(PurpleConnection *gc)
{
GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc);
@@ -1812,12 +1907,21 @@ gboolean purple_http_cookie_jar_is_empty
/*** HTTP Keep-Alive pool API *************************************************/
+static void
+purple_http_keepalive_pool_socketlist_free(gpointer socketlist)
+{
+ g_slist_free_full(socketlist,
+ (GDestroyNotify)purple_http_socket_close_free);
+}
+
PurpleHttpKeepalivePool *
purple_http_keepalive_pool_new(void)
{
PurpleHttpKeepalivePool *pool = g_new0(PurpleHttpKeepalivePool, 1);
pool->ref_count = 1;
+ pool->by_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ purple_http_keepalive_pool_socketlist_free);
return pool;
}
@@ -1827,6 +1931,7 @@ purple_http_keepalive_pool_free(PurpleHt
{
g_return_if_fail(pool != NULL);
+ g_hash_table_destroy(pool->by_hash);
More information about the Commits
mailing list