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