Rewrote the connection pool logic, and cleaned up the request queueing
authoralex <alex>
Mon, 11 Dec 2000 08:18:59 +0000 (08:18 +0000)
committeralex <alex>
Mon, 11 Dec 2000 08:18:59 +0000 (08:18 +0000)
* Rewrote the connection pool logic, and cleaned up the request queueing loop.

* Added ref/unref to SoupContext.

* Made getting a connection for a SoupContext generic which cleans up the code and makes it useable for purposes other than soup.

* Connection limits handling moved to the connection pooling to avoid races, and allows for better handling when we have hit the connection limit.

* Added soup-misc.[ch] which provide global functions for getting and setting the proxy context and the connection limit.

* Changed proxy to be a SoupContext.

* Support for http headers near completion.

* Added support for custom request headers which can override the standard headers without duplication.

* Lots of code reorg and cleaning up.

configure.in
libsoup/Makefile.am
libsoup/soup-context.c
libsoup/soup-context.h
libsoup/soup-misc.c [new file with mode: 0644]
libsoup/soup-misc.h [new file with mode: 0644]
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-queue.h
libsoup/soup-uri.h
libsoup/soup.h

index 054fc93..889059f 100644 (file)
@@ -9,7 +9,7 @@ AC_INIT(configure.in)
 AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)
 SOUP_MAJOR_VERSION=0
 SOUP_MINOR_VERSION=1
-SOUP_MICRO_VERSION=5
+SOUP_MICRO_VERSION=6
 SOUP_INTERFACE_AGE=0
 SOUP_BINARY_AGE=0
 SOUP_VERSION=$SOUP_MAJOR_VERSION.$SOUP_MINOR_VERSION.$SOUP_MICRO_VERSION
index 052135c..de48f27 100644 (file)
@@ -16,14 +16,16 @@ libsoup_la_SOURCES =        \
        soup-context.c  \
        soup-queue.c    \
        soup-request.c  \
-       soup-uri.c
+       soup-uri.c      \
+       soup-misc.c
 
 soupinclude_HEADERS =  \
        soup.h          \
        soup-context.h  \
        soup-queue.h    \
        soup-request.h  \
-       soup-uri.h
+       soup-uri.h      \
+       soup-misc.c
 
 EXTRA_DIST = soup-private.h
 
index 6353f1b..a59b88a 100644 (file)
@@ -8,13 +8,19 @@
  * Copyright (C) 2000, Helix Code, Inc.
  */
 
+#include <glib.h>
 #include <gnet/gnet.h>
 
 #include "soup-context.h"
 #include "soup-private.h"
+#include "soup-misc.h"
 #include "soup-uri.h"
 
-GHashTable *servers;
+gint connection_count = 0;
+
+GHashTable *servers;  /* KEY: hostname, VALUE: SoupServer */
+
+static guint most_recently_used_id = 0;
 
 static SoupContext *
 soup_context_new (SoupServer *server, SoupUri *uri) 
@@ -23,9 +29,7 @@ soup_context_new (SoupServer *server, SoupUri *uri)
        ctx->priv = g_new0 (SoupContextPrivate, 1);
        ctx->priv->server = server;
        ctx->priv->keep_alive = TRUE;
-       ctx->priv->chunk_size = DEFAULT_CHUNK_SIZE;
        ctx->uri = uri;
-       ctx->custom_headers = NULL;
        return ctx;
 }
 
@@ -56,6 +60,7 @@ soup_context_get (gchar *uri)
        if (ret) return ret;
 
        ret = soup_context_new (serv, suri);
+       soup_context_ref (ret);
 
        g_hash_table_insert (serv->contexts, suri->path, ret);
 
@@ -63,8 +68,42 @@ soup_context_get (gchar *uri)
 }
 
 void
