pidgin: 52beafec: Various fixes to get BOSH working with P...

darkrain42 at pidgin.im darkrain42 at pidgin.im
Wed Jul 15 22:25:57 EDT 2009


-----------------------------------------------------------------
Revision: 52beafecb8a7a6f963f4ff6271eef29ca699e552
Ancestor: 382916ff7d78e3641f26a361778ff8808755e03f
Author: darkrain42 at pidgin.im
Date: 2009-07-15T05:49:30
Branch: im.pidgin.pidgin
URL: http://d.pidgin.im/viewmtn/revision/info/52beafecb8a7a6f963f4ff6271eef29ca699e552

Modified files:
        libpurple/protocols/jabber/bosh.c

ChangeLog: 

Various fixes to get BOSH working with Prosody 0.5.

Prosody closes the TCP socket immediately after every reply, so is an
extremely good test of that corner case.

-------------- next part --------------
============================================================
--- libpurple/protocols/jabber/bosh.c	4b25db980d197aeefe52aaf6d8275d6fcc3544bd
+++ libpurple/protocols/jabber/bosh.c	5f1c2a3358e21e11c9dc84968a456a80c1e400e1
@@ -51,8 +51,13 @@ struct _PurpleBOSHConnection {
 	PurpleHTTPConnection *connections[MAX_HTTP_CONNECTIONS];
 	unsigned short failed_connections;
 
-	gboolean ready;
+	enum {
+		BOSH_CONN_OFFLINE,
+		BOSH_CONN_BOOTING,
+		BOSH_CONN_ONLINE
+	} state;
 	gboolean ssl;
+	gboolean needs_restart;
 
 	/* decoded URL */
 	char *host;
@@ -84,7 +89,11 @@ struct _PurpleHTTPConnection {
 
 	PurpleCircBuffer *write_buffer;
 
-	gboolean ready;
+	enum {
+		HTTP_CONN_OFFLINE,
+		HTTP_CONN_CONNECTING,
+		HTTP_CONN_CONNECTED
+	} state;
 	int requests; /* number of outstanding HTTP requests */
 
 	GString *buf;
@@ -129,7 +138,7 @@ jabber_bosh_http_connection_init(PurpleB
 	PurpleHTTPConnection *conn = g_new0(PurpleHTTPConnection, 1);
 	conn->bosh = bosh;
 	conn->fd = -1;
-	conn->ready = FALSE;
+	conn->state = HTTP_CONN_OFFLINE;
 
 	conn->write_buffer = purple_circ_buffer_new(0 /* default grow size */);
 
@@ -176,6 +185,7 @@ jabber_bosh_connection_init(JabberStream
 	conn->path = g_strdup_printf("/%s", path);
 	g_free(path);
 	conn->pipelining = TRUE;
+	conn->needs_restart = FALSE;
 
 	if ((user && user[0] != '\0') || (passwd && passwd[0] != '\0')) {
 		purple_debug_info("jabber", "Ignoring unexpected username and password "
@@ -198,7 +208,7 @@ jabber_bosh_connection_init(JabberStream
 
 	conn->pending = purple_circ_buffer_new(0 /* default grow size */);
 
-	conn->ready = FALSE;
+	conn->state = BOSH_CONN_OFFLINE;
 	if (purple_strcasestr(url, "https://") != NULL)
 		conn->ssl = TRUE;
 	else
@@ -243,18 +253,28 @@ find_available_http_connection(PurpleBOS
 	/* Easy solution: Does everyone involved support pipelining? Hooray! Just use
 	 * one TCP connection! */
 	if (conn->pipelining)
-		return conn->connections[0]->ready ? conn->connections[0] : NULL;
+		return conn->connections[0]->state == HTTP_CONN_CONNECTED ?
+				conn->connections[0] : NULL;
 
 	/* First loop, look for a connection that's ready */
 	for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) {
-		if (conn->connections[i] && conn->connections[i]->ready &&
-		                            conn->connections[i]->requests == 0)
+		if (conn->connections[i] &&
+				conn->connections[i]->state == HTTP_CONN_CONNECTED &&
+				conn->connections[i]->requests == 0)
 			return conn->connections[i];
 	}
 
-	/* Second loop, look for one that's NULL and create a new connection */
+	/* Second loop, is something currently connecting? If so, just queue up. */
 	for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) {
+		if (conn->connections[i] &&
+				conn->connections[i]->state == HTTP_CONN_CONNECTING)
+			return NULL;
+	}
+
+	/* Third loop, look for one that's NULL and create a new connection */
+	for (i = 0; i < MAX_HTTP_CONNECTIONS; ++i) {
 		if (!conn->connections[i]) {
+			purple_debug_info("jabber", "bosh: Creating and connecting new httpconn\n");
 			conn->connections[i] = jabber_bosh_http_connection_init(conn);
 
 			http_connection_connect(conn->connections[i]);
@@ -268,7 +288,7 @@ jabber_bosh_connection_send(PurpleBOSHCo
 
 static void
 jabber_bosh_connection_send(PurpleBOSHConnection *conn,
-                            PurpleBOSHPacketType type, const char *data)
+                            const PurpleBOSHPacketType type, const char *data)
 {
 	PurpleHTTPConnection *chosen;
 	GString *packet = NULL;
@@ -277,12 +297,12 @@ jabber_bosh_connection_send(PurpleBOSHCo
 
 	if (type != PACKET_NORMAL && !chosen) {
 		/*
-		 * For non-ordinary traffic, we don't want to 'buffer' it, so use the
+		 * For non-ordinary traffic, we can't 'buffer' it, so use the
 		 * first connection.
 		 */
 		chosen = conn->connections[0];
 
-		if (!chosen->ready) {
+		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;
@@ -300,6 +320,7 @@ jabber_bosh_connection_send(PurpleBOSHCo
 			int len = data ? strlen(data) : 0;
 			purple_circ_buffer_append(conn->pending, data, len);
 		}
+
 		return;
 	}
 
@@ -316,9 +337,11 @@ jabber_bosh_connection_send(PurpleBOSHCo
 	                conn->sid,
 	                conn->js->user->domain);
 
-	if (type == PACKET_STREAM_RESTART)
+	if (type == PACKET_STREAM_RESTART) {
 		packet = g_string_append(packet, " xmpp:restart='true'/>");
-	else {
+		/* TODO: Do we need to wait for a response? */
+		conn->needs_restart = FALSE;
+	} else {
 		gsize read_amt;
 		if (type == PACKET_TERMINATE)
 			packet = g_string_append(packet, " type='terminate'");
@@ -343,7 +366,9 @@ void jabber_bosh_connection_close(Purple
 	jabber_bosh_connection_send(conn, PACKET_TERMINATE, NULL);
 }
 
-static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn) {
+static void jabber_bosh_connection_stream_restart(PurpleBOSHConnection *conn)
+{
+	conn->needs_restart = TRUE;
 	jabber_bosh_connection_send(conn, PACKET_STREAM_RESTART, NULL);
 }
 
@@ -353,7 +378,7 @@ static gboolean jabber_bosh_connection_e
 	type = xmlnode_get_attrib(node, "type");
 
 	if (type != NULL && !strcmp(type, "terminate")) {
-		conn->ready = FALSE;
+		conn->state = BOSH_CONN_OFFLINE;
 		purple_connection_error_reason(conn->js->gc,
 			PURPLE_CONNECTION_ERROR_OTHER_ERROR,
 			_("The BOSH connection manager terminated your session."));
@@ -384,11 +409,6 @@ static void jabber_bosh_connection_recei
 		/* jabber_process_packet might free child */
 		xmlnode *next = child->next;
 		if (child->type == XMLNODE_TYPE_TAG) {
-			if (!strcmp(child->name, "iq")) {
-				if (xmlnode_get_child(child, "session"))
-					conn->ready = TRUE;
-			}
-
 			jabber_process_packet(js, &child);
 		}
 
@@ -476,9 +496,13 @@ static void boot_response_cb(PurpleBOSHC
 			conn->max_inactivity = 0;
 		} else {
 			/* TODO: Integrate this with jabber.c keepalive checks... */
-			conn->inactivity_timer = purple_timeout_add_seconds(
-					conn->max_inactivity - 2 /* rounding */, bosh_inactivity_cb,
-					conn);
+			if (conn->inactivity_timer == 0) {
+				purple_debug_misc("jabber", "Starting BOSH inactivity timer for %d secs (compensating for rounding)\n",
+						conn->max_inactivity - 5);
+				conn->inactivity_timer = purple_timeout_add_seconds(
+						conn->max_inactivity - 5 /* rounding */,
+						bosh_inactivity_cb, conn);
+			}
 		}
 	}
 
@@ -487,6 +511,7 @@ static void boot_response_cb(PurpleBOSHC
 
 	/* FIXME: Depending on receiving features might break with some hosts */
 	packet = xmlnode_get_child(node, "features");
+	conn->state = BOSH_CONN_ONLINE;
 	conn->js->use_bosh = TRUE;
 	conn->receive_cb = auth_response_cb;
 	jabber_stream_features_parse(conn->js, packet);
@@ -511,6 +536,8 @@ static void jabber_bosh_connection_boot(
 	                conn->js->user->domain,
 	                ++conn->rid);
 
+	purple_debug_misc("jabber", "SendBOSH Boot %s(%" G_GSIZE_FORMAT "): %s\n",
+	                  conn->ssl ? "(ssl)" : "", buf->len, buf->str);
 	conn->receive_cb = boot_response_cb;
 	http_connection_send_request(conn->connections[0], buf);
 	g_string_free(buf, TRUE);
@@ -550,7 +577,7 @@ connection_common_established_cb(PurpleH
 connection_common_established_cb(PurpleHTTPConnection *conn)
 {
 	/* Indicate we're ready and reset some variables */
-	conn->ready = TRUE;
+	conn->state = HTTP_CONN_CONNECTED;
 	conn->requests = 0;
 	if (conn->buf) {
 		g_string_free(conn->buf, TRUE);
@@ -559,9 +586,11 @@ connection_common_established_cb(PurpleH
 	conn->headers_done = FALSE;
 	conn->handled_len = conn->body_len = 0;
 
-	if (conn->bosh->ready) {
+	if (conn->bosh->needs_restart)
+		jabber_bosh_connection_stream_restart(conn->bosh);
+	else if (conn->bosh->state == BOSH_CONN_ONLINE) {
 		purple_debug_info("jabber", "BOSH session already exists. Trying to reuse it.\n");
-		if (conn->bosh->pending->bufused > 0) {
+		if (conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0) {
 			/* Send the pending data */
 			jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL);
 		}
@@ -585,7 +614,7 @@ static void http_connection_disconnected
 	 * Well, then. Fine! I never liked you anyway, server! I was cheating on you
 	 * with AIM!
 	 */
-	conn->ready = FALSE;
+	conn->state = HTTP_CONN_OFFLINE;
 	if (conn->psc) {
 		purple_ssl_close(conn->psc);
 		conn->psc = NULL;
@@ -620,6 +649,10 @@ void jabber_bosh_connection_connect(Purp
 
 void jabber_bosh_connection_connect(PurpleBOSHConnection *bosh) {
 	PurpleHTTPConnection *conn = bosh->connections[0];
+
+	g_return_if_fail(bosh->state == BOSH_CONN_OFFLINE);
+	bosh->state = BOSH_CONN_BOOTING;
+
 	http_connection_connect(conn);
 }
 
@@ -679,10 +712,10 @@ jabber_bosh_http_connection_process(Purp
 	http_received_cb(conn->buf->str + conn->handled_len, conn->body_len,
 	                 conn->bosh);
 
-	if (conn->bosh->ready &&
+	if (conn->bosh->state == BOSH_CONN_ONLINE &&
 			(conn->bosh->requests == 0 || conn->bosh->pending->bufused > 0)) {
+		purple_debug_misc("jabber", "BOSH: Sending an empty request\n");
 		jabber_bosh_connection_send(conn->bosh, PACKET_NORMAL, NULL);
-		purple_debug_misc("jabber", "BOSH: Sending an empty request\n");
 	}
 
 	g_string_free(conn->buf, TRUE);
@@ -802,6 +835,8 @@ static void http_connection_connect(Purp
 	PurpleConnection *gc = bosh->js->gc;
 	PurpleAccount *account = purple_connection_get_account(gc);
 
+	conn->state = HTTP_CONN_CONNECTING;
+
 	if (bosh->ssl) {
 		if (purple_ssl_is_supported()) {
 			conn->psc = purple_ssl_connect(account, bosh->host, bosh->port,


More information about the Commits mailing list