changeset 27795:3eef8392a54b

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. :-)
author Paul Aurich <paul@darkrain42.org>
date Sun, 02 Aug 2009 03:00:00 +0000
parents eb2d17945ce3
children aca8afc93264
files libpurple/protocols/jabber/bosh.c libpurple/protocols/jabber/bosh.h
diffstat 2 files changed, 61 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- 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)
 {
 	/*
--- 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_ */