-soup_context_free (SoupContext *ctx)
+soup_context_ref (SoupContext *ctx)
 {
+       ctx->priv->refcnt++;
+}
+
+void
+soup_context_unref (SoupContext *ctx)
+{
+       if (ctx->priv->refcnt-- == 0) {
+               SoupServer *serv = ctx->priv->server;
+
+               g_hash_table_remove (serv->contexts, ctx->uri->path);
+
+               if (g_hash_table_size (serv->contexts) == 0) {
+                       GSList *conns = serv->connections;
+
+                       g_hash_table_remove (servers, serv->host);
+                       
+                       while (conns) {
+                               SoupConnection *conn = conns->data;
+                               gnet_tcp_socket_unref (conn->socket);
+                               g_free (conn);
+                               connection_count--;
+
+                               conns = conns->next;
+                       }
+
+                       g_free (serv->host);
+                       g_slist_free (serv->connections);
+                       g_hash_table_destroy (serv->contexts);
+                       g_free (serv);
+               }
+                       
+               soup_uri_free (ctx->uri);
+               g_free (ctx);
+       }
 }
 
 struct SoupContextConnectFunctor {
@@ -96,6 +135,8 @@ soup_context_connect_cb (GTcpSocket                   *socket,
                new_conn->in_use = TRUE;
                new_conn->socket = socket;
 
+               connection_count++;
+
                ctx->priv->server->connections = 
                        g_slist_prepend (ctx->priv->server->connections, 
                                         new_conn);
@@ -111,15 +152,68 @@ soup_context_connect_cb (GTcpSocket                   *socket,
        }
 }
 
-void
+struct SoupConnDesc {
+       SoupServer     *serv;
+       SoupConnection *conn;
+};
+
+static void
+soup_prune_foreach (gchar *hostname, 
+                   SoupServer *serv, 
+                   struct SoupConnDesc *last)
+{
+       GSList *conns = serv->connections;
+
+       while (conns) {
+               SoupConnection *conn = conns->data;
+               if (!conn->in_use)
+                       if (last->conn == NULL || 
+                           last->conn->last_used_id > conn->last_used_id) {
+                               last->conn = conn;
+                               last->serv = serv;
+                       }
+               
+               conns = conns->next;
+       }
+}
+
+static gboolean
+soup_prune_least_used_connection (void)
+{
+       struct SoupConnDesc last;
+       last.serv = NULL;
+       last.conn = NULL;
+
+       g_hash_table_foreach (servers, (GHFunc) soup_prune_foreach, &last);
+
+       if (last.conn) {
+               last.serv->connections = 
+                       g_slist_remove (last.serv->connections, last.conn);
+               gnet_tcp_socket_unref (last.conn->socket);
+               g_free (last.conn);
+
+               connection_count--;
+
+               return TRUE;
+       }
+               
+       return FALSE;
+}
+
+gpointer
 soup_context_get_connection (SoupContext           *ctx,
                             SoupConnectCallbackFn  cb,
                             gpointer               user_data)
 {
        GSList *conns;
 
+       if (connection_count >= soup_get_connection_limit() && 
+           !soup_prune_least_used_connection ()) {
+               //FIXME: set timeout to try pruning again
+       }
+
        if (!ctx->priv->keep_alive)
-               goto FORCE_NEW_CONNECTION;
+               goto NEW_CONNECTION;
        
        conns = ctx->priv->server->connections;
 
@@ -128,34 +222,35 @@ soup_context_get_connection (SoupContext           *ctx,
 
                if (!conn->in_use && conn->port == ctx->uri->port) {
                        conn->in_use = TRUE;
+
                        (*cb) (ctx, 
                               SOUP_CONNECT_ERROR_NONE, 
                               conn->socket, 
                               user_data);
-                       return;
+
+                       return NULL;
                }
 
                conns = conns->next;
        }
 
FORCE_NEW_CONNECTION:
+ NEW_CONNECTION:
        {
                struct SoupContextConnectFunctor *data;
                data = g_new0 (struct SoupContextConnectFunctor, 1);
                data->ctx = ctx;
                data->cb = cb;
                data->user_data = user_data;
-               gnet_tcp_socket_connect_async (ctx->uri->host, 
-                                              ctx->uri->port,
-                                              soup_context_connect_cb,
-                                              user_data);
-               return;
+               return gnet_tcp_socket_connect_async (ctx->uri->host, 
+                                                     ctx->uri->port,
+                                                     soup_context_connect_cb,
+                                                     user_data);
        }
 }
 
 void 
-soup_context_return_connection (SoupContext       *ctx,
-                               GTcpSocket        *socket)
+soup_context_release_connection (SoupContext       *ctx,
+                                GTcpSocket        *socket)
 {
        SoupServer *server = ctx->priv->server;
        GSList *conns = server->connections;
@@ -165,12 +260,14 @@ soup_context_return_connection (SoupContext       *ctx,
 
                if (conn->socket == socket) {
                        if (ctx->priv->keep_alive) {
+                               conn->last_used_id = ++most_recently_used_id;
                                conn->in_use = FALSE;
                        } else {
                                server->connections = 
                                        g_slist_remove (server->connections, 
-                                                       socket);
+                                                       conn);
                                gnet_tcp_socket_unref (socket);
+                               g_free (conn);
                                connection_count--;
                        }
 
index c1f95e6..393e261 100644 (file)
@@ -8,19 +8,19 @@
  * Copyright (C) 2000, Helix Code, Inc.
  */
 
-#ifndef  SOUP_CONTEXT_H
-#define  SOUP_CONTEXT_H 1
+#ifndef SOUP_CONTEXT_H
+#define SOUP_CONTEXT_H 1
 
 #include <glib.h>
+#include <gnet/gnet.h>
 
 #include "soup-uri.h"
 
 typedef struct _SoupContextPrivate SoupContextPrivate;
 
 typedef struct {
-       SoupContextPrivate       *priv;
-       SoupUri                  *uri;
-       GList                    *custom_headers;
+       SoupContextPrivate *priv;
+       SoupUri            *uri;
 } SoupContext;
 
 typedef enum {
@@ -34,19 +34,19 @@ typedef void (*SoupConnectCallbackFn) (SoupContext          *ctx,
                                       GTcpSocket           *socket, 
                                       gpointer              user_data);
 
-SoupContext *soup_context_get               (gchar                 *uri);
+typedef gpointer SoupConnectId;
 
-void         soup_context_free              (SoupContext           *ctx);
+SoupContext  *soup_context_get                (gchar               *uri);
 
-void         soup_context_add_header        (SoupContext           *ctx,
-                                            gchar                 *name,
-                                            gchar                 *value);
+void          soup_context_ref                (SoupContext         *ctx);
 
-void         soup_context_get_connection    (SoupContext           *ctx,
-                                            SoupConnectCallbackFn  cb,
-                                            gpointer               user_data);
+void          soup_context_unref              (SoupContext         *ctx);
 
-void         soup_context_return_connection (SoupContext           *ctx,
-                                            GTcpSocket            *socket);
+SoupConnectId soup_context_get_connection     (SoupContext          *ctx,
+                                              SoupConnectCallbackFn cb,
+                                              gpointer              user_data);
+
+void          soup_context_release_connection (SoupContext         *ctx,
+                                              GTcpSocket          *socket);
 
 #endif /*SOUP_CONTEXT_H*/
diff --git a/libsoup/soup-misc.c b/libsoup/soup-misc.c
new file mode 100644 (file)
index 0000000..31d19f9
--- /dev/null
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-queue.c: Asyncronous Callback-based SOAP Request Queue.
+ *
+ * Authors:
+ *      Alex Graveley (alex@helixcode.com)
+ *
+ * Copyright (C) 2000, Helix Code, Inc.
+ */
+
+#include "soup-misc.h"
+
+gint max_connections = -1;
+
+static SoupContext *proxy_context;
+
+void         
+soup_set_proxy (SoupContext *context)
+{
+       if (proxy_context)
+               soup_context_unref (proxy_context);
+
+       proxy_context = context;
+       soup_context_ref (proxy_context);
+}
+
+SoupContext *
+soup_get_proxy (void)
+{
+       return proxy_context;
+}
+
+void         
+soup_set_connection_limit (guint max_conn)
+{
+       max_connections = max_conn;
+}
+
+guint
+soup_get_connection_limit (void)
+{
+       return max_connections;
+}
+
diff --git a/libsoup/soup-misc.h b/libsoup/soup-misc.h
new file mode 100644 (file)
index 0000000..67e94f5
--- /dev/null
@@ -0,0 +1,26 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-queue.h: Asyncronous Callback-based SOAP Request Queue.
+ *
+ * Authors:
+ *      Alex Graveley (alex@helixcode.com)
+ *
+ * Copyright (C) 2000, Helix Code, Inc.
+ */
+
+#ifndef SOUP_MISC_H
+#define SOUP_MISC_H 1
+
+#include <glib.h>
+
+#include "soup-context.h"
+
+void         soup_set_proxy            (SoupContext *ctx);
+
+SoupContext *soup_get_proxy            (void);
+
+void         soup_set_connection_limit (guint max_conn);
+
+guint        soup_get_connection_limit (void);
+
+#endif /* SOUP_MISC_H */
index be3b7f4..645fd44 100644 (file)
  * extraneous circumstances.
  */
 
-#ifndef  SOAP_PRIVATE_H
-#define  SOAP_PRIVATE_H 1
+#ifndef SOAP_PRIVATE_H
+#define SOAP_PRIVATE_H 1
 
 #include "soup-queue.h"
 
-#define DEFAULT_CHUNK_SIZE  1024
-#define RESPONSE_BLOCK_SIZE 1024
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RESPONSE_BLOCK_SIZE 8192
+
+extern gint        connection_count;
+extern GSList     *active_requests;   /* CONTAINS: SoupRequest */
+extern GHashTable *servers;           /* KEY: uri->host, VALUE: SoupServer */
 
 typedef struct {
+       GTcpSocket *socket;
        guint       port;
        gboolean    in_use;
-       GTcpSocket *socket;
+       guint       last_used_id;
 } SoupConnection;
 
 typedef struct {
        gchar      *host;
-       GSList     *connections;  /* CONTAINS: SoupConnection */
-       GHashTable *contexts;     /* KEY: uri->path, VALUE: SoupContext */
+       GSList     *connections;      /* CONTAINS: SoupConnection */
+       GHashTable *contexts;         /* KEY: uri->path, VALUE: SoupContext */
 } SoupServer;
 
-extern GHashTable *servers;
-extern guint connection_count;
-extern GList *active_requests;
+typedef enum {
+       SOUP_PROTOCOL_HTTP_1_1,
+       SOUP_PROTOCOL_HTTP_1_0,
+       SOUP_PROTOCOL_SMTP
+} SoupProtocol;
 
 struct _SoupContextPrivate {
-       SoupServer *server;
+       SoupServer   *server;
+       guint         refcnt;
 
-       gboolean    keep_alive;
-       gint        chunk_size;
+       SoupProtocol  protocol;
+       gboolean      keep_alive;
+       gboolean      is_chunked;
 };
 
 struct _SoupRequestPrivate {
-       GTcpSocket *socket;
+       GTcpSocket     *socket;
+
+       SoupConnectId   connect_tag;
+       guint           read_tag;
+       guint           write_tag;
+       guint           error_tag;
+       guint           timeout_tag;
 
-       gulong write_len;
-       gulong read_len;
+       gulong          write_len;
+       gulong          read_len;
 
-       guint connect_tag;
-       guint read_tag;
-       guint write_tag;
-       guint timeout_tag;
+       GString        *req_header;
+       GByteArray     *recv_buf;
 
-       SoupCallbackFn callback;
-       gpointer user_data;
+       SoupCallbackFn  callback;
+       gpointer        user_data;
 };
 
 SoupCallbackResult soup_request_issue_callback (SoupRequest   *req, 
                                                SoupErrorCode  error);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /*SOUP_PRIVATE_H*/
index 5ebb7db..6a3e029 100644 (file)
@@ -5,11 +5,16 @@
  * Authors:
  *      Alex Graveley (alex@helixcode.com)
  *
+ * soup_base64_encode() written by Joe Orton borrowed from ghttp.
+ *
  * Copyright (C) 2000, Helix Code, Inc.
  */
 
 #include <config.h>
 #include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
 #include <gnet/gnet.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
 #include "soup-queue.h"
 #include "soup-context.h"
+#include "soup-misc.h"
 #include "soup-private.h"
 
-guint connection_count = 0;
+GSList *active_requests = NULL;
 
-GList *active_requests = NULL;
+static guint soup_queue_idle_tag = 0;
 
-static guint max_connections = 4;
+static gint
+soup_substring_index (gchar *str, gint len, gchar *substr) 
+{
+       int i, sublen = strlen (substr);
+       
+       for (i = 0; i < len; ++i)
+               if (str[i] == substr[0])
+                       if (memcmp (&str[i], substr, sublen) == 0)
+                               return i;
 
-static guint soup_queue_idle_tag = 0;
+       return -1;
+}
+
+static void 
+soup_process_headers (SoupRequest *req, gchar *str, guint len)
+{
+       gchar *http_ver, *reason_phrase;
+       gint status_code;
+
+       sscanf (str, "%s %u %s", http_ver, &status_code, reason_phrase);
 
-static SoupContext *proxy_context;
+       req->response_code = status_code;
+       req->response_phrase = reason_phrase;
+
+       g_free (http_ver);
+}
 
 static gboolean 
 soup_queue_read_async (GIOChannel* iochannel, 
                       GIOCondition condition, 
                       SoupRequest *req)
 {
+       gchar read_buf[RESPONSE_BLOCK_SIZE];
        guint bytes_read;
+       gint index;
        GIOError error;
 
-       if (!req->response.body) {
-               req->response.body = g_malloc (RESPONSE_BLOCK_SIZE);
-               req->response.length = RESPONSE_BLOCK_SIZE;
-       } else if (req->priv->read_len == req->response.length) {
-               req->response.length += RESPONSE_BLOCK_SIZE;
-               req->response.body = g_realloc (req->response.body, 
-                                                req->response.length);
-       }
-
-       error = g_io_channel_read (iochannel, 
-                                  &req->response.body[req->priv->read_len], 
-                                  req->response.length - req->priv->read_len,
+       error = g_io_channel_read (iochannel,
+                                  read_buf,
+                                  sizeof (read_buf),
                                   &bytes_read);
 
        if (error == G_IO_ERROR_AGAIN)
@@ -65,28 +85,263 @@ soup_queue_read_async (GIOChannel* iochannel,
                return FALSE;
        }
 
+       if (!req->priv->recv_buf) 
+               req->priv->recv_buf = g_byte_array_new ();
+
        if (bytes_read == 0) {
+               index = soup_substring_index (req->priv->recv_buf->data, 
+                                             req->priv->recv_buf->len,
+                                             "\r\n\r\n");
+
+               req->response.length = req->priv->recv_buf->len - index + 4;
+               req->response.body = 
+                       g_memdup (&req->priv->recv_buf->data [index + 4],
+                                 req->response.length + 1);
+               req->response.body [req->response.length] = '\0';
+
                req->status = SOUP_STATUS_FINISHED;
                soup_request_issue_callback (req, SOUP_ERROR_NONE);
                return FALSE;
        }
+
+       g_byte_array_append (req->priv->recv_buf,
+                            read_buf,
+                            bytes_read);
        
        req->priv->read_len += bytes_read;
 
+       if (!req->response_code) {
+               index = soup_substring_index (req->priv->recv_buf->data, 
+                                             req->priv->recv_buf->len,
+                                             "\r\n\r\n");
+               if (index)
+                       soup_process_headers (req, 
+                                             req->priv->recv_buf->data,
+                                             index + 2);
+       } 
+
        return TRUE;
 }
 
+const char base64_alphabet[65] = 
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+static gchar *
+soup_base64_encode (gchar *text)
+{
+       char *buffer = NULL;
+       char *point = NULL;
+       int inlen = 0;
+       int outlen = 0;
+
+       /* check our args */
+       if (text == NULL)
+               return NULL;
+  
+       /* Use 'buffer' to store the output. Work out how big it should be...
+        * This must be a multiple of 4 bytes */
+  
+       inlen = strlen (text);
+       /* check our arg...avoid a pesky FPE */
+       if (inlen == 0) {
+               buffer = malloc (sizeof(char));
+               buffer[0] = '\0';
+               return buffer;
+       }
+
+       outlen = (inlen*4)/3;
+       if ((inlen % 3) > 0) /* got to pad */
+               outlen += 4 - (inlen % 3);
+  
+       buffer = malloc (outlen + 1); /* +1 for the \0 */
+       memset (buffer, 0, outlen + 1); /* initialize to zero */
+  
+       /* now do the main stage of conversion, 3 bytes at a time,
+        * leave the trailing bytes (if there are any) for later */
+  
+       for (point=buffer; inlen>=3; inlen-=3, text+=3) {
+               *(point++) = base64_alphabet [*text>>2]; 
+               *(point++) = base64_alphabet [(*text<<4 & 0x30) | 
+                                            *(text+1)>>4]; 
+               *(point++) = base64_alphabet [(*(text+1)<<2 & 0x3c) | 
+                                            *(text+2)>>6];
+               *(point++) = base64_alphabet [*(text+2) & 0x3f];
+       }
+  
+       /* Now deal with the trailing bytes */
+       if (inlen) {
+               /* We always have one trailing byte */
+               *(point++) = base64_alphabet [*text>>2];
+               *(point++) = base64_alphabet [(*text<<4 & 0x30) |
+                                            (inlen==2?*(text+1)>>4:0)]; 
+               *(point++) = (inlen == 1 ? 
+                             '=' : 
+                             base64_alphabet [*(text+1)<<2 & 0x3c]);
+               *(point++) = '=';
+       }
+       
+       *point = '\0';
+       
+       return buffer;
+}
+
+static void
+soup_encode_http_auth (gboolean proxy_auth, SoupUri *uri, GString *header)
+{
+       if (!uri->authmech) {
+               gchar *authpass, *encoded;
+               authpass = g_strconcat (uri->user, ":", uri->passwd);
+               encoded = soup_base64_encode (authpass);
+               g_string_sprintfa (header,
+                                  "%s: Basic %s\r\n",
+                                  proxy_auth ? 
+                                          "Proxy-Authorization" : 
+                                          "Authorization",
+                                  encoded);
+               g_free (encoded);
+               g_free (authpass);
+       }
+}
+
+#define custom_header(_name, _var) ({                                   \
+       gchar *val = g_hash_table_lookup (req->custom_headers, (_name)); \
+        if (val) (_var) = val;                                           \
+})
+
+static GString *
+soup_get_request_header (SoupRequest *req)
+{
+       GString *header = g_string_new ("");
+       gchar *uri;
+       SoupContext *proxy = soup_get_proxy ();
+
+       gchar   *host                = req->context->uri->host, 
+               *user_agent          = "Soup/0.1", 
+               *content_type        = "text/xml", 
+               *charset             = "\"utf-8\"",
+               *content_length      = NULL, 
+               *soapaction          = req->action,
+               *connection          = "keep-alive",
+               *proxy_authorization = NULL,
+               *authorization       = NULL;
+
+       gboolean my_content_length = FALSE;
+
+       if (req->custom_headers) {
+               custom_header ("Host", host);
+               custom_header ("User-Agent", user_agent);
+               custom_header ("Content-Type", content_type);
+               custom_header ("Charset", charset);
+               custom_header ("Content-Length", content_length);
+               custom_header ("SOAPAction", soapaction);
+               custom_header ("Proxy-Authorization", proxy_authorization);
+               custom_header ("Authorization", authorization);
+               custom_header ("Connection", connection);
+       }
+       
+       if (!content_length) {
+               content_length = g_strdup_printf ("%d", req->request.length);
+               my_content_length = TRUE;
+       }
+
+       if (proxy)
+               uri = soup_uri_to_string (proxy->uri, FALSE);
+       else 
+               uri = req->context->uri->path;
+
+       /* If we specify an absoluteURI in the request line, the 
+          Host header MUST be ignored by the proxy. */
+
+       g_string_sprintfa (header,
+                          "POST %s HTTP/1.1\r\n"
+                          "Host: %s\r\n"
+                          "User-Agent: %s\r\n"
+                          "Content-Type: %s;\r\n"
+                          "charset=%s\r\n"
+                          "Content-Length: %s\r\n"
+                          "SOAPAction: %s\r\n"
+                          "Connection: %s\r\n",
+                          uri,
+                          host,
+                          user_agent,
+                          content_type,
+                          charset,
+                          content_length,
+                          soapaction,
+                          connection);
+
+       if (my_content_length)
+               g_free (content_length);
+
+       if (!proxy_authorization) {
+               if (proxy && proxy->uri->user)
+                       soup_encode_http_auth (TRUE, proxy->uri, header);
+       } else
+               g_string_sprintfa (header, 
+                                  "Proxy-Authorization: %s\r\n",
+                                  proxy_authorization);
+
+       /* FIXME: if going through a proxy, do we use the absoluteURI on 
+                 the request line, or encode the Authorization header into
+                 the message? */
+
+       if (!authorization) {
+               if (req->context->uri->user)
+                       soup_encode_http_auth (FALSE, proxy->uri, header);
+       } else 
+               g_string_sprintfa (header, 
+                                  "Authorization: %s\r\n",
+                                  authorization);
+
+       g_string_append (header, "\r\n");
+
+       return header;
+}
+
 static gboolean 
 soup_queue_write_async (GIOChannel* iochannel, 
                        GIOCondition condition, 
                        SoupRequest *req)
 {
-       guint bytes_written;
+       guint head_len, body_len, total_len, total_written, bytes_written;
        GIOError error;
+       gchar *write_buf;
+       guint  write_len;
+
+       if (!req->priv->req_header)
+               req->priv->req_header = soup_get_request_header (req);
+
+       head_len = req->priv->req_header->len;
+       body_len = req->request.length;
+       total_len = head_len + body_len;
+       total_written = req->priv->write_len;
+       
+       if (total_written < head_len) {
+               /* headers not done yet */
+               /* send rest of headers and all of body */
+               /* maybe we should just send the rest of the headers here, 
+                  and avoid memcpy/alloca altogether at the loss of cpu 
+                  cycles */
+               guint offset = head_len - total_written;
+               write_len = (offset) + body_len;
+               write_buf = alloca (write_len);
+               memcpy (write_buf, 
+                       &req->priv->req_header->str [offset],
+                       offset);
+               memcpy (&write_buf [offset + 1],
+                       req->request.body,
+                       req->request.length);
+       } else if (total_written >= head_len) {
+               /* headers done, maybe some of body */
+               /* send rest of body */
+               guint offset = total_written - head_len;
+               write_buf = &req->request.body [offset];
+               write_len = body_len - offset;
+       }
 
        error = g_io_channel_write (iochannel, 
-                                   &req->request.body[req->priv->write_len]
-                                   req->request.length - req->priv->write_len,
+                                   write_buf
+                                   write_len, 
                                    &bytes_written);
 
        if (error == G_IO_ERROR_AGAIN)
@@ -97,13 +352,14 @@ soup_queue_write_async (GIOChannel* iochannel,
                return FALSE;
        }
 
-       req->priv->write_len += bytes_written;
+       total_written += bytes_written;
+       req->priv->write_len = total_written;
 
-       if (req->priv->write_len == req->request.length) {
+       if (total_written == total_len) {
                req->status = SOUP_STATUS_READING_RESPONSE;
                req->priv->read_tag = 
                        g_io_add_watch (iochannel, 
-                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL
+                                       G_IO_IN, 
                                        (GIOFunc) soup_queue_read_async, 
                                        req);
                return FALSE;
@@ -112,6 +368,37 @@ soup_queue_write_async (GIOChannel* iochannel,
        return TRUE;
 }
 
+
+static gboolean 
+soup_queue_error_async (GIOChannel* iochannel, 
+                       GIOCondition condition, 
+                       SoupRequest *req)
+{
+       switch (condition) {
+       case G_IO_ERR:
+       case G_IO_HUP:
+       case G_IO_NVAL:
+               switch (req->status) {
+               case SOUP_STATUS_FINISHED:
+                       break;
+               case SOUP_STATUS_CONNECTING:
+                       soup_request_issue_callback (req, 
+                                                    SOUP_ERROR_CANT_CONNECT);
+                       break;
+               default:
+                       soup_request_issue_callback (req, 
+                                                    SOUP_ERROR_IO);
+                       break;
+               }
+       case G_IO_IN:
+       case G_IO_OUT:
+       case G_IO_PRI:
+               return TRUE;
+       }
+
+        return FALSE;
+}
+
 static void 
 soup_setup_socket (GIOChannel *channel)
 {
@@ -142,16 +429,21 @@ soup_queue_connect (SoupContext          *ctx,
 
                req->status = SOUP_STATUS_SENDING_REQUEST;
                req->priv->socket = socket;
+               req->priv->connect_tag = NULL;
                req->priv->write_tag = 
                        g_io_add_watch (channel, 
-                                       G_IO_OUT|G_IO_ERR|G_IO_HUP|G_IO_NVAL
+                                       G_IO_OUT, 
                                        (GIOFunc) soup_queue_write_async, 
                                        req);
+               req->priv->error_tag = 
+                       g_io_add_watch (channel, 
+                                       G_IO_ERR|G_IO_HUP|G_IO_NVAL, 
+                                       (GIOFunc) soup_queue_error_async, 
+                                       req);
                break;
        case SOUP_CONNECT_ERROR_ADDR_RESOLVE:
        case SOUP_CONNECT_ERROR_NETWORK:
                soup_request_issue_callback (req, SOUP_ERROR_CANT_CONNECT);
-               connection_count--;
                break;
        }
 }
@@ -159,52 +451,29 @@ soup_queue_connect (SoupContext          *ctx,
 static gboolean 
 soup_idle_handle_new_requests (gpointer unused)
 {
-        GList *iter;
-       gboolean work_to_do = FALSE;
-
-       if (connection_count >= max_connections)
-               return TRUE;
+        GSList *iter = active_requests;
        
-       for (iter = active_requests; iter; iter = iter->next) {
+       while (iter) {
                SoupRequest *req = iter->data;
-
-               if (connection_count >= max_connections)
-                       return TRUE;
+               SoupContext *ctx, *proxy;
 
                if (req->status != SOUP_STATUS_QUEUED)
                        continue;
 
-               if (req->priv->socket) {
-                       GTcpSocket *sock = req->priv->socket;
-                       GIOChannel *channel;
-                       channel = gnet_tcp_socket_get_iochannel (sock);
-
-                       req->status = SOUP_STATUS_SENDING_REQUEST;
-                       req->priv->write_tag = g_io_add_watch (
-                               channel, 
-                               G_IO_OUT|G_IO_ERR|G_IO_HUP|G_IO_NVAL, 
-                               (GIOFunc) soup_queue_write_async, 
-                               req);
-               } else {
-                       SoupContext *ctx;
-                       ctx = proxy_context ? proxy_context : req->context;
-                       connection_count++;
-
-                       req->status = SOUP_STATUS_CONNECTING;
+               proxy = soup_get_proxy ();
+               ctx = proxy ? proxy : req->context;
+
+               req->status = SOUP_STATUS_CONNECTING;
+               req->priv->connect_tag =
                        soup_context_get_connection (ctx, 
                                                     soup_queue_connect, 
                                                     req);
-               }
 
-               work_to_do = TRUE;
+               iter = iter->next;
        }
 
-       if (!work_to_do) {
-               soup_queue_idle_tag = 0;
-               return FALSE;
-       }
-
-       return TRUE;
+       soup_queue_idle_tag = 0;
+       return FALSE;
 }
 
 void 
@@ -216,8 +485,7 @@ soup_queue_request (SoupRequest    *req,
                soup_queue_idle_tag = 
                        g_idle_add (soup_idle_handle_new_requests, NULL);
 
-       if (req->response.body && 
-           req->response.owner == SOUP_BUFFER_SYSTEM_OWNED) {
+       if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED) {
                g_free (req->response.body);
                req->response.body = NULL;
                req->response.length = 0;
@@ -227,13 +495,15 @@ soup_queue_request (SoupRequest    *req,
        req->priv->user_data = user_data;
        req->status = SOUP_STATUS_QUEUED;
 
-       active_requests = g_list_append (active_requests, req);
+       soup_context_ref (req->context);
+
+       active_requests = g_slist_append (active_requests, req);
 }
 
 void 
 soup_queue_shutdown ()
 {
-        GList *iter;
+        GSList *iter;
 
        g_source_remove (soup_queue_idle_tag);
        soup_queue_idle_tag = 0;
@@ -241,19 +511,3 @@ soup_queue_shutdown ()
        for (iter = active_requests; iter; iter = iter->next)
                soup_request_cancel (iter->data);
 }
-
-void         
-soup_queue_set_proxy (SoupContext *context)
-{
-       if (proxy_context)
-               soup_context_free (proxy_context);
-
-       proxy_context = context;
-}
-
-SoupContext *
-soup_queue_get_proxy ()
-{
-       return proxy_context;
-}
-
index 1b6664a..cc9fa6c 100644 (file)
@@ -8,8 +8,8 @@
  * Copyright (C) 2000, Helix Code, Inc.
  */
 
-#ifndef  SOUP_QUEUE_H
-#define  SOUP_QUEUE_H 1
+#ifndef SOUP_QUEUE_H
+#define SOUP_QUEUE_H 1
 
 #include <glib.h>
 
@@ -19,6 +19,7 @@
 typedef enum {
        SOUP_RESULT_FREE_REQUEST = 0,
        SOUP_RESULT_RESEND_REQUEST,
+       SOUP_RESULT_CONTINUE_PROCESSING,
        SOUP_RESULT_DO_NOTHING
 } SoupCallbackResult;
 
@@ -34,20 +35,14 @@ typedef enum {
        SOUP_ERROR_UNKNOWN
 } SoupErrorCode;
 
-typedef SoupCallbackResult (*SoupCallbackFn) (SoupRequest  *req,
-                                             SoupErrorCode err,
-                                             gpointer      user_data);
+typedef SoupCallbackResult (*SoupCallbackFn) (SoupRequest   *req,
+                                             SoupErrorCode  err,
+                                             gpointer       user_data);
 
-void         soup_queue_request        (SoupRequest       *req, 
-                                       SoupCallbackFn     callback, 
-                                       gpointer           user_data);
+void         soup_queue_request  (SoupRequest       *req, 
+                                 SoupCallbackFn     callback, 
+                                 gpointer           user_data);
 
-void         soup_queue_cancel_request (SoupRequest       *req);
-
-void         soup_queue_set_proxy      (SoupContext       *ctx);
-
-SoupContext *soup_queue_get_proxy      (void);
-
-void         soup_queue_shutdown       (void);
+void         soup_queue_shutdown (void);
 
 #endif /* SOUP_QUEUE_H */
index a37b684..051d08a 100644 (file)
 
 #include <glib.h>
 
-#ifdef __cplusplus
-extern "C" {
-#pragma }
-#endif /* __cplusplus */
-
 typedef struct {
+       /* Changes here will only affect new connections: */
        gchar *protocol;
-       gchar *user;
-       gchar *authmech;
-       gchar *passwd;
        gchar *host;
        int port;
+
+       /* Changes will be applied to existing and future connections: */
        gchar *path;
+       gchar *user;
+       gchar *authmech;
+       gchar *passwd;
 } SoupUri;
 
 /* the cache system has been disabled because it would 
@@ -55,9 +53,4 @@ gchar   *soup_uri_to_string (const SoupUri *uri, gboolean show_password);
 
 void     soup_uri_free      (SoupUri *uri);
 
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-
 #endif /*SOUP_URI_H*/
index d451e19..f2fb71b 100644 (file)
@@ -8,13 +8,21 @@
  * Copyright (C) 2000, Helix Code, Inc.
  */
 
-#ifndef  SOUP_H
-#define  SOUP_H 1
+#ifndef SOUP_H
+#define SOUP_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 #include "soup-queue.h"
 #include "soup-context.h"
 #include "soup-request.h"
-#include "soup-header.h"
 #include "soup-uri.h"
+#include "soup-misc.h"
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /*SOUP_H*/