Move a bunch of logic here from soup-context. Now the session keeps track
authorDan Winship <danw@src.gnome.org>
Fri, 5 Sep 2003 20:02:15 +0000 (20:02 +0000)
committerDan Winship <danw@src.gnome.org>
Fri, 5 Sep 2003 20:02:15 +0000 (20:02 +0000)
* libsoup/soup-session.c: Move a bunch of logic here from
soup-context. Now the session keeps track of hosts (instead of
having a global soup_hosts hash) and their connections.
(soup_session_new_with_proxy, soup_session_new_full): New session
constructors to specify a proxy or a proxy and connection limits
(send_request): Add Authorization and Proxy-Authorization headers
before sending off the request.
(soup_session_queue_message, et al): Improve the way this works.
There's no need to use timeouts to wait for connections to become
free; we *know* when they become free.

* libsoup/soup-private.h: Remove SoupHost and some other
no-longer-used stuff.

* libsoup/soup-misc.c (soup_set_proxy, soup_get_proxy,
soup_set_connection_limit, soup_set_connection_limit): Gone. These
are all per-session now.

* libsoup/soup-message.c: Remove all SoupContext references
(mostly replaced with SoupUri references)
(cleanup_message): priv->connect_tag and priv->connection are gone
now, so this was just soup_message_io_cancel(). So remove
cleanup_message and replace it with that everywhere.
(soup_message_disconnect): Gone.
(soup_message_set_uri): Replaces soup_message_set_context.
(soup_message_set_connection, soup_message_get_connection): Gone

* libsoup/soup-message-server-io.c (parse_request_headers):
s/soup_message_set_context/soup_message_set_uri/

* libsoup/soup-message-private.h (SoupMessagePrivate): Remove
connect_tag, context, and connection.

* libsoup/soup-message-client-io.c (encode_http_auth): Gone.

* libsoup/soup-context.c: Gone

* tests/auth-test.c (identify_auth): update for session/context
changes

16 files changed:
ChangeLog
libsoup/Makefile.am
libsoup/soup-context.c [deleted file]
libsoup/soup-context.h [deleted file]
libsoup/soup-message-client-io.c
libsoup/soup-message-private.h
libsoup/soup-message-server-io.c
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-misc.c
libsoup/soup-misc.h
libsoup/soup-private.h
libsoup/soup-session.c
libsoup/soup-session.h
libsoup/soup.h
tests/auth-test.c

index b6a4284..ab34f5e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2003-09-05  Dan Winship  <danw@ximian.com>
+
+       * libsoup/soup-session.c: Move a bunch of logic here from
+       soup-context. Now the session keeps track of hosts (instead of
+       having a global soup_hosts hash) and their connections.
+       (soup_session_new_with_proxy, soup_session_new_full): New session
+       constructors to specify a proxy or a proxy and connection limits
+       (send_request): Add Authorization and Proxy-Authorization headers
+       before sending off the request.
+       (soup_session_queue_message, et al): Improve the way this works.
+       There's no need to use timeouts to wait for connections to become
+       free; we *know* when they become free.
+
+       * libsoup/soup-private.h: Remove SoupHost and some other
+       no-longer-used stuff.
+
+       * libsoup/soup-misc.c (soup_set_proxy, soup_get_proxy,
+       soup_set_connection_limit, soup_set_connection_limit): Gone. These
+       are all per-session now.
+
+       * libsoup/soup-message.c: Remove all SoupContext references
+       (mostly replaced with SoupUri references)
+       (cleanup_message): priv->connect_tag and priv->connection are gone
+       now, so this was just soup_message_io_cancel(). So remove
+       cleanup_message and replace it with that everywhere.
+       (soup_message_disconnect): Gone.
+       (soup_message_set_uri): Replaces soup_message_set_context.
+       (soup_message_set_connection, soup_message_get_connection): Gone
+
+       * libsoup/soup-message-server-io.c (parse_request_headers):
+       s/soup_message_set_context/soup_message_set_uri/
+
+       * libsoup/soup-message-private.h (SoupMessagePrivate): Remove
+       connect_tag, context, and connection.
+
+       * libsoup/soup-message-client-io.c (encode_http_auth): Gone.
+
+       * libsoup/soup-context.c: Gone
+
+       * tests/auth-test.c (identify_auth): update for session/context
+       changes
+
 2003-09-03  Dan Winship  <danw@ximian.com>
 
        * libsoup/soup-status.h: Renamed from soup-error.h, with types
index d6a5457..bae54f8 100644 (file)
@@ -31,7 +31,6 @@ libsoupinclude_HEADERS =      \
        soup.h                  \
        soup-address.h          \
        soup-connection.h       \
-       soup-context.h          \
        soup-headers.h          \
        soup-message.h          \
        soup-method.h           \
@@ -68,7 +67,6 @@ libsoup_2_2_la_SOURCES =              \
        soup-auth-ntlm.h                \
        soup-auth-ntlm.c                \
        soup-connection.c               \
-       soup-context.c                  \
        soup-dns.h                      \
        soup-dns.c                      \
        soup-gnutls.h                   \
