# HG changeset patch # User Paul Aurich # Date 1249182000 0 # Node ID 3eef8392a54b661d771247585930a38188f6099e # Parent eb2d17945ce3b8a7bbfa5c082a4f3e7e26d11c40 jabber: Add a BOSH send timer (queue up stanzas), fixes connecting to Prosody This send timer shouldn't strictly be necessary, but Prosody currently closes the TCP stream after every response (MattJ tells me there's a pipelining bug that they couldn't fix, so they just disabled it), but in my tests, the initial purple_ssl_read doesn't catch the EOF/disconnection (the read returns EAGAIN), so the prpl, while processing the response from the server, thinks that BOSH connection is still open, and tries to send to send to it. *Then*, the write input watcher triggers and read returns 0 (EOF). The request that was sent is then lost. Anyway, I've termed Prosody a fuzzer for BOSH. :-) diff -r eb2d17945ce3 -r 3eef8392a54b libpurple/protocols/jabber/bosh.c --- a/libpurple/protocols/jabber/bosh.c Sat Aug 01 22:12:30 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.c Sun Aug 02 03:00:00 2009 +0000 @@ -31,6 +31,7 @@ #define MAX_HTTP_CONNECTIONS 2 #define MAX_FAILED_CONNECTIONS 3 +#define BUFFER_SEND_IN_SECS 1 typedef struct _PurpleHTTPConnection PurpleHTTPConnection; @@ -40,9 +41,9 @@ static char *bosh_useragent = NULL; typedef enum { + PACKET_NORMAL, PACKET_TERMINATE, - PACKET_STREAM_RESTART, - PACKET_NORMAL, + PACKET_FLUSH, } PurpleBOSHPacketType; struct _PurpleBOSHConnection { @@ -80,6 +81,7 @@ int requests; guint inactivity_timer; + guint send_timer; }; struct _PurpleHTTPConnection { @@ -110,6 +112,7 @@ static void http_connection_connect(PurpleHTTPConnection *conn); static void http_connection_send_request(PurpleHTTPConnection *conn, const GString *req); +static gboolean send_timer_cb(gpointer data); void jabber_bosh_init(void) { @@ -231,6 +234,8 @@ g_free(conn->host); g_free(conn->path); + if (conn->send_timer) + purple_timeout_remove(conn->send_timer); if (conn->inactivity_timer) purple_timeout_remove(conn->inactivity_timer); @@ -312,36 +317,11 @@ PurpleHTTPConnection *chosen; GString *packet = NULL; - chosen = find_available_http_connection(conn); - - if ((type != PACKET_NORMAL) && !chosen) { + if (type != PACKET_FLUSH && type != PACKET_TERMINATE) { /* - * For non-ordinary traffic, we can't 'buffer' it, so use the - * first connection. - */ - chosen = conn->connections[0]; - - if (chosen->state != HTTP_CONN_CONNECTED) { - purple_debug_info("jabber", "Unable to find a ready BOSH " - "connection. Ignoring send of type 0x%02x.\n", type); - return; - } - } - - if (type == PACKET_NORMAL) { - if (conn->max_requests > 0 && conn->requests == conn->max_requests) { - purple_debug_warning("jabber", "BOSH connection %p has %d requests out\n", conn, conn->requests); - } else if (!chosen) { - purple_debug_warning("jabber", "No BOSH connection found!\n"); - } - } - - if (type == PACKET_NORMAL && (!chosen || - (conn->max_requests > 0 && conn->requests == conn->max_requests))) { - /* - * For normal data, send up to max_requests requests at a time or there is no - * connection ready (likely, we're currently opening a second connection and - * will send these packets when connected). + * Unless this is a flush (or session terminate, which needs to be + * sent immediately), queue up the data and start a timer to flush + * the buffer. */ if (data) { int len = data ? strlen(data) : 0; @@ -350,9 +330,33 @@ if (purple_debug_is_verbose()) purple_debug_misc("jabber", "bosh: %p has %" G_GSIZE_FORMAT " bytes in " - "the buffer.\n", conn, conn->pending->buflen); + "the buffer.\n", conn, conn->pending->bufused); + if (conn->send_timer == 0) + conn->send_timer = purple_timeout_add_seconds(BUFFER_SEND_IN_SECS, + send_timer_cb, conn); + return; + } + + chosen = find_available_http_connection(conn); - return; + if (!chosen) { + /* + * For non-ordinary traffic, we can't 'buffer' it, so use the + * first connection. + */ + chosen = conn->connections[0]; + + if (chosen->state != HTTP_CONN_CONNECTED) { + purple_debug_warning("jabber", "Unable to find a ready BOSH " + "connection. Ignoring send of type 0x%02x.\n", type); + return; + } + } + + /* We're flushing the send buffer, so remove the send timer */ + if (conn->send_timer != 0) { + purple_timeout_remove(conn->send_timer); + conn->send_timer = 0; } packet = g_string_new(NULL); @@ -368,7 +372,7 @@ conn->sid, conn->js->user->domain); - if (type == PACKET_STREAM_RESTART) { + if (conn->needs_restart) { packet = g_string_append(packet, " xmpp:restart='true'/>"); /* TODO: Do we need to wait for a response? */ conn->needs_restart = FALSE; @@ -400,7 +404,7 @@ static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) { conn->needs_restart = TRUE; - jabber_bosh_connection_send(conn, PACKET_STREAM_RESTART, NULL); + jabber_bosh_connection_send(conn, PACKET_NORMAL, NULL); } static gboolean jabber_bosh_connection_error_check(PurpleBOSHConnection *conn, xmlnode *node) { @@ -419,12 +423,31 @@ } static gboolean +send_timer_cb(gpointer data) +{ + PurpleBOSHConnection *bosh; + + bosh = data; + bosh->send_timer = 0; + + jabber_bosh_connection_send(bosh, PACKET_FLUSH, NULL); + + return FALSE; +} + +static gboolean bosh_inactivity_cb(gpointer data) { PurpleBOSHConnection *bosh = data; + bosh->inactivity_timer = 0; - jabber_bosh_connection_send(bosh, PACKET_NORMAL, NULL); - return TRUE; + if (bosh->send_timer != 0) + purple_timeout_remove(bosh->send_timer); + + /* clears bosh->send_timer */ + send_timer_cb(bosh); + + return FALSE; } static void @@ -641,22 +664,12 @@ purple_debug_info("jabber", "BOSH session already exists. Trying to reuse it.\n"); if (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0) { /* Send the pending data */ - jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL); + jabber_bosh_connection_send(conn->bosh, PACKET_FLUSH, NULL); } -#if 0 - conn->bosh->receive_cb = jabber_bosh_connection_received; - if (conn->bosh->connect_cb) - conn->bosh->connect_cb(conn->bosh); -#endif } else jabber_bosh_connection_boot(conn->bosh); } -void jabber_bosh_connection_refresh(PurpleBOSHConnection *conn) -{ - jabber_bosh_connection_send(conn, PACKET_NORMAL, NULL); -} - static void http_connection_disconnected(PurpleHTTPConnection *conn) { /* diff -r eb2d17945ce3 -r 3eef8392a54b libpurple/protocols/jabber/bosh.h --- a/libpurple/protocols/jabber/bosh.h Sat Aug 01 22:12:30 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.h Sun Aug 02 03:00:00 2009 +0000 @@ -37,5 +37,4 @@ void jabber_bosh_connection_connect(PurpleBOSHConnection *conn); void jabber_bosh_connection_close(PurpleBOSHConnection *conn); void jabber_bosh_connection_send_raw(PurpleBOSHConnection *conn, const char *data); -void jabber_bosh_connection_refresh(PurpleBOSHConnection *conn); #endif /* PURPLE_JABBER_BOSH_H_ */