diff src/protocols/msn/httpconn.c @ 13200:33bef17125c2

[gaim-migrate @ 15563] This is the soon-to-be-infamous nonblocking network activity patch that I've been working on. Feel free to yell at me if this makes you unhappy. committer: Tailor Script <tailor@pidgin.im>
author Daniel Atallah <daniel.atallah@gmail.com>
date Thu, 09 Feb 2006 04:17:56 +0000
parents fc464a0abccc
children 8754a0fe2297
line wrap: on
line diff
--- a/src/protocols/msn/httpconn.c	Thu Feb 09 04:14:54 2006 +0000
+++ b/src/protocols/msn/httpconn.c	Thu Feb 09 04:17:56 2006 +0000
@@ -25,16 +25,7 @@
 #include "debug.h"
 #include "httpconn.h"
 
-typedef struct
-{
-	MsnHttpConn *httpconn;
-	char *data;
-	size_t size;
-
-} MsnHttpQueueData;
-
 static void read_cb(gpointer data, gint source, GaimInputCondition cond);
-void msn_httpconn_process_queue(MsnHttpConn *httpconn);
 gboolean msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf,
 								 size_t size, char **ret_buf, size_t *ret_size,
 								 gboolean *error);
@@ -55,6 +46,9 @@
 
 	httpconn->servconn = servconn;
 
+	httpconn->tx_buf = gaim_circ_buffer_new(MSN_BUF_LEN);
+	httpconn->tx_handler = -1;
+
 	return httpconn;
 }
 
@@ -68,14 +62,15 @@
 	if (httpconn->connected)
 		msn_httpconn_disconnect(httpconn);
 
-	if (httpconn->full_session_id != NULL)
-		g_free(httpconn->full_session_id);
+	g_free(httpconn->full_session_id);
+
+	g_free(httpconn->session_id);
 
-	if (httpconn->session_id != NULL)
-		g_free(httpconn->session_id);
+	g_free(httpconn->host);
 
-	if (httpconn->host != NULL)
-		g_free(httpconn->host);
+	gaim_circ_buffer_destroy(httpconn->tx_buf);
+	if (httpconn->tx_handler > 0)
+		gaim_input_remove(httpconn->tx_handler);
 
 	g_free(httpconn);
 }
@@ -114,49 +109,70 @@
 	return auth;
 }
 
-static ssize_t
-write_raw(MsnHttpConn *httpconn, const char *header,
-		  const char *body, size_t body_len)
+static void
+httpconn_write_cb(gpointer data, gint source, GaimInputCondition cond)
 {
-	char *buf;
-	size_t buf_len;
+	MsnHttpConn *httpconn = data;
+	int ret, writelen;
+
+	if (httpconn->waiting_response)
+		return;
+
+	writelen = gaim_circ_buffer_get_max_read(httpconn->tx_buf);
+
+	if (writelen == 0) {
+		gaim_input_remove(httpconn->tx_handler);
+		httpconn->tx_handler = -1;
+		return;
+	}
 
-	ssize_t s;
+	ret = write(httpconn->fd, httpconn->tx_buf->outptr, writelen);
+
+	if (ret < 0 && errno == EAGAIN)
+		return;
+	else if (ret <= 0) {
+		msn_servconn_got_error(httpconn->servconn,
+			MSN_SERVCONN_ERROR_WRITE);
+		return;
+	}
+
+	gaim_circ_buffer_mark_read(httpconn->tx_buf, ret);
+}
+
+static ssize_t
+write_raw(MsnHttpConn *httpconn, const char *data, size_t data_len)
+{
 	ssize_t res; /* result of the write operation */
 
 #ifdef MSN_DEBUG_HTTP
 	gaim_debug_misc("msn", "Writing HTTP (header): {%s}\n", header);
 #endif
 
-	buf = g_strdup_printf("%s\r\n", header);
-	buf_len = strlen(buf);
 
-	if (body != NULL)
+	if (httpconn->tx_handler == -1 && !httpconn->waiting_response)
+		res = write(httpconn->fd, data, data_len);
+	else
 	{
-		buf = g_realloc(buf, buf_len + body_len);
-		memcpy(buf + buf_len, body, body_len);
-		buf_len += body_len;
+		res = -1;
+		errno = EAGAIN;
 	}
 
-	s = 0;
-
-	do
+	if (res <= 0 && errno != EAGAIN)
 	{
-		res = write(httpconn->fd, buf + s, buf_len - s);
-		if (res >= 0)
-		{
-			s += res;
-		}
-		else if (errno != EAGAIN)
-		{
-			msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE);
-			return -1;
-		}
-	} while (s < buf_len);
+		msn_servconn_got_error(httpconn->servconn,
+			MSN_SERVCONN_ERROR_WRITE);
+		return -1;
+	} else if (res < data_len) {
+		if (res < 0)
+			res = 0;
+		if (httpconn->tx_handler == -1)
+			httpconn->tx_handler = gaim_input_add(httpconn->fd,
+				GAIM_INPUT_WRITE, httpconn_write_cb, httpconn);
+		gaim_circ_buffer_append(httpconn->tx_buf, data + res,
+			data_len - res);
+	}
 