diff --git a/libsoup/soup-context.c b/libsoup/soup-context.c
deleted file mode 100644 (file)
index 19166da..0000000
+++ /dev/null
@@ -1,828 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * soup-context.c: Asyncronous Callback-based HTTP Request Queue.
- *
- * Copyright (C) 2000-2003, Ximian, Inc.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <unistd.h>
-#include <string.h>
-#include <stdlib.h>
-#include <glib.h>
-
-#include <fcntl.h>
-#include <sys/types.h>
-
-#include <sys/socket.h>
-#include <netinet/tcp.h>
-#include <netinet/in.h>
-
-#include "soup-auth.h"
-#include "soup-auth-ntlm.h"
-#include "soup-context.h"
-#include "soup-connection.h"
-#include "soup-message-private.h"
-#include "soup-private.h"
-#include "soup-misc.h"
-#include "soup-socket.h"
-#include "soup-ssl.h"
-
-GHashTable *soup_hosts;  /* KEY: hostname, VALUE: SoupHost */
-static int connection_count = 0;
-
-struct SoupContextPrivate {
-       SoupUri      *uri;
-       SoupHost     *server;
-};
-
-#define PARENT_TYPE G_TYPE_OBJECT
-static GObjectClass *parent_class;
-
-static void
-init (GObject *object)
-{
-       SoupContext *ctx = SOUP_CONTEXT (object);
-
-       ctx->priv = g_new0 (SoupContextPrivate, 1);
-}
-
-static void
-free_path (gpointer path, gpointer realm, gpointer unused)
-{
-       g_free (path);
-       g_free (realm);
-}
-
-static void
-free_auth (gpointer key, gpointer auth, gpointer free_key)
-{
-       if (free_key)
-               g_free (key);
-       g_object_unref (auth);
-}
-
-static void
-finalize (GObject *object)
-{
-       SoupContext *ctx = SOUP_CONTEXT (object);
-       SoupHost *serv = ctx->priv->server;
-
-       if (serv && ctx->priv->uri) {
-               g_hash_table_remove (serv->contexts, ctx->priv->uri);
-               if (g_hash_table_size (serv->contexts) == 0) {
-                       /* Remove this host from the active hosts hash */
-                       g_hash_table_remove (soup_hosts, serv->host);
-
-                       /* Free all cached SoupAuths */
-                       if (serv->auth_realms) {
-                               g_hash_table_foreach (serv->auth_realms,
-                                                     free_path, NULL);
-                               g_hash_table_destroy (serv->auth_realms);
-                       }
-                       if (serv->auths) {
-                               g_hash_table_foreach (serv->auths,
-                                                     free_auth,
-                                                     GINT_TO_POINTER (TRUE));
-                               g_hash_table_destroy (serv->auths);
-                       }
-                       if (serv->ntlm_auths) {
-                               g_hash_table_foreach (serv->ntlm_auths,
-                                                     free_auth,
-                                                     GINT_TO_POINTER (FALSE));
-                               g_hash_table_destroy (serv->ntlm_auths);
-                       }
-
-                       g_hash_table_destroy (serv->contexts);
-                       g_free (serv->host);
-                       g_free (serv);
-               }
-       }
-
-       if (ctx->priv->uri)
-               soup_uri_free (ctx->priv->uri);
-
-       g_free (ctx->priv);
-
-       G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-class_init (GObjectClass *object_class)
-{
-       parent_class = g_type_class_ref (PARENT_TYPE);
-
-       /* virtual method override */
-       object_class->finalize = finalize;
-}
-
-SOUP_MAKE_TYPE (soup_context, SoupContext, class_init, init, PARENT_TYPE)
-
-
-/**
- * soup_context_get:
- * @uri: the stringified URI.
- *
- * Returns a pointer to the #SoupContext representing @uri. If a
- * context already exists for the URI, it is returned with an added
- * reference. Otherwise, a new context is created with a reference
- * count of one.
- *
- * Return value: a #SoupContext representing @uri.
- */
-SoupContext *
-soup_context_get (const char *uri)
-{
-       SoupUri *suri;
-       SoupContext *ctx;
-
-       g_return_val_if_fail (uri != NULL, NULL);
-
-       suri = soup_uri_new (uri);
-       if (!suri)
-               return NULL;
-
-       ctx = soup_context_from_uri (suri);
-       soup_uri_free (suri);
-
-       return ctx;
-}
-
-/**
- * soup_context_uri_hash:
- * @key: a #SoupUri
- *
- * Return value: Hash value of the user, passwd, path, and query
- * fields in @key.
- **/
-static guint
-soup_context_uri_hash (gconstpointer key)
-{
-       const SoupUri *uri = key;
-       guint ret;
-
-       ret = uri->protocol;
-       if (uri->path)
-               ret += g_str_hash (uri->path);
-       if (uri->query)
-               ret += g_str_hash (uri->query);
-       if (uri->user)
-               ret += g_str_hash (uri->user);
-       if (uri->passwd)
-               ret += g_str_hash (uri->passwd);
-
-       return ret;
-}
-
-static inline gboolean
-parts_equal (const char *one, const char *two)
-{
-       if (!one && !two)
-               return TRUE;
-       if (!one || !two)
-               return FALSE;
-       return !strcmp (one, two);
-}
-
-/**
- * soup_context_uri_equal:
- * @v1: a #SoupUri
- * @v2: a #SoupUri
- *
- * Return value: %TRUE if @v1 and @v2 match in user, passwd, path, and
- * query. Otherwise, %FALSE.
- **/
-static gboolean
-soup_context_uri_equal (gconstpointer v1, gconstpointer v2)
-{
-       const SoupUri *one = v1;
-       const SoupUri *two = v2;
-
-       if (one->protocol != two->protocol)
-               return FALSE;
-       if (!parts_equal (one->path, two->path))
-               return FALSE;
-       if (!parts_equal (one->user, two->user))
-               return FALSE;
-       if (!parts_equal (one->passwd, two->passwd))
-               return FALSE;
-       if (!parts_equal (one->query, two->query))
-               return FALSE;
-
-       return TRUE;
-}
-
-/**
- * soup_context_from_uri:
- * @suri: a #SoupUri.
- *
- * Returns a pointer to the #SoupContext representing @suri. If a
- * context already exists for the URI, it is returned with an added
- * reference. Otherwise, a new context is created with a reference
- * count of one.
- *
- * Return value: a #SoupContext representing @uri.
- */
-SoupContext *
-soup_context_from_uri (const SoupUri *suri)
-{
-       SoupHost *serv = NULL;
-       SoupContext *ctx = NULL;
-
-       g_return_val_if_fail (suri != NULL, NULL);
-       g_return_val_if_fail (suri->protocol != 0, NULL);
-
-       if (!soup_hosts) {
-               soup_hosts = g_hash_table_new (soup_str_case_hash,
-                                              soup_str_case_equal);
-       } else
-               serv = g_hash_table_lookup (soup_hosts, suri->host);
-
-       if (!serv) {
-               serv = g_new0 (SoupHost, 1);
-               serv->host = g_strdup (suri->host);
-               g_hash_table_insert (soup_hosts, serv->host, serv);
-       }
-
-       if (!serv->contexts) {
-               serv->contexts = g_hash_table_new (soup_context_uri_hash,
-                                                  soup_context_uri_equal);
-       } else
-               ctx = g_hash_table_lookup (serv->contexts, suri);
-
-       if (!ctx) {
-               ctx = g_object_new (SOUP_TYPE_CONTEXT, NULL);
-               ctx->priv->server = serv;
-               ctx->priv->uri = soup_uri_copy (suri);
-
-               g_hash_table_insert (serv->contexts, ctx->priv->uri, ctx);
-       }
-
-       return g_object_ref (ctx);
-}
-
-static void
-connection_disconnected (SoupConnection *conn, gpointer user_data)
-{
-       SoupHost *server = user_data;
-       SoupAuth *auth;
-
-       if (server->ntlm_auths) {
-               auth = g_hash_table_lookup (server->ntlm_auths, conn);
-               if (auth) {
-                       g_hash_table_remove (server->ntlm_auths, conn);
-                       g_object_unref (auth);
-               }
-       }
-
-       server->connections = g_slist_remove (server->connections, conn);
-       connection_count--;
-}
-
-struct SoupConnectData {
-       SoupContext           *ctx;
-       SoupConnectCallbackFn  cb;
-       gpointer               user_data;
-
-       guint                  timeout_tag;
-       SoupConnection        *conn;
-};
-
-static void
-prune_connection_foreach (gpointer key, gpointer value, gpointer data)
-{
-       SoupHost *serv = value;
-       SoupConnection **last = data;
-       GSList *conns;
-
-       for (conns = serv->connections; conns; conns = conns->next) {
-               SoupConnection *conn = conns->data;
-
-               if (soup_connection_is_in_use (conn))
-                       continue;
-
-               if (*last == NULL ||
-                   soup_connection_last_used (*last) >
-                   soup_connection_last_used (conn))
-                       *last = conn;
-       }
-}
-
-static gboolean
-prune_least_used_connection (void)
-{
-       SoupConnection *last = NULL;
-
-       g_hash_table_foreach (soup_hosts, 
-                             (GHFunc) prune_connection_foreach, 
-                             &last);
-       if (last) {
-               soup_connection_disconnect (last);
-               g_object_unref (last);
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-static gboolean retry_connect_timeout_cb (struct SoupConnectData *data);
-
-static void
-soup_context_connect_cb (SoupConnection *conn,
-                        guint           status,
-                        gpointer        user_data)
-{
-       struct SoupConnectData *data = user_data;
-       SoupContext            *ctx = data->ctx;
-
-       switch (status) {
-       case SOUP_STATUS_OK:
-               g_signal_connect (conn, "disconnected",
-                                 G_CALLBACK (connection_disconnected),
-                                 ctx->priv->server);
-
-               /* FIXME */
-               g_object_set_data (G_OBJECT (conn), "SoupContext-port",
-                                  GUINT_TO_POINTER (ctx->priv->uri->port));
-
-               ctx->priv->server->connections =
-                       g_slist_prepend (ctx->priv->server->connections, conn);
-
-               break;
-
-       case SOUP_STATUS_CANT_RESOLVE:
-               connection_count--;
-               g_object_unref (conn);
-               break;
-
-       default:
-               connection_count--;
-               g_object_unref (conn);
-
-               /*
-                * Check if another connection exists to this server
-                * before reporting error. 
-                */
-               if (ctx->priv->server->connections) {
-                       data->timeout_tag =
-                               g_timeout_add (
-                                       150,
-                                       (GSourceFunc) retry_connect_timeout_cb,
-                                       data);
-                       return;
-               }
-
-               break;
-       }
-
-       (*data->cb) (ctx, status, conn, data->user_data);
-       g_object_unref (ctx);
-       g_free (data);
-}
-
-static gboolean
-try_existing_connections (SoupContext           *ctx,
-                         SoupConnectCallbackFn  cb,
-                         gpointer               user_data)
-{
-       GSList *conns = ctx->priv->server->connections;
-       
-       while (conns) {
-               SoupConnection *conn = conns->data;
-               guint port = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (conn), "SoupContext-port"));
-
-               if (!soup_connection_is_in_use (conn) &&
-                   port == ctx->priv->uri->port) {
-                       /* Issue success callback */
-                       (*cb) (ctx, SOUP_STATUS_OK, conn, user_data);
-                       return TRUE;
-               }
-
-               conns = conns->next;
-       }
-
-       return FALSE;
-}
-
-static gboolean
-try_create_connection (struct SoupConnectData *data)
-{
-       int conn_limit = soup_get_connection_limit ();
-       SoupContext *proxy = soup_get_proxy ();
-       SoupUri *uri = data->ctx->priv->uri;
-
-       /* 
-        * Check if we are allowed to create a new connection, otherwise wait
-        * for next timeout.  
-        */
-       if (conn_limit &&
-           connection_count >= conn_limit &&
-           !prune_least_used_connection ()) {
-               data->conn = NULL;
-               return FALSE;
-       }
-
-       connection_count++;
-
-       data->timeout_tag = 0;
-
-       if (proxy) {
-               if (uri->protocol == SOUP_PROTOCOL_HTTPS) {
-                       data->conn = soup_connection_new_tunnel (
-                               proxy->priv->uri, uri,
-                               soup_context_connect_cb, data);
-               } else {
-                       data->conn = soup_connection_new_proxy (
-                               proxy->priv->uri,
-                               soup_context_connect_cb, data);
-               }
-       } else
-               data->conn = soup_connection_new (uri, soup_context_connect_cb, data);
-
-       return TRUE;
-}
-
-static gboolean
-retry_connect_timeout_cb (struct SoupConnectData *data)
-{
-       if (try_existing_connections (data->ctx, 
-                                     data->cb, 
-                                     data->user_data)) {
-               g_object_unref (data->ctx);
-               g_free (data);
-               return FALSE;
-       }
-
-       return try_create_connection (data) == FALSE;
-}
-
-/**
- * soup_context_get_connection:
- * @ctx: a #SoupContext.
- * @cb: a #SoupConnectCallbackFn to be called when a valid connection is
- * available.
- * @user_data: the user_data passed to @cb.
- *
- * Initiates the process of establishing a network connection to the
- * server referenced in @ctx. If an existing connection is available
- * and not in use, @cb is called immediately, and a #SoupConnectId of
- * 0 is returned. Otherwise, a new connection is established. If the
- * current connection count exceeds that set in
- * soup_set_connection_limit(), the new connection is not created
- * until an existing connection is closed.
- *
- * Once a network connection is successfully established, or an
- * existing connection becomes available for use, @cb is called,
- * passing the #SoupConnection representing it.
- *
- * Return value: a #SoupConnectId which can be used to cancel a
- * connection attempt using soup_context_cancel_connect().
- */
-SoupConnectId
-soup_context_get_connection (SoupContext           *ctx,
-                            SoupConnectCallbackFn  cb,
-                            gpointer               user_data)
-{
-       struct SoupConnectData *data;
-
-       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), NULL);
-
-       /* Look for an existing unused connection */
-       if (try_existing_connections (ctx, cb, user_data))
-               return NULL;
-
-       data = g_new0 (struct SoupConnectData, 1);
-       data->cb = cb;
-       data->user_data = user_data;
-
-       data->ctx = g_object_ref (ctx);
-
-       if (!try_create_connection (data)) {
-               data->timeout_tag =
-                       g_timeout_add (150,
-                                      (GSourceFunc) retry_connect_timeout_cb,
-                                      data);
-       }
-       return data;
-}
-
-/**
- * soup_context_cancel_connect:
- * @tag: a #SoupConnectId representing a connection in progress.
- *
- * Cancels the connection attempt represented by @tag. The
- * #SoupConnectCallbackFn passed in soup_context_get_connection() is
- * not called.
- */
-void
-soup_context_cancel_connect (SoupConnectId tag)
-{
-       struct SoupConnectData *data = tag;
-
-       g_return_if_fail (data != NULL);
-
-       if (data->timeout_tag)
-               g_source_remove (data->timeout_tag);
-       else if (data->conn) {
-               connection_count--;
-               g_object_unref (data->conn);
-       }
-
-       g_free (data);
-}
-
-/**
- * soup_context_get_uri:
- * @ctx: a #SoupContext.
- *
- * Returns a pointer to the #SoupUri represented by @ctx.
- *
- * Return value: the #SoupUri for @ctx.
- */
-const SoupUri *
-soup_context_get_uri (SoupContext *ctx)
-{
-       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), NULL);
-       return ctx->priv->uri;
-}
-
-static void
-get_idle_conns_for_host (gpointer key, gpointer value, gpointer data)
-{
-       SoupHost *host = value;
-       SoupConnection *conn;
-       GSList *c, **idle_conns = data;
-
-       for (c = host->connections; c; c = c->next) {
-               conn = c->data;
-               if (!soup_connection_is_in_use (conn))
-                       *idle_conns = g_slist_prepend (*idle_conns, conn);
-       }
-}
-
-/**
- * soup_connection_purge_idle:
- *
- * Closes all idle open connections.
- **/
-void
-soup_connection_purge_idle (void)
-{
-       GSList *idle_conns, *i;
-
-       if (!soup_hosts)
-               return;
-
-       idle_conns = NULL;
-       g_hash_table_foreach (soup_hosts, get_idle_conns_for_host, &idle_conns);
-
-       for (i = idle_conns; i; i = i->next) {
-               soup_connection_disconnect (i->data);
-               g_object_unref (i->data);
-       }
-       g_slist_free (idle_conns);
-}
-
-
-/* Authentication */
-
-SoupAuth *
-soup_context_lookup_auth (SoupContext *ctx, SoupMessage *msg)
-{
-       char *path, *dir;
-       const char *realm;
-
-       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), NULL);
-
-       if (ctx->priv->server->ntlm_auths && msg) {
-               SoupConnection *conn = soup_message_get_connection (msg);
-
-               if (conn) {
-                       GHashTable *ntlm_auths = ctx->priv->server->ntlm_auths;
-                       SoupAuth *auth;
-
-                       auth = g_hash_table_lookup (ntlm_auths, conn);
-                       if (!auth) {
-                               auth = soup_auth_ntlm_new ();
-                               g_hash_table_insert (ntlm_auths, conn, auth);
-                       }
-                       return auth;
-               }
-       }
-
-       if (!ctx->priv->server->auth_realms)
-               return NULL;
-
-       path = g_strdup (ctx->priv->uri->path);
-       dir = path;
-        do {
-                realm = g_hash_table_lookup (ctx->priv->server->auth_realms, path);
-                if (realm)
-                       break;
-
-                dir = strrchr (path, '/');
-                if (dir)
-                       *dir = '\0';
-        } while (dir);
-
-       g_free (path);
-       if (realm)
-               return g_hash_table_lookup (ctx->priv->server->auths, realm);
-       else
-               return NULL;
-}
-
-static gboolean
-update_auth_internal (SoupContext *ctx, SoupConnection *conn,
-                     const GSList *headers, gboolean prior_auth_failed)
-{
-       SoupHost *server = ctx->priv->server;
-       SoupAuth *new_auth, *prior_auth, *old_auth;
-       gpointer old_path, old_realm;
-       const char *path;
-       char *realm;
-       GSList *pspace, *p;
-
-       if (server->ntlm_auths && conn) {
-               prior_auth = g_hash_table_lookup (server->ntlm_auths, conn);
-               if (prior_auth) {
-                       if (soup_auth_is_authenticated (prior_auth)) {
-                               /* This is a "permission denied", not
-                                * a "password incorrect". There's
-                                * nothing more we can do.
-                                */
-                               return FALSE;
-                       }
-
-                       /* Free the intermediate auth */
-                       g_hash_table_remove (server->ntlm_auths, conn);
-                       g_object_unref (prior_auth);
-               }
-       }
-
-       /* Try to construct a new auth from the headers; if we can't,
-        * there's no way we'll be able to authenticate.
-        */
-       new_auth = soup_auth_new_from_header_list (headers, ctx->priv->uri->authmech);
-       if (!new_auth)
-               return FALSE;
-
-       /* See if this auth is the same auth we used last time */
-       prior_auth = soup_context_lookup_auth (ctx, NULL);
-       if (prior_auth &&
-           G_OBJECT_TYPE (prior_auth) == G_OBJECT_TYPE (new_auth) &&
-           !strcmp (soup_auth_get_realm (prior_auth),
-                    soup_auth_get_realm (new_auth))) {
-               g_object_unref (new_auth);
-               if (prior_auth_failed) {
-                       /* The server didn't like the username/password
-                        * we provided before.
-                        */
-                       soup_context_invalidate_auth (ctx, prior_auth);
-                       return FALSE;
-               } else {
-                       /* The user is trying to preauthenticate using
-                        * information we already have, so there's nothing
-                        * that needs to be done.
-                        */
-                       return TRUE;
-               }
-       }
-
-       if (SOUP_IS_AUTH_NTLM (new_auth)) {
-               if (!server->ntlm_auths)
-                       server->ntlm_auths = g_hash_table_new (NULL, NULL);
-               if (conn) {
-                       g_hash_table_insert (server->ntlm_auths, conn, new_auth);
-                       return soup_context_authenticate_auth (ctx, new_auth);
-               } else {
-                       g_object_unref (new_auth);
-                       return FALSE;
-               }
-       }
-
-       if (!server->auth_realms) {
-               server->auth_realms = g_hash_table_new (g_str_hash, g_str_equal);
-               server->auths = g_hash_table_new (g_str_hash, g_str_equal);
-       }
-
-       /* Record where this auth realm is used */
-       realm = g_strdup_printf ("%s:%s",
-                                soup_auth_get_scheme_name (new_auth),
-                                soup_auth_get_realm (new_auth));
-       pspace = soup_auth_get_protection_space (new_auth, ctx->priv->uri);
-       for (p = pspace; p; p = p->next) {
-               path = p->data;
-               if (g_hash_table_lookup_extended (server->auth_realms, path,
-                                                 &old_path, &old_realm)) {
-                       g_hash_table_remove (server->auth_realms, old_path);
-                       g_free (old_path);
-                       g_free (old_realm);
-               }
-
-               g_hash_table_insert (server->auth_realms,
-                                    g_strdup (path), g_strdup (realm));
-       }
-       soup_auth_free_protection_space (new_auth, pspace);
-
-       /* Now, make sure the auth is recorded. (If there's a
-        * pre-existing auth, we keep that rather than the new one,
-        * since the old one might already be authenticated.)
-        */
-       old_auth = g_hash_table_lookup (server->auths, realm);
-       if (old_auth) {
-               g_free (realm);
-               g_object_unref (new_auth);
-               new_auth = old_auth;
-       } else 
-               g_hash_table_insert (server->auths, realm, new_auth);
-
-       /* Try to authenticate if needed. */
-       if (!soup_auth_is_authenticated (new_auth))
-               return soup_context_authenticate_auth (ctx, new_auth);
-
-       return TRUE;
-}
-
-gboolean
-soup_context_update_auth (SoupContext *ctx, SoupMessage *msg)
-{
-       const GSList *headers;
-
-       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), FALSE);
-       g_return_val_if_fail (msg != NULL, FALSE);
-
-       if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
-               headers = soup_message_get_header_list (msg->response_headers,
-                                                       "Proxy-Authenticate");
-       } else {
-               headers = soup_message_get_header_list (msg->response_headers,
-                                                       "WWW-Authenticate");
-       }
-
-       return update_auth_internal (ctx, soup_message_get_connection (msg),
-                                    headers, TRUE);
-}
-
-void
-soup_context_preauthenticate (SoupContext *ctx, const char *header)
-{
-       GSList *headers;
-
-       g_return_if_fail (SOUP_IS_CONTEXT (ctx));
-       g_return_if_fail (header != NULL);
-
-       headers = g_slist_append (NULL, (char *)header);
-       update_auth_internal (ctx, NULL, headers, FALSE);
-       g_slist_free (headers);
-}
-
-gboolean
-soup_context_authenticate_auth (SoupContext *ctx, SoupAuth *auth)
-{
-       const SoupUri *uri = ctx->priv->uri;
-
-       if (!uri->user && soup_auth_fn) {
-               (*soup_auth_fn) (soup_auth_get_scheme_name (auth),
-                                (SoupUri *) uri,
-                                soup_auth_get_realm (auth), 
-                                soup_auth_fn_user_data);
-       }
-
-       if (!uri->user)
-               return FALSE;
-
-       soup_auth_authenticate (auth, uri->user, uri->passwd);
-       return TRUE;
-}
-
-void
-soup_context_invalidate_auth (SoupContext *ctx, SoupAuth *auth)
-{
-       char *realm;
-       gpointer key, value;
-
-       g_return_if_fail (SOUP_IS_CONTEXT (ctx));
-       g_return_if_fail (auth != NULL);
-
-       /* Try to just clean up the auth without removing it. */
-       if (soup_auth_invalidate (auth))
-               return;
-
-       /* Nope, need to remove it completely */
-       realm = g_strdup_printf ("%s:%s",
-                                soup_auth_get_scheme_name (auth),
-                                soup_auth_get_realm (auth));
-
-       if (g_hash_table_lookup_extended (ctx->priv->server->auths, realm,
-                                         &key, &value) &&
-           auth == (SoupAuth *)value) {
-               g_hash_table_remove (ctx->priv->server->auths, realm);
-               g_free (key);
-               g_object_unref (auth);
-       }
-       g_free (realm);
-}
diff --git a/libsoup/soup-context.h b/libsoup/soup-context.h
deleted file mode 100644 (file)
index 18735f5..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
-/*
- * Copyright (C) 2000-2003, Ximian, Inc.
- */
-
-#ifndef SOUP_CONTEXT_H
-#define SOUP_CONTEXT_H 1
-
-#include <libsoup/soup-types.h>
-
-#define SOUP_TYPE_CONTEXT            (soup_context_get_type ())
-#define SOUP_CONTEXT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONTEXT, SoupContext))
-#define SOUP_CONTEXT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONTEXT, SoupContextClass))
-#define SOUP_IS_CONTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONTEXT))
-#define SOUP_IS_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONTEXT))
-#define SOUP_CONTEXT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONTEXT, SoupContextClass))
-
-typedef struct SoupContextPrivate SoupContextPrivate;
-
-struct SoupContext {
-       GObject parent;
-
-       SoupContextPrivate *priv;
-};
-
-typedef struct {
-       GObjectClass parent_class;
-
-} SoupContextClass;
-
-GType soup_context_get_type (void);
-
-
-typedef void (*SoupConnectCallbackFn) (SoupContext    *ctx,
-                                      guint           status,
-                                      SoupConnection *conn, 
-                                      gpointer        user_data);
-
-typedef gpointer SoupConnectId;
-
-SoupContext   *soup_context_get               (const char           *uri);
-
-SoupContext   *soup_context_from_uri          (const SoupUri        *suri);
-
-SoupConnectId  soup_context_get_connection    (SoupContext          *ctx,
-                                              SoupConnectCallbackFn cb,
-                                              gpointer              user_data);
-
-void           soup_context_cancel_connect    (SoupConnectId         tag);
-
-const SoupUri *soup_context_get_uri           (SoupContext          *ctx);
-
-
-void           soup_context_preauthenticate   (SoupContext          *ctx,
-                                              const char           *header);
-                                         
-
-void           soup_connection_purge_idle     (void);
-
-
-#endif /*SOUP_CONTEXT_H*/
index 64848e8..d3a1dc5 100644 (file)
@@ -14,7 +14,6 @@
 
 #include "soup-message-private.h"
 #include "soup-auth.h"
