# HG changeset patch # User Paul Aurich # Date 1247671931 0 # Node ID 38feacee7c8609921127395ec513201261b2197d # Parent 828d21a1ec43957b949d14d70e9054a4f20e133d# Parent 70dcaa0b6fea55c9474ee0d56eae128c2982d7b2 merge of '04fc08604e52c75e1a6fbe762bd0bf5646fb7707' and '52beafecb8a7a6f963f4ff6271eef29ca699e552' diff -r 828d21a1ec43 -r 38feacee7c86 libpurple/protocols/jabber/bosh.c --- a/libpurple/protocols/jabber/bosh.c Wed Jul 15 08:59:00 2009 +0000 +++ b/libpurple/protocols/jabber/bosh.c Wed Jul 15 15:32:11 2009 +0000 @@ -51,8 +51,13 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ int len = data ? strlen(data) : 0; purple_circ_buffer_append(conn->pending, data, len); } + return; } @@ -316,9 +337,11 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ /* 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 @@ 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(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 @@ 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 @@ * 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(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 @@ 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 @@ 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,