changeset 26462:e0218e4ec785

BOSH: Use a write buffer (read: basically cut-n-paste from jabber.c)
author Paul Aurich <paul@darkrain42.org>
date Sat, 04 Apr 2009 23:05:04 +0000
parents 43cfca2eab64
children b01e8e76c59d
files libpurple/protocols/jabber/bosh.c
diffstat 1 files changed, 92 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/bosh.c	Sat Apr 04 21:49:34 2009 +0000
+++ b/libpurple/protocols/jabber/bosh.c	Sat Apr 04 23:05:04 2009 +0000
@@ -79,7 +79,10 @@
 	PurpleBOSHConnection *bosh;
 	PurpleSslConnection *psc;
 	int fd;
-	int ie_handle;
+	guint readh;
+	guint writeh;
+
+	PurpleCircBuffer *write_buffer;
 
 	gboolean ready;
 	int requests; /* number of outstanding HTTP requests */
@@ -92,7 +95,8 @@
 };
 
 static void http_connection_connect(PurpleHTTPConnection *conn);
-static void http_connection_send_request(PurpleHTTPConnection *conn, const GString *req);
+static void http_connection_send_request(PurpleHTTPConnection *conn,
+                                         const GString *req);
 
 void jabber_bosh_init(void)
 {
@@ -127,6 +131,8 @@
 	conn->fd = -1;
 	conn->ready = FALSE;
 
+	conn->write_buffer = purple_circ_buffer_new(0 /* default grow size */);
+
 	return conn;
 }
 
@@ -136,8 +142,12 @@
 	if (conn->buf)
 		g_string_free(conn->buf, TRUE);
 
-	if (conn->ie_handle)
-		purple_input_remove(conn->ie_handle);
+	if (conn->write_buffer)
+		purple_circ_buffer_destroy(conn->write_buffer);
+	if (conn->readh)
+		purple_input_remove(conn->readh);
+	if (conn->writeh)
+		purple_input_remove(conn->writeh);
 	if (conn->psc)
 		purple_ssl_close(conn->psc);
 	if (conn->fd >= 0)
@@ -579,9 +589,14 @@
 		conn->fd = -1;
 	}
 
-	if (conn->ie_handle) {
-		purple_input_remove(conn->ie_handle);
-		conn->ie_handle = 0;
+	if (conn->readh) {
+		purple_input_remove(conn->readh);
+		conn->readh = 0;
+	}
+
+	if (conn->writeh) {
+		purple_input_remove(conn->writeh);
+		conn->writeh = 0;
 	}
 
 	if (conn->bosh->pipelining)
@@ -696,7 +711,7 @@
 
 		/*
 		 * If the socket is closed, the processing really needs to know about
-		 * it. Handle that now (it will be handled again post-processing).
+		 * it. Handle that now.
 		 */
 		http_connection_disconnected(conn);
 
@@ -762,7 +777,7 @@
 	}
 
 	conn->fd = source;
-	conn->ie_handle = purple_input_add(conn->fd, PURPLE_INPUT_READ,
+	conn->readh = purple_input_add(conn->fd, PURPLE_INPUT_READ,
 	        http_connection_read_cb, conn);
 	connection_common_established_cb(conn);
 }
@@ -797,13 +812,59 @@
 	}
 }
 
+static int
+http_connection_do_send(PurpleHTTPConnection *conn, const char *data, int len)
+{
+	int ret;
+
+	if (conn->psc)
+		ret = purple_ssl_write(conn->psc, data, len);
+	else
+		ret = write(conn->fd, data, len);
+
+	return ret;
+}
+
+static void
+http_connection_send_cb(gpointer data, gint source, PurpleInputCondition cond)
+{
+	PurpleHTTPConnection *conn = data;
+	int ret;
+	int writelen = purple_circ_buffer_get_max_read(conn->write_buffer);
+
+	if (writelen == 0) {
+		purple_input_remove(conn->writeh);
+		conn->writeh = 0;
+		return;
+	}
+
+	ret = http_connection_do_send(conn, conn->write_buffer->outptr, writelen);
+
+	if (ret < 0 && errno == EAGAIN)
+		return;
+	else if (ret <= 0) {
+		/*
+		 * TODO: Handle this better. Probably requires a PurpleBOSHConnection
+		 * buffer that stores what is "being sent" until the
+		 * PurpleHTTPConnection reports it is fully sent.
+		 */
+		purple_connection_error_reason(conn->bosh->js->gc,
+				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
+				_("Write error"));
+		return;
+	}
+
+	purple_circ_buffer_mark_read(conn->write_buffer, ret);
+}
+
 static void
 http_connection_send_request(PurpleHTTPConnection *conn, const GString *req)
 {
-	char *packet;
+	char *data;
 	int ret;
+	size_t len;
 
-	packet = g_strdup_printf("POST %s HTTP/1.1\r\n"
+	data = g_strdup_printf("POST %s HTTP/1.1\r\n"
 	                       "Host: %s\r\n"
 	                       "User-Agent: %s\r\n"
 	                       "Content-Encoding: text/xml; charset=utf-8\r\n"
@@ -812,25 +873,35 @@
 	                       conn->bosh->path, conn->bosh->host, bosh_useragent,
 	                       req->len, req->str);
 
-	/* TODO: Better error handling, circbuffer or possible integration with
-	 * low-level code in jabber.c */
-	if (conn->psc)
-		ret = purple_ssl_write(conn->psc, packet, strlen(packet));
-	else
-		ret = write(conn->fd, packet, strlen(packet));
+	len = strlen(data);
 
 	++conn->requests;
 	++conn->bosh->requests;
-	g_free(packet);
 
-	if (ret < 0 && errno == EAGAIN)
-		purple_debug_error("jabber", "BOSH write would have blocked\n");
+	if (conn->writeh == 0)
+		ret = http_connection_do_send(conn, data, len);
+	else {
+		ret = -1;
+		errno = EAGAIN;
+	}
 
-	if (ret <= 0) {
+	if (ret < 0 && errno != EAGAIN) {
+		/*
+		 * TODO: Handle this better. Probably requires a PurpleBOSHConnection
+		 * buffer that stores what is "being sent" until the
+		 * PurpleHTTPConnection reports it is fully sent.
+		 */
 		purple_connection_error_reason(conn->bosh->js->gc,
 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
 				_("Write error"));
 		return;
+	} else if (ret < len) {
+		if (ret < 0)
+			ret = 0;
+		if (conn->writeh == 0)
+			conn->writeh = purple_input_add(conn->psc ? conn->psc->fd : conn->fd,
+					PURPLE_INPUT_WRITE, http_connection_send_cb, conn);
+		purple_circ_buffer_append(conn->write_buffer, data + ret, len - ret);
 	}
 }