-#include "soup-context.h"
 #include "soup-headers.h"
 #include "soup-misc.h"
 #include "soup-private.h"
@@ -92,33 +91,6 @@ parse_response_headers (SoupMessage *req,
        return SOUP_STATUS_OK;
 }
 
-static void
-encode_http_auth (SoupMessage *msg, GString *header, gboolean proxy_auth)
-{
-       SoupAuth *auth;
-       SoupContext *ctx;
-       char *token;
-
-       ctx = proxy_auth ? soup_get_proxy () : msg->priv->context;
-
-       auth = soup_context_lookup_auth (ctx, msg);
-       if (!auth)
-               return;
-       if (!soup_auth_is_authenticated (auth) &&
-           !soup_context_authenticate_auth (ctx, auth))
-               return;
-
-       token = soup_auth_get_authorization (auth, msg);
-       if (token) {
-               g_string_sprintfa (header, "%s: %s\r\n",
-                                  proxy_auth ? 
-                                       "Proxy-Authorization" : 
-                                       "Authorization",
-                                  token);
-               g_free (token);
-       }
-}
-
 static void 
 add_header (gpointer name, gpointer value, gpointer data)
 {
@@ -174,10 +146,6 @@ get_request_headers (SoupMessage *req, GString *header,
                *encoding = SOUP_TRANSFER_CONTENT_LENGTH;
        }
 
-       encode_http_auth (req, header, FALSE);
-       if (proxy)
-               encode_http_auth (req, header, TRUE);
-
        soup_message_foreach_header (req->request_headers, add_header, header);
        g_string_append (header, "\r\n");
 }