-	g_free(buf);
-
-	return s;
+	return res;
 }
 
 static void
@@ -169,11 +185,14 @@
 	g_return_if_fail(httpconn != NULL);
 
 	if (httpconn->waiting_response ||
-		httpconn->queue != NULL)
+		httpconn->tx_handler > 0)
 	{
 		return;
 	}
 
+	/* It is OK if this is buffered because it will only be buffered if
+	   nothing else is in the buffer */
+
 	auth = msn_httpconn_proxy_auth(httpconn);
 
 	header = g_strdup_printf(
@@ -187,20 +206,19 @@
 		"Connection: Keep-Alive\r\n"
 		"Pragma: no-cache\r\n"
 		"Content-Type: application/x-msn-messenger\r\n"
-		"Content-Length: 0\r\n",
+		"Content-Length: 0\r\n\r\n",
 		httpconn->host,
 		httpconn->full_session_id,
 		httpconn->host,
 		auth ? auth : "");
 
-	if (auth != NULL)
-		g_free(auth);
+	g_free(auth);
 
-	r = write_raw(httpconn, header, NULL, -1);
+	r = write_raw(httpconn, header, strlen(header));
 
 	g_free(header);
 
-	if (r > 0)
+	if (r >= 0)
 	{
 		httpconn->waiting_response = TRUE;
 		httpconn->dirty = FALSE;
@@ -242,12 +260,13 @@
 	if (source > 0)
 	{
 		httpconn->inpa = gaim_input_add(httpconn->fd, GAIM_INPUT_READ,
-										read_cb, data);
+			read_cb, data);
 
 		httpconn->timer = gaim_timeout_add(2000, do_poll, httpconn);
 
 		httpconn->waiting_response = FALSE;
-		msn_httpconn_process_queue(httpconn);
+		if (httpconn->tx_handler > 0)
+			httpconn_write_cb(httpconn, source, GAIM_INPUT_WRITE);
 	}
 	else
 	{
@@ -269,8 +288,7 @@
 		msn_httpconn_disconnect(httpconn);
 
 	r = gaim_proxy_connect(httpconn->session->account,
-						   "gateway.messenger.hotmail.com", 80, connect_cb,
-						   httpconn);
+		"gateway.messenger.hotmail.com", 80, connect_cb, httpconn);
 
 	if (r == 0)
 	{
@@ -329,7 +347,9 @@
 
 	len = read(httpconn->fd, buf, sizeof(buf) - 1);
 
-	if (len <= 0)
+	if (len < 0 && errno == EAGAIN)
+		return;
+	else if (len <= 0)
 	{
 		gaim_debug_error("msn", "HTTP: Read error\n");
 		msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ);
@@ -444,37 +464,12 @@
 	g_free(old_rx_buf);
 }
 