index 2d6c279..cff4702 100644 (file)
@@ -7,7 +7,6 @@
 #define SOUP_MESSAGE_PRIVATE_H 1
 
 #include <libsoup/soup-message.h>
-#include <libsoup/soup-context.h>
 
 typedef enum {
        SOUP_MESSAGE_STATUS_IDLE,
@@ -22,7 +21,6 @@ typedef enum {
 struct SoupMessagePrivate {
        SoupMessageStatus  status;
 
-       SoupConnectId      connect_tag;
        gpointer           io_data;
 
        guint              msg_flags;
@@ -33,8 +31,7 @@ struct SoupMessagePrivate {
 
        SoupHttpVersion    http_version;
 
-       SoupContext       *context;
-       SoupConnection    *connection;
+       SoupUri           *uri;
 };
 
 void             soup_message_run_handlers   (SoupMessage      *msg,
@@ -44,11 +41,8 @@ void             soup_message_cleanup        (SoupMessage      *req);
 
 gboolean         soup_message_is_keepalive   (SoupMessage      *msg);
 
-void             soup_message_set_context    (SoupMessage      *msg,
-                                             SoupContext      *new_ctx);
-void             soup_message_set_connection (SoupMessage      *msg,
-                                             SoupConnection   *conn);
-SoupConnection  *soup_message_get_connection (SoupMessage      *msg);
+void             soup_message_set_uri        (SoupMessage      *msg,
+                                             const SoupUri    *uri);
 
 
 typedef void     (*SoupMessageGetHeadersFn)  (SoupMessage      *msg,
index 45fccb8..f469d3b 100644 (file)
@@ -15,7 +15,6 @@
 #include "soup-message-private.h"
 #include "soup-address.h"
 #include "soup-auth.h"
-#include "soup-context.h"
 #include "soup-headers.h"
 #include "soup-misc.h"
 #include "soup-private.h"
@@ -28,7 +27,7 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
                       SoupTransferEncoding *encoding, guint *content_len,
                       gpointer sock)
 {
-       SoupContext *ctx;
+       SoupUri *uri;
        char *req_path = NULL, *url;
        const char *length, *enc, *req_host;
        SoupServer *server;
@@ -103,15 +102,15 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
                return SOUP_STATUS_BAD_REQUEST;
        }
 
-       ctx = soup_context_get (url);
+       uri = soup_uri_new (url);
        g_free (url);
        g_free (req_path);
 
-       if (!ctx)
+       if (!uri)
                return SOUP_STATUS_BAD_REQUEST;
 
-       soup_message_set_context (msg, ctx);
-       g_object_unref (ctx);
+       soup_message_set_uri (msg, uri);
+       soup_uri_free (uri);
 
        return SOUP_STATUS_OK;
 }
index 60faeec..2eb46e1 100644 (file)
@@ -8,12 +8,10 @@
 #include <string.h>
 
 #include "soup-auth.h"
-#include "soup-connection.h"
 #include "soup-marshal.h"
 #include "soup-message.h"
 #include "soup-message-private.h"
 #include "soup-misc.h"
-#include "soup-context.h"
 #include "soup-private.h"
 
 #define PARENT_TYPE G_TYPE_OBJECT
@@ -39,7 +37,6 @@ static void got_headers (SoupMessage *req);
 static void got_chunk (SoupMessage *req);
 static void got_body (SoupMessage *req);
 static void finished (SoupMessage *req);
-static void cleanup_message (SoupMessage *req);
 static void free_chunks (SoupMessage *msg);
 
 static void
@@ -65,10 +62,10 @@ finalize (GObject *object)
 {
        SoupMessage *msg = SOUP_MESSAGE (object);
 
-       cleanup_message (msg);
+       soup_message_io_cancel (msg);
 
-       if (msg->priv->context)
-               g_object_unref (msg->priv->context);
+       if (msg->priv->uri)
+               soup_uri_free (msg->priv->uri);
 
        if (msg->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (msg->request.body);
@@ -175,7 +172,7 @@ SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
 /**
  * soup_message_new:
  * @method: the HTTP method for the created request
- * @uri: the destination endpoint (as a string)
+ * @uri_string: the destination endpoint (as a string)
  * 
  * Creates a new empty #SoupMessage, which will connect to @uri
  *
@@ -183,18 +180,18 @@ SOUP_MAKE_TYPE (soup_message, SoupMessage, class_init, init, PARENT_TYPE)
  * be parsed).
  */
 SoupMessage *
-soup_message_new (const char *method, const char *uri)
+soup_message_new (const char *method, const char *uri_string)
 {
        SoupMessage *msg;
-       SoupContext *ctx;
+       SoupUri *uri;
 
-       ctx = soup_context_get (uri);
-       if (!ctx)
+       uri = soup_uri_new (uri_string);
+       if (!uri)
                return NULL;
 
        msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
        msg->method = method ? method : SOUP_METHOD_GET;
-       msg->priv->context = ctx;
+       msg->priv->uri = uri;
 
        return msg;
 }
@@ -206,21 +203,16 @@ soup_message_new (const char *method, const char *uri)
  * 
  * Creates a new empty #SoupMessage, which will connect to @uri
  *
- * Return value: the new #SoupMessage (or %NULL if @uri is invalid)
+ * Return value: the new #SoupMessage
  */
 SoupMessage *
 soup_message_new_from_uri (const char *method, const SoupUri *uri)
 {
        SoupMessage *msg;
-       SoupContext *ctx;
-
-       ctx = soup_context_from_uri (uri);
-       if (!ctx)
-               return NULL;
 
        msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
        msg->method = method ? method : SOUP_METHOD_GET;
-       msg->priv->context = ctx;
+       msg->priv->uri = soup_uri_copy (uri);
 
        return msg;
 }
@@ -350,7 +342,7 @@ soup_message_got_body (SoupMessage *msg)
 static void
 finished (SoupMessage *req)
 {
-       cleanup_message (req);
+       soup_message_io_cancel (req);
 }
 
 void
@@ -360,49 +352,18 @@ soup_message_finished (SoupMessage *msg)
 }
 
 
-static void
-cleanup_message (SoupMessage *req)
-{
-       if (req->priv->io_data)
-               soup_message_io_cancel (req);
-
-       if (req->priv->connect_tag) {
-               soup_context_cancel_connect (req->priv->connect_tag);
-               req->priv->connect_tag = NULL;
-       }
-
-       soup_message_set_connection (req, NULL);
-}
-
-/**
- * soup_message_disconnect:
- * @msg: a #SoupMessage
- *
- * Utility function to close and unref the connection associated with
- * @msg if there was an error.
- **/
-void
-soup_message_disconnect (SoupMessage *msg)
-{
-       if (msg->priv->connection) {
-               soup_connection_disconnect (msg->priv->connection);
-               soup_message_set_connection (msg, NULL);
-       }
-}
-
 /**
  * soup_message_cancel:
  * @msg: a #SoupMessage currently being processed.
  * 
  * Cancel a running message, and issue completion callback with an
- * error code of %SOUP_STATUS_CANCELLED. If not requeued by the
+ * status code of %SOUP_STATUS_CANCELLED. If not requeued by the
  * completion callback, the @msg will be destroyed.
  */
 void
 soup_message_cancel (SoupMessage *msg)
 {
        soup_message_set_status (msg, SOUP_STATUS_CANCELLED);
-       soup_message_disconnect (msg);
        soup_message_finished (msg);
 }
 
@@ -541,10 +502,10 @@ soup_message_foreach_header (GHashTable *hash, GHFunc func, gpointer user_data)
 void
 soup_message_prepare (SoupMessage *req)
 {
-       if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE) {
-               cleanup_message (req);
+       soup_message_io_cancel (req);
+
+       if (req->priv->status != SOUP_MESSAGE_STATUS_IDLE)
                req->priv->status = SOUP_MESSAGE_STATUS_IDLE;
-       }
 
        if (req->response.owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (req->response.body);
@@ -629,26 +590,19 @@ soup_message_is_keepalive (SoupMessage *msg)
 }
 
 void
-soup_message_set_context (SoupMessage *msg, SoupContext *new_ctx)
+soup_message_set_uri (SoupMessage *msg, const SoupUri *new_uri)
 {
        g_return_if_fail (SOUP_IS_MESSAGE (msg));
 
-       if (msg->priv->context && new_ctx) {
-               const SoupUri *old, *new;
-
-               old = soup_context_get_uri (msg->priv->context);
-               new = soup_context_get_uri (new_ctx);
-               if (strcmp (old->host, new->host) != 0)
-                       cleanup_message (msg);
-       } else if (!new_ctx)
-               cleanup_message (msg);
+       if (msg->priv->uri && new_uri) {
+               if (strcmp (msg->priv->uri->host, new_uri->host) != 0)
+                       soup_message_io_cancel (msg);
+       } else if (!new_uri)
+               soup_message_io_cancel (msg);
 
-       if (new_ctx)
-               g_object_ref (new_ctx);
-       if (msg->priv->context)
-               g_object_unref (msg->priv->context);
-
-       msg->priv->context = new_ctx;
+       if (msg->priv->uri)
+               soup_uri_free (msg->priv->uri);
+       msg->priv->uri = soup_uri_copy (new_uri);
 }
 
 const SoupUri *
@@ -656,26 +610,7 @@ soup_message_get_uri (SoupMessage *msg)
 {
        g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
 
-       return soup_context_get_uri (msg->priv->context);
-}
-
-void
-soup_message_set_connection (SoupMessage *msg, SoupConnection *conn)
-{
-       if (conn)
-               g_object_ref (conn);
-       if (msg->priv->connection)
-               g_object_unref (msg->priv->connection);
-
-       msg->priv->connection = conn;
-}
-
-SoupConnection *
-soup_message_get_connection (SoupMessage *msg)
-{
-       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
-
-       return msg->priv->connection;
+       return msg->priv->uri;
 }
 
 void
index 7491daa..33c2910 100644 (file)
@@ -91,8 +91,6 @@ void           soup_message_set_response        (SoupMessage       *msg,
 
 void           soup_message_cancel              (SoupMessage       *req);
 
-void           soup_message_disconnect          (SoupMessage       *req);
-
 void           soup_message_prepare             (SoupMessage       *req);
 
 void           soup_message_add_header          (GHashTable        *hash,
index d24d2d1..13a6f62 100644 (file)
 
 gboolean soup_initialized = FALSE;
 
-static guint max_connections = 10;
-
-static SoupContext *proxy_context = NULL;
-
 static SoupSecurityPolicy ssl_security_level = SOUP_SECURITY_DOMESTIC;
 
 /**
- * soup_set_proxy:
- * @context: a %SoupContext to use as the proxy context for all outgoing
- * connections.
- *
- * Use @context as the %SoupContext to connect to instead of the actual
- * destination specified in a SoupMessage. Messages will be routed through the
- * proxy host on their way to the actual specified destination. The URL for this
- * context should be of the form:
- *     [http|https]://<USERNAME>:<PASSWORD>@<PROXYHOST>
- */
-void
-soup_set_proxy (SoupContext *context)
-{
-       if (context)
-               g_object_ref (context);
-       if (proxy_context)
-               g_object_unref (proxy_context);
-
-       proxy_context = context;
-}
-
-/**
- * soup_get_proxy:
- *
- * Get the current proxy %SoupContext.
- *
- * Return value: the current proxy context.
- */
-SoupContext *
-soup_get_proxy (void)
-{
-       return proxy_context;
-}
-
-/**
- * soup_set_connection_limit:
- * @max_conn: the number of connections.
- *
- * Set the maximum concurrent connection limit for outgoing requests.
- */
-void
-soup_set_connection_limit (guint max_conn)
-{
-       max_connections = max_conn;
-}
-
-/**
- * soup_get_connection_limit:
- *
- * Return value: The maximum concurrent connection limit for outgoing requests.
- */
-guint
-soup_get_connection_limit (void)
-{
-       return max_connections;
-}
-
-/**
  * soup_set_security_policy:
  * @policy: the %SoupSecurityPolicy to use.
  *
@@ -428,19 +366,6 @@ static GSList *allow_tokens = NULL;
 static GSList *deny_tokens = NULL;
 
 static void
-soup_config_connection_limit (gchar *key, gchar *value)
-{
-       soup_set_connection_limit (MAX (atoi (value), 0));
-}
-
-static void
-soup_config_proxy_uri (gchar *key, gchar *value)
-{
-       SoupContext *con = soup_context_get (value);
-       if (con) soup_set_proxy (con);
-}
-
-static void
 soup_config_security_policy (gchar *key, gchar *value)
 {
        switch (toupper (value [0])) {
@@ -493,9 +418,6 @@ struct SoupConfigFuncs {
        gchar          *key;
        SoupConfigFunc  func;
 } soup_config_funcs [] = {
-       { "connection-limit", soup_config_connection_limit },
-       { "proxy-uri",        soup_config_proxy_uri },
-       { "proxy-url",        soup_config_proxy_uri },
        { "security-policy",  soup_config_security_policy },
        { "ssl-ca-file",      soup_config_ssl_ca_file },
        { "ssl-ca-directory", soup_config_ssl_ca_directory },
@@ -644,8 +566,6 @@ soup_load_config (gchar *config_file)
 {
        /* Reset values */
        if (soup_initialized) {
-               soup_set_proxy (NULL);
-               soup_set_connection_limit (0);
                soup_set_security_policy (SOUP_SECURITY_DOMESTIC);
        }
 
index 4688428..6321c0f 100644 (file)
@@ -7,7 +7,6 @@
 #define SOUP_MISC_H 1
 
 #include <glib.h>
-#include <libsoup/soup-context.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-uri.h>
 
 
 void               soup_load_config          (gchar       *config_file);
 
-void               soup_set_proxy            (SoupContext *context);
-
-SoupContext       *soup_get_proxy            (void);
-
-void               soup_set_connection_limit (guint        max_conn);
-
-guint              soup_get_connection_limit (void);
-
 typedef enum {
        SOUP_SECURITY_DOMESTIC = 1,
        SOUP_SECURITY_EXPORT   = 2,
index 05cc1a1..7cd8959 100644 (file)
@@ -21,23 +21,10 @@ extern "C" {
 #define RESPONSE_BLOCK_SIZE 8192
 
 extern gboolean    soup_initialized;
-extern GSList     *soup_active_requests; /* CONTAINS: SoupMessage */
-extern GHashTable *soup_hosts;           /* KEY: uri->host, VALUE: SoupHost */
 
 extern SoupAuthorizeFn soup_auth_fn;
 extern gpointer        soup_auth_fn_user_data;
 
-typedef struct {
-       gchar      *host;
-       GSList     *connections;      /* CONTAINS: SoupConnection */
-       GHashTable *contexts;         /* KEY: uri->path, VALUE: SoupContext */
-
-       GHashTable *auth_realms;      /* KEY: uri->path, VALUE: scheme:realm */
-       GHashTable *auths;            /* KEY: scheme:realm, VALUE: SoupAuth */
-
-       GHashTable *ntlm_auths;       /* KEY: SoupConnection, VALUE: SoupAuth */
-} SoupHost;
-
 #ifdef HAVE_IPV6
 #define soup_sockaddr_max sockaddr_in6
 #else
index b2c203e..3d01605 100644 (file)
 #include "soup-message-queue.h"
 #include "soup-private.h"
 
+typedef struct {
+       SoupUri    *root_uri;
+       guint       error;
+
+       GSList     *connections;      /* CONTAINS: SoupConnection */
+       guint       num_conns;
+
+       GHashTable *auth_realms;      /* path -> scheme:realm */
+       GHashTable *auths;            /* scheme:realm -> SoupAuth */
+
+       GHashTable *ntlm_auths;       /* SoupConnection -> SoupAuth */
+} SoupSessionHost;
+
 struct SoupSessionPrivate {
+       SoupUri *proxy_uri;
+       guint max_conns, max_conns_per_host;
+
        SoupMessageQueue *queue;
        guint queue_idle_tag;
 
+       GHashTable *hosts; /* SoupUri -> SoupSessionHost */
+       GHashTable *conns; /* SoupConnection -> SoupSessionHost */
+       guint num_conns;
+
+       SoupSessionHost *proxy_host;
 };
 
+static guint    host_uri_hash  (gconstpointer key);
+static gboolean host_uri_equal (gconstpointer v1, gconstpointer v2);
+
+static gboolean run_queue (SoupSession *session, gboolean try_pruning);
+
+#define SOUP_SESSION_MAX_CONNS_DEFAULT 10
+#define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 4
+
 #define PARENT_TYPE G_TYPE_OBJECT
 static GObjectClass *parent_class;
 
@@ -35,6 +64,12 @@ init (GObject *object)
 
        session->priv = g_new0 (SoupSessionPrivate, 1);
        session->priv->queue = soup_message_queue_new ();
+       session->priv->hosts = g_hash_table_new (host_uri_hash,
+                                                host_uri_equal);
+       session->priv->conns = g_hash_table_new (NULL, NULL);
+
+       session->priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
+       session->priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
 }
 
 static void
@@ -70,28 +105,272 @@ class_init (GObjectClass *object_class)
 
 SOUP_MAKE_TYPE (soup_session, SoupSession, class_init, init, PARENT_TYPE)
 
-
 SoupSession *
 soup_session_new (void)
 {
        return g_object_new (SOUP_TYPE_SESSION, NULL);
 }
 
+SoupSession *
+soup_session_new_with_proxy (const SoupUri *proxy_uri)
+{
+       SoupSession *session;
+
+       session = soup_session_new ();
+       if (proxy_uri)
+               session->priv->proxy_uri = soup_uri_copy (proxy_uri);
+
+       return session;
+}
+
+SoupSession *
+soup_session_new_full (const SoupUri *proxy_uri,
+                      guint max_conns, guint max_per_host)
+{
+       SoupSession *session;
+
+       session = soup_session_new_with_proxy (proxy_uri);
+       session->priv->max_conns = max_conns;
+       session->priv->max_conns_per_host = max_conns_per_host;
+
+       return session;
+}
+
+
+/* Hosts */
+static guint
+host_uri_hash (gconstpointer key)
+{
+       const SoupUri *uri = key;
+
+       return (uri->protocol << 16) + uri->port + g_str_hash (uri->host);
+}
+
+static gboolean
+host_uri_equal (gconstpointer v1, gconstpointer v2)
+{
+       const SoupUri *one = v1;
+       const SoupUri *two = v2;
+
+       if (one->protocol != two->protocol)
+               return FALSE;
+       if (one->port != two->port)
+               return FALSE;
+
+       return strcmp (one->host, two->host) == 0;
+}
+
+static SoupSessionHost *
+get_host_for_message (SoupSession *session, SoupMessage *msg)
+{
+       SoupSessionHost *host;
+       const SoupUri *source = soup_message_get_uri (msg);
+
+       host = g_hash_table_lookup (session->priv->hosts, source);
+       if (host)
+               return host;
+
+       host = g_new0 (SoupSessionHost, 1);
+       host->root_uri = g_new0 (SoupUri, 1);
+       host->root_uri->protocol = source->protocol;
+       host->root_uri->host = g_strdup (source->host);
+       host->root_uri->port = source->port;
+
+       g_hash_table_insert (session->priv->hosts, host->root_uri, host);
+       return host;
+}
+
+
+/* Authentication */
+
+static SoupAuth *
+lookup_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
+{
+       SoupSessionHost *host;
+       char *path, *dir;
+       const char *realm, *const_path;
+
+       if (proxy) {
+               host = session->priv->proxy_host;
+               const_path = "/";
+       } else {
+               host = get_host_for_message (session, msg);
+               const_path = soup_message_get_uri (msg)->path;
+       }
+       g_return_val_if_fail (host != NULL, NULL);
+
+       if (!host->auth_realms)
+               return NULL;
+
+       path = g_strdup (const_path);
+       dir = path;
+        do {
+                realm = g_hash_table_lookup (host->auth_realms, path);
+                if (realm)
+                       break;
 
-/* Default handlers */
+                dir = strrchr (path, '/');
+                if (dir)
+                       *dir = '\0';
+        } while (dir);
+
+       g_free (path);
+       if (realm)
+               return g_hash_table_lookup (host->auths, realm);
+       else
+               return NULL;
+}
+
+static void
+invalidate_auth (SoupSessionHost *host, SoupAuth *auth)
+{
+       char *realm;
+       gpointer key, value;
+
+       /* Try to just clean up the auth without removing it. */
+       if (soup_auth_invalidate (auth))
+               return;
+
+       /* Nope, need to remove it completely */
+       realm = g_strdup_printf ("%s:%s",
+                                soup_auth_get_scheme_name (auth),
+                                soup_auth_get_realm (auth));
+
+       if (g_hash_table_lookup_extended (host->auths, realm, &key, &value) &&
+           auth == (SoupAuth *)value) {
+               g_hash_table_remove (host->auths, realm);
+               g_free (key);
+               g_object_unref (auth);
+       }
+       g_free (realm);
+}
+
+static gboolean
+authenticate_auth (SoupAuth *auth, SoupMessage *msg)
+{
+       const SoupUri *uri = soup_message_get_uri (msg);
+
+       if (!uri->user && soup_auth_fn) {
+               (*soup_auth_fn) (soup_auth_get_scheme_name (auth),
+                                (SoupUri *) uri,
+                                soup_auth_get_realm (auth), 
+                                soup_auth_fn_user_data);
+       }
+
+       if (!uri->user)
+               return FALSE;
+
+       soup_auth_authenticate (auth, uri->user, uri->passwd);
+       return TRUE;
+}
+
+static gboolean
+update_auth_internal (SoupSession *session, SoupMessage *msg,
+                     const GSList *headers, gboolean proxy,
+                     gboolean prior_auth_failed)
+{
+       SoupSessionHost *host;
+       SoupAuth *new_auth, *prior_auth, *old_auth;
+       gpointer old_path, old_realm;
+       const SoupUri *msg_uri;
+       const char *path;
+       char *realm;
+       GSList *pspace, *p;
+
+       host = get_host_for_message (session, msg);
+       g_return_val_if_fail (host != NULL, FALSE);
+
+       /* Try to construct a new auth from the headers; if we can't,
+        * there's no way we'll be able to authenticate.
+        */
+       msg_uri = soup_message_get_uri (msg);
+       new_auth = soup_auth_new_from_header_list (headers, msg_uri->authmech);
+       if (!new_auth)
+               return FALSE;
+
+       /* See if this auth is the same auth we used last time */
+       prior_auth = lookup_auth (session, msg, proxy);
+       if (prior_auth &&
+           G_OBJECT_TYPE (prior_auth) == G_OBJECT_TYPE (new_auth) &&
+           !strcmp (soup_auth_get_realm (prior_auth),
+                    soup_auth_get_realm (new_auth))) {
+               g_object_unref (new_auth);
+               if (prior_auth_failed) {
+                       /* The server didn't like the username/password
+                        * we provided before.
+                        */
+                       invalidate_auth (host, prior_auth);
+                       return FALSE;
+               } else {
+                       /* The user is trying to preauthenticate using
+                        * information we already have, so there's nothing
+                        * that needs to be done.
+                        */
+                       return TRUE;
+               }
+       }
+
+       if (!host->auth_realms) {
+               host->auth_realms = g_hash_table_new (g_str_hash, g_str_equal);
+               host->auths = g_hash_table_new (g_str_hash, g_str_equal);
+       }
+
+       /* Record where this auth realm is used */
+       realm = g_strdup_printf ("%s:%s",
+                                soup_auth_get_scheme_name (new_auth),
+                                soup_auth_get_realm (new_auth));
+       pspace = soup_auth_get_protection_space (new_auth, msg_uri);
+       for (p = pspace; p; p = p->next) {
+               path = p->data;
+               if (g_hash_table_lookup_extended (host->auth_realms, path,
+                                                 &old_path, &old_realm)) {
+                       g_hash_table_remove (host->auth_realms, old_path);
+                       g_free (old_path);
+                       g_free (old_realm);
+               }
+
+               g_hash_table_insert (host->auth_realms,
+                                    g_strdup (path), g_strdup (realm));
+       }
+       soup_auth_free_protection_space (new_auth, pspace);
+
+       /* Now, make sure the auth is recorded. (If there's a
+        * pre-existing auth, we keep that rather than the new one,
+        * since the old one might already be authenticated.)
+        */
+       old_auth = g_hash_table_lookup (host->auths, realm);
+       if (old_auth) {
+               g_free (realm);
+               g_object_unref (new_auth);
+               new_auth = old_auth;
+       } else 
+               g_hash_table_insert (host->auths, realm, new_auth);
+
+       /* Try to authenticate if needed. */
+       if (!soup_auth_is_authenticated (new_auth))
+               return authenticate_auth (new_auth, msg);
+
+       return TRUE;
+}
 
 static void
 authorize_handler (SoupMessage *msg, gpointer user_data)
 {
        SoupSession *session = user_data;
-       SoupContext *ctx;
-
-       if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED)
-               ctx = soup_get_proxy ();
-       else
-               ctx = msg->priv->context;
+       const GSList *headers;
+       gboolean proxy;
+
+       if (msg->status_code == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
+               headers = soup_message_get_header_list (msg->response_headers,
+                                                       "Proxy-Authenticate");
+               proxy = TRUE;
+       } else {
+               headers = soup_message_get_header_list (msg->response_headers,
+                                                       "WWW-Authenticate");
+               proxy = FALSE;
+       }
 
-       if (soup_context_update_auth (ctx, msg))
+       if (update_auth_internal (session, msg, headers, proxy, TRUE))
                soup_session_requeue_message (session, msg);
 }
 
@@ -102,7 +381,6 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
        const char *new_loc;
        const SoupUri *old_uri;
        SoupUri *new_uri;
-       SoupContext *new_ctx;
 
        new_loc = soup_message_get_header (msg->response_headers, "Location");
        if (!new_loc)
@@ -120,13 +398,8 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
                                   old_uri->passwd,
                                   old_uri->authmech);
 
-       new_ctx = soup_context_from_uri (new_uri);
+       soup_message_set_uri (msg, new_uri);
        soup_uri_free (new_uri);
-       if (!new_ctx)
-               goto INVALID_REDIRECT;
-
-       soup_message_set_context (msg, new_ctx);
-       g_object_unref (new_ctx);
 
        soup_session_requeue_message (session, msg);
        return;
@@ -157,63 +430,247 @@ final_finished (SoupMessage *req, gpointer session)
 }
 
 static void
-start_request (SoupConnection *conn, SoupMessage *req)
+add_auth (SoupSession *session, SoupMessage *msg, gboolean proxy)
+{
+       const char *header = proxy ? "Proxy-Authorization" : "Authorization";
+       SoupAuth *auth;
+       char *token;
+
+       soup_message_remove_header (msg->request_headers, header);
+
+       auth = lookup_auth (session, msg, proxy);
+       if (!auth)
+               return;
+       if (!soup_auth_is_authenticated (auth) &&
+           !authenticate_auth (auth, msg))
+               return;
+
+       token = soup_auth_get_authorization (auth, msg);
+       if (token) {
+               soup_message_add_header (msg->request_headers, header, token);
+               g_free (token);
+       }
+}
+
+static void
+send_request (SoupSession *session, SoupMessage *req, SoupConnection *conn)
 {
        req->priv->status = SOUP_MESSAGE_STATUS_RUNNING;
+
+       add_auth (session, req, FALSE);
+       if (session->priv->proxy_uri)
+               add_auth (session, req, TRUE);
        soup_connection_send_request (conn, req);
 }
 
 static void
-got_connection (SoupContext *ctx, guint status,
-               SoupConnection *conn, gpointer user_data)
+find_oldest_connection (gpointer key, gpointer host, gpointer data)
 {
-       SoupMessage *req = user_data;
+       SoupConnection *conn = key, **oldest = data;
 
-       req->priv->connect_tag = NULL;
-       soup_message_set_connection (req, conn);
+       if (!oldest || (soup_connection_last_used (conn) <
+                       soup_connection_last_used (*oldest)))
+               *oldest = conn;
+}
 
-       if (status != SOUP_STATUS_OK) {
-               soup_message_set_status (req, status);
-               soup_message_finished (req);
+static gboolean
+try_prune_connection (SoupSession *session)
+{
+       SoupConnection *oldest = NULL;
+
+       g_hash_table_foreach (session->priv->conns, find_oldest_connection,
+                             &oldest);
+       if (oldest) {
+               soup_connection_disconnect (oldest);
+               g_object_unref (oldest);
+               return TRUE;
+       } else
+               return FALSE;
+}
+
+static void connection_closed (SoupConnection *conn, SoupSession *session);
+
+static void
+cleanup_connection (SoupSession *session, SoupConnection *conn)
+{
+       SoupSessionHost *host =
+               g_hash_table_lookup (session->priv->conns, conn);
+
+       g_return_if_fail (host != NULL);
+
+       g_hash_table_remove (session->priv->conns, conn);
+       g_signal_handlers_disconnect_by_func (conn, connection_closed, session);
+       session->priv->num_conns--;
+
+       host->connections = g_slist_remove (host->connections, conn);
+       host->num_conns--;
+}
+
+static void
+connection_closed (SoupConnection *conn, SoupSession *session)
+{
+       cleanup_connection (session, conn);
+
+       /* Run the queue in case anyone was waiting for a connection
+        * to be closed.
+        */
+       run_queue (session, FALSE);
+}
+
+static void
+got_connection (SoupConnection *conn, guint status, gpointer user_data)
+{
+       SoupSession *session = user_data;
+       SoupSessionHost *host = g_hash_table_lookup (session->priv->conns, conn);
+
+       g_return_if_fail (host != NULL);
+
+       if (status == SOUP_STATUS_OK) {
+               host->connections = g_slist_prepend (host->connections, conn);
+               run_queue (session, FALSE);
+               return;
+       }
+
+       /* We failed */
+       cleanup_connection (session, conn);
+       g_object_unref (conn);
+
+       if (host->connections) {
+               /* Something went wrong this time, but we have at
+                * least one open connection to this host. So just
+                * leave the message in the queue so it can use that
+                * connection once it's free.
+                */
                return;
        }
 
-       start_request (conn, req);
+       /* Flush any queued messages for this host */
+       host->error = status;
+       run_queue (session, FALSE);
+
+       if (status != SOUP_STATUS_CANT_RESOLVE &&
+           status != SOUP_STATUS_CANT_RESOLVE_PROXY) {
+               /* If the error was "can't resolve", then it's not likely
+                * to improve. But if it was something else, it may have
+                * been transient, so we clear the error so the user can
+                * try again later.
+                */
+               host->error = 0;
+       }
 }
 
 static gboolean
-idle_run_queue (gpointer user_data)
+run_queue (SoupSession *session, gboolean try_pruning)
 {
-       SoupSession *session = user_data;
        SoupMessageQueueIter iter;
-       SoupMessage *req;
+       SoupMessage *msg;
        SoupConnection *conn;
+       SoupSessionHost *host;
+       gboolean skipped_any = FALSE, started_any = FALSE;
+       GSList *conns;
 
-       session->priv->queue_idle_tag = 0;
+       /* FIXME: prefer CONNECTING messages */
+
+ try_again:
+       for (msg = soup_message_queue_first (session->priv->queue, &iter); msg; msg = soup_message_queue_next (session->priv->queue, &iter)) {
+
+               if (!SOUP_MESSAGE_IS_STARTING (msg))
+                       continue;
+
+               host = get_host_for_message (session, msg);
+
+               /* If the hostname is known to be bad, fail right away */
+               if (host->error) {
+                       soup_message_set_status (msg, host->error);
+                       soup_message_finished (msg);
+               }
+
+               /* If there is an idle connection, use it */
+               for (conns = host->connections; conns; conns = conns->next) {
+                       if (!soup_connection_is_in_use (conns->data))
+                               break;
+               }
+               if (conns) {
+                       send_request (session, msg, conns->data);
+                       started_any = TRUE;
+                       continue;
+               }
 
-       for (req = soup_message_queue_first (session->priv->queue, &iter); req;
-            req = soup_message_queue_next (session->priv->queue, &iter)) {
+               if (msg->priv->status == SOUP_MESSAGE_STATUS_CONNECTING) {
+                       /* We already started a connection for this
+                        * message, so don't start another one.
+                        */
+                       continue;
+               }
 
-               if (req->priv->status != SOUP_MESSAGE_STATUS_QUEUED)
+               /* If we have the max number of per-host connections
+                * or total connections open, we'll have to wait.
+                */
+               if (host->num_conns >= session->priv->max_conns_per_host)
+                       continue;
+               else if (session->priv->num_conns >= session->priv->max_conns) {
+                       /* In this case, closing an idle connection
+                        * somewhere else would let us open one here.
+                        */
+                       skipped_any = TRUE;
                        continue;
+               }
 
-               conn = soup_message_get_connection (req);
-               if (conn && soup_connection_is_connected (conn)) {
-                       start_request (conn, req);
+               /* Otherwise, open a new connection */
+               if (session->priv->proxy_uri &&
+                   host->root_uri->protocol == SOUP_PROTOCOL_HTTPS) {
+                       conn = soup_connection_new_tunnel (
+                               session->priv->proxy_uri, host->root_uri,
+                               got_connection, session);
+               } else if (session->priv->proxy_uri) {
+                       conn = soup_connection_new_proxy (
+                               session->priv->proxy_uri,
+                               got_connection, session);
                } else {
-                       gpointer connect_tag;
+                       conn = soup_connection_new (host->root_uri,
+                                                   got_connection, session);
+               }
+
+               g_signal_connect (conn, "disconnected",
+                                 G_CALLBACK (connection_closed), session);
+               g_hash_table_insert (session->priv->conns, conn, host);
+               session->priv->num_conns++;
 
-                       req->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
-                       connect_tag = 
-                               soup_context_get_connection (
-                                       req->priv->context,
-                                       got_connection, req);
+               /* Increment the host's connection count, but don't add
+                * this connection to the list yet, since it's not ready.
+                */
+               host->num_conns++;
 
-                       if (connect_tag)
-                               req->priv->connect_tag = connect_tag;
+               /* Mark the request as connecting, so we don't try to
+                * open another new connection for it next time around.
+                */
+               msg->priv->status = SOUP_MESSAGE_STATUS_CONNECTING;
+
+               started_any = TRUE;
+       }
+
+       if (try_pruning && skipped_any && !started_any) {
+               /* We didn't manage to start any message, but there is
+                * at least one message in the queue that could be
+                * sent if we pruned an idle connection from some
+                * other server.
+                */
+               if (try_prune_connection (session)) {
+                       try_pruning = FALSE;
+                       goto try_again;
                }
        }
 
+       return started_any;
+}
+
+static gboolean
+idle_run_queue (gpointer user_data)
+{
+       SoupSession *session = user_data;
+
+       session->priv->queue_idle_tag = 0;
+       run_queue (session, TRUE);
        return FALSE;
 }
 
index 1a61d86..3a07b29 100644 (file)
@@ -30,7 +30,11 @@ typedef struct {
 
 GType soup_session_get_type (void);
 
-SoupSession    *soup_session_new             (void);
+SoupSession    *soup_session_new_default     (void);
+SoupSession    *soup_session_new_with_proxy  (const SoupUri         *proxy_uri);
+SoupSession    *soup_session_new_full        (const SoupUri         *proxy_uri,
+                                             guint                  max_conns,
+                                             guint                  max_per_host);
 
 void            soup_session_queue_message   (SoupSession           *session,
                                              SoupMessage           *req,
index 08d9c4f..a2f4774 100644 (file)
@@ -10,7 +10,6 @@
 extern "C" {
 #endif
 
-#include <libsoup/soup-context.h>
 #include <libsoup/soup-message.h>
 #include <libsoup/soup-misc.h>
 #include <libsoup/soup-session.h>
index 8fe73b5..4448845 100644 (file)
@@ -161,14 +161,14 @@ static int
 identify_auth (SoupMessage *msg)
 {
        SoupAuth *auth;
-       char *header;
+       const char *header;
        int num;
 
-       auth = soup_context_lookup_auth (msg->priv->context, msg);
-       if (!auth || !soup_auth_is_authenticated (auth))
+       header = soup_message_get_header (msg->request_headers,
+                                         "Authorization");
+       if (!header)
                return 0;
 
-       header = soup_auth_get_authorization (auth, msg);
        if (!g_ascii_strncasecmp (header, "Basic ", 6)) {
                char *token;
                int len;
@@ -186,7 +186,6 @@ identify_auth (SoupMessage *msg)
                        num = 0;
        }
 
-       g_free (header);
        return num;
 }