-void
-msn_httpconn_process_queue(MsnHttpConn *httpconn)
-{
-	if (httpconn->queue != NULL)
-	{
-		MsnHttpQueueData *queue_data;
-
-		queue_data = (MsnHttpQueueData *)httpconn->queue->data;
-
-		httpconn->queue = g_list_remove(httpconn->queue, queue_data);
-
-		msn_httpconn_write(queue_data->httpconn,
-						   queue_data->data,
-						   queue_data->size);
-
-		g_free(queue_data->data);
-		g_free(queue_data);
-	}
-	else
-	{
-		httpconn->dirty = TRUE;
-	}
-}
-
 size_t
-msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t size)
+msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t size)
 {
 	char *params;
-	char *header;
+	char *data;
 	char *auth;
-	gboolean first;
 	const char *server_types[] = { "NS", "SB" };
 	const char *server_type;
 	size_t r; /* result of the write operation */
@@ -484,31 +479,14 @@
 	/* TODO: remove http data from servconn */
 
 	g_return_val_if_fail(httpconn != NULL, 0);
-	g_return_val_if_fail(data     != NULL, 0);
-	g_return_val_if_fail(size      > 0,    0);
+	g_return_val_if_fail(body != NULL, 0);
+	g_return_val_if_fail(size > 0, 0);
 
 	servconn = httpconn->servconn;
 
-	if (httpconn->waiting_response)
-	{
-		MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1);
-
-		queue_data->httpconn = httpconn;
-		queue_data->data     = g_memdup(data, size);
-		queue_data->size     = size;
-
-		httpconn->queue = g_list_append(httpconn->queue, queue_data);
-		/* httpconn->dirty = TRUE; */
-
-		/* servconn->processing = TRUE; */
-
-		return size;
-	}
-
-	first = httpconn->virgin;
 	server_type = server_types[servconn->type];
 
-	if (first)
+	if (httpconn->virgin)
 	{
 		host = "gateway.messenger.hotmail.com";
 
@@ -516,6 +494,7 @@
 		params = g_strdup_printf("Action=open&Server=%s&IP=%s",
 								 server_type,
 								 servconn->host);
+		httpconn->virgin = FALSE;
 	}
 	else
 	{
@@ -529,12 +508,12 @@
 		}
 
 		params = g_strdup_printf("SessionID=%s",
-								 httpconn->full_session_id);
+			httpconn->full_session_id);
 	}
 
 	auth = msn_httpconn_proxy_auth(httpconn);
 
-	header = g_strdup_printf(
+	data = g_strdup_printf(
 		"POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n"
 		"Accept: */*\r\n"
 		"Accept-Language: en-us\r\n"
@@ -545,25 +524,26 @@
 		"Connection: Keep-Alive\r\n"
 		"Pragma: no-cache\r\n"
 		"Content-Type: application/x-msn-messenger\r\n"
-		"Content-Length: %d\r\n",
+		"Content-Length: %d\r\n\r\n"
+		"%s",
 		host,
 		params,
 		host,
 		auth ? auth : "",
-		(int)size);
+		(int) size,
+		body ? body : "");
+
 
 	g_free(params);
 
-	if (auth != NULL)
-		g_free(auth);
+	g_free(auth);
 
-	r = write_raw(httpconn, header, data, size);
+	r = write_raw(httpconn, data, strlen(data));
 
-	g_free(header);
+	g_free(data);
 
-	if (r > 0)
+	if (r >= 0)
 	{
-		httpconn->virgin = FALSE;
 		httpconn->waiting_response = TRUE;
 		httpconn->dirty = FALSE;
 	}
@@ -629,7 +609,9 @@
 			*ret_buf = g_strdup("");
 			*ret_size = 0;
 
-			msn_httpconn_process_queue(httpconn);
+			if (httpconn->tx_handler > 0)
+				httpconn_write_cb(httpconn, httpconn->fd,
+					GAIM_INPUT_WRITE);
 
 			return TRUE;
 		}
@@ -781,7 +763,8 @@
 	*ret_buf  = body;
 	*ret_size = body_len;
 
-	msn_httpconn_process_queue(httpconn);
+	if (httpconn->tx_handler > 0)
+		httpconn_write_cb(httpconn, httpconn->fd, GAIM_INPUT_WRITE);
 
 	return TRUE;
 }