New, split out from soup-context and made into a GObject.
authorDan Winship <danw@src.gnome.org>
Thu, 14 Aug 2003 15:02:32 +0000 (15:02 +0000)
committerDan Winship <danw@src.gnome.org>
Thu, 14 Aug 2003 15:02:32 +0000 (15:02 +0000)
* libsoup/soup-connection.c: New, split out from soup-context and
made into a GObject.
(soup_connection_disconnect): Disconnects the connection and emits
a signal. (Replaces the old "keep_alive" flag.)
(soup_connection_is_connected): Checks if the connection is still
connected
(connection_died): Just disconnect, rather than freeing the
connection. This way if anyone else is still referencing it they
won't end up with an invalid pointer.

* libsoup/soup-context.c: Make this a GObject, remove all the
SoupConnection code. Add an "ntlm_auths" field to SoupHost so that
SoupContext can keep track of connection auth stuff there without
SoupConnection needing to care. Various other updates.

* libsoup/soup-private.h: Remove SoupContext and SoupConnection
definitions.

* libsoup/*.c, tests/get.c: Update for context/connection changes

* libsoup/soup-socks.c (soup_connect_socks_proxy): Change the
definition to deal with the fact that there's no
soup_connection_get_context any more.

* libsoup/soup-queue.c (soup_queue_read_headers_cb): Don't deal
with connection persistence here.
(soup_queue_read_done_cb): Do it here instead. Disconnect the
connection when appropriate.
(proxy_connect, proxy_https_connect, proxy_https_connect_cb):
Reference-count the connection properly. (I think.)

* libsoup/soup-marshal.list: New, for SoupConnection's
"disconnected" signal.

* libsoup/Makefile.am: add rules to build soup-marshal.[ch]

* configure.in: Use AM_PATH_GLIB_2 rather than pkg-config, so that
GLIB_GENMARSHAL gets set too.

18 files changed:
ChangeLog
configure.in
libsoup/.cvsignore
libsoup/Makefile.am
libsoup/soup-connection.c [new file with mode: 0644]
libsoup/soup-connection.h [new file with mode: 0644]
libsoup/soup-context.c
libsoup/soup-context.h
libsoup/soup-marshal.list [new file with mode: 0644]
libsoup/soup-message.c
libsoup/soup-message.h
libsoup/soup-misc.c
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-server.c
libsoup/soup-socks.c
libsoup/soup-socks.h
tests/get.c

index 54f26e1..34f1250 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,46 @@
 2003-08-14  Dan Winship  <danw@ximian.com>
 
+       * libsoup/soup-connection.c: New, split out from soup-context and
+       made into a GObject.
+       (soup_connection_disconnect): Disconnects the connection and emits
+       a signal. (Replaces the old "keep_alive" flag.)
+       (soup_connection_is_connected): Checks if the connection is still
+       connected
+       (connection_died): Just disconnect, rather than freeing the
+       connection. This way if anyone else is still referencing it they
+       won't end up with an invalid pointer.
+
+       * libsoup/soup-context.c: Make this a GObject, remove all the
+       SoupConnection code. Add an "ntlm_auths" field to SoupHost so that
+       SoupContext can keep track of connection auth stuff there without
+       SoupConnection needing to care. Various other updates.
+
+       * libsoup/soup-private.h: Remove SoupContext and SoupConnection
+       definitions.
+
+       * libsoup/*.c, tests/get.c: Update for context/connection changes
+
+       * libsoup/soup-socks.c (soup_connect_socks_proxy): Change the
+       definition to deal with the fact that there's no
+       soup_connection_get_context any more.
+
+       * libsoup/soup-queue.c (soup_queue_read_headers_cb): Don't deal
+       with connection persistence here.
+       (soup_queue_read_done_cb): Do it here instead. Disconnect the
+       connection when appropriate.
+       (proxy_connect, proxy_https_connect, proxy_https_connect_cb):
+       Reference-count the connection properly. (I think.)
+
+       * libsoup/soup-marshal.list: New, for SoupConnection's
+       "disconnected" signal.
+
+       * libsoup/Makefile.am: add rules to build soup-marshal.[ch]
+
+       * configure.in: Use AM_PATH_GLIB_2 rather than pkg-config, so that
+       GLIB_GENMARSHAL gets set too.
+
+2003-08-14  Dan Winship  <danw@ximian.com>
+
        * libsoup/soup-error.c: Fix a spelling mistake.
 
        * libsoup/*.c: Fix use of @/%/#/() in gtk-doc comments
index fd17602..8d01009 100644 (file)
@@ -72,9 +72,7 @@ dnl ***********************
 dnl *** Checks for glib ***
 dnl ***********************
 
-PKG_CHECK_MODULES(GLIB, glib-2.0 gobject-2.0)
-AC_SUBST(GLIB_CFLAGS)
-AC_SUBST(GLIB_LIBS)
+AM_PATH_GLIB_2_0(2.0.0,,,gobject)
 
 dnl *********************************
 dnl *** Networking library checks ***
index 0be24a0..513e154 100644 (file)
@@ -4,4 +4,5 @@
 .libs
 Makefile
 Makefile.in
-libsoup-ssl-proxy
+soup-marshal.c
+soup-marshal.h
index cf17e6f..c1d4a2a 100644 (file)
@@ -9,11 +9,28 @@ INCLUDES =                            \
        $(GLIB_CFLAGS)                  \
        $(GNUTLS_CFLAGS)
 
+MARSHAL_GENERATED = soup-marshal.c soup-marshal.h
+
+soup-marshal.h: soup-marshal.list
+       ( @GLIB_GENMARSHAL@ --prefix=soup_marshal soup-marshal.list --header > soup-marshal.tmp \
+       && mv soup-marshal.tmp soup-marshal.h ) \
+       || ( rm -f soup-marshal.tmp && exit 1 )
+
+soup-marshal.c: soup-marshal.h
+       ( (echo '#include "soup-marshal.h"'; @GLIB_GENMARSHAL@ --prefix=soup_marshal soup-marshal.list --body) > soup-marshal.tmp \
+       && mv soup-marshal.tmp soup-marshal.c ) \
+       || ( rm -f soup-marshal.tmp && exit 1 )
+
+BUILT_SOURCES = $(MARSHAL_GENERATED)
+
+CLEANFILES = $(MARSHAL_GENERATED)
+
 libsoupincludedir = $(includedir)/libsoup-2.2/libsoup
 
 libsoupinclude_HEADERS =       \
        soup.h                  \
        soup-address.h          \
+       soup-connection.h       \
        soup-context.h          \
        soup-error.h            \
        soup-headers.h          \
@@ -35,6 +52,7 @@ libsoup_2_2_la_LIBADD =               \
        $(GNUTLS_LIBS)
 
 libsoup_2_2_la_SOURCES =       \
+       $(MARSHAL_GENERATED)    \
        md5-utils.h             \
        md5-utils.c             \
        soup-address.c          \
@@ -46,6 +64,7 @@ libsoup_2_2_la_SOURCES =      \
        soup-auth-digest.c      \
        soup-auth-ntlm.h        \
        soup-auth-ntlm.c        \
+       soup-connection.c       \
        soup-context.c          \
        soup-dns.h              \
        soup-dns.c              \
@@ -69,3 +88,5 @@ libsoup_2_2_la_SOURCES =      \
        soup-transfer.h         \
        soup-transfer.c         \
        soup-uri.c
+
+EXTRA_DIST= soup-marshal.list
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
new file mode 100644 (file)
index 0000000..571eafe
--- /dev/null
@@ -0,0 +1,304 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-connection.c: A single HTTP/HTTPS connection
+ *
+ * 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-connection.h"
+#include "soup-private.h"
+#include "soup-marshal.h"
+#include "soup-misc.h"
+#include "soup-socket.h"
+#include "soup-ssl.h"
+
+struct SoupConnectionPrivate {
+       SoupSocket *socket;
+       GIOChannel *raw_chan, *cooked_chan;
+       gboolean    in_use, new;
+       time_t      last_used;
+       guint       death_tag;
+};
+
+#define PARENT_TYPE G_TYPE_OBJECT
+static GObjectClass *parent_class;
+
+enum {
+       DISCONNECTED,
+       LAST_SIGNAL
+};
+
+guint signals[LAST_SIGNAL] = { 0 };
+static void
+init (GObject *object)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+
+       conn->priv = g_new0 (SoupConnectionPrivate, 1);
+       conn->priv->in_use = FALSE;
+       conn->priv->new = TRUE;
+}
+
+static void
+finalize (GObject *object)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+
+       soup_connection_disconnect (conn);
+       g_free (conn->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;
+
+       /* signals */
+       signals[DISCONNECTED] =
+               g_signal_new ("disconnected",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_FIRST,
+                             G_STRUCT_OFFSET (SoupConnectionClass, disconnected),
+                             NULL, NULL,
+                             soup_marshal_NONE__NONE,
+                             G_TYPE_NONE, 0);
+}
+
+SOUP_MAKE_TYPE (soup_connection, SoupConnection, class_init, init, PARENT_TYPE)
+
+
+/**
+ * soup_connection_new:
+ * @sock: a #SoupSocket
+ *
+ * Creates a new #SoupConnection, wrapped around @sock.
+ *
+ * Return value: the new #SoupConnection
+ **/
+SoupConnection *
+soup_connection_new (SoupSocket *sock)
+{
+       SoupConnection *conn;
+       int yes = 1, flags = 0, fd;
+
+       conn = g_object_new (SOUP_TYPE_CONNECTION, NULL);
+       conn->priv->socket = g_object_ref (sock);
+
+       conn->priv->raw_chan = soup_socket_get_iochannel (sock);
+       fd = g_io_channel_unix_get_fd (conn->priv->raw_chan);
+       setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof (yes));
+       flags = fcntl (fd, F_GETFL, 0);
+       fcntl (fd, F_SETFL, flags | O_NONBLOCK);
+
+       return conn;
+}
+
+/**
+ * soup_connection_start_ssl:
+ * @conn: a connection
+ *
+ * Negotiates SSL on @conn
+ **/
+void
+soup_connection_start_ssl (SoupConnection *conn)
+{
+       g_return_if_fail (SOUP_IS_CONNECTION (conn));
+       g_return_if_fail (conn->priv->socket != NULL);
+
+       if (conn->priv->cooked_chan)
+               g_io_channel_unref (conn->priv->cooked_chan);
+       conn->priv->cooked_chan =
+               soup_ssl_get_iochannel (conn->priv->raw_chan);
+}
+
+/**
+ * soup_connection_disconnect:
+ * @conn: a connection
+ *
+ * Disconnects @conn's socket and emits a %disconnected signal.
+ * After calling this, @conn will be essentially useless.
+ **/
+void
+soup_connection_disconnect (SoupConnection *conn)
+{
+       g_return_if_fail (SOUP_IS_CONNECTION (conn));
+
+       if (conn->priv->death_tag) {
+               g_source_remove (conn->priv->death_tag);
+               conn->priv->death_tag = 0;
+       }
+
+       if (conn->priv->raw_chan) {
+               g_io_channel_unref (conn->priv->raw_chan);
+               conn->priv->raw_chan = NULL;
+       }
+       if (conn->priv->cooked_chan) {
+               g_io_channel_unref (conn->priv->cooked_chan);
+               conn->priv->cooked_chan = NULL;
+       }
+
+       if (conn->priv->socket) {
+               g_object_unref (conn->priv->socket);
+               conn->priv->socket = NULL;
+               g_signal_emit (conn, signals[DISCONNECTED], 0);
+       }
+}
+
+gboolean
+soup_connection_is_connected (SoupConnection *conn)
+{
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+
+       return conn->priv->socket != NULL;
+}
+
+/**
+ * soup_connection_get_iochannel:
+ * @conn: a #SoupConnection.
+ *
+ * Returns a #GIOChannel used for IO operations on the network
+ * connection represented by @conn.
+ *
+ * Return value: a pointer to the #GIOChannel used for IO on @conn,
+ * which the caller must unref.
+ */
+GIOChannel *
+soup_connection_get_iochannel (SoupConnection *conn)
+{
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
+
+       if (!conn->priv->socket)
+               return NULL;
+
+       if (!conn->priv->cooked_chan) {
+               conn->priv->cooked_chan = conn->priv->raw_chan;
+               g_io_channel_ref (conn->priv->cooked_chan);
+       }
+
+       g_io_channel_ref (conn->priv->cooked_chan);
+       return conn->priv->cooked_chan;
+}
+
+
+static gboolean
+connection_died (GIOChannel   *iochannel,
+                 GIOCondition  condition,
+                 gpointer      conn)
+{
+       soup_connection_disconnect (conn);
+       return FALSE;
+}
+
+/**
+ * soup_connection_set_in_use:
+ * @conn: a connection
+ * @in_use: whether or not @conn is in_use
+ *
+ * Marks @conn as being either in use or not. If @in_use is %FALSE,
+ * @conn's last-used time is updated.
+ **/
+void
+soup_connection_set_in_use (SoupConnection *conn, gboolean in_use)
+{
+       g_return_if_fail (SOUP_IS_CONNECTION (conn));
+
+       if (!in_use)
+               conn->priv->last_used = time (NULL);
+
+       if (in_use == conn->priv->in_use)
+               return;
+
+       conn->priv->in_use = in_use;
+       if (!conn->priv->in_use) {
+               if (!conn->priv->cooked_chan)
+                       soup_connection_get_iochannel (conn);
+               conn->priv->death_tag = 
+                       g_io_add_watch (conn->priv->cooked_chan,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+                                       connection_died,
+                                       conn);
+       } else if (conn->priv->death_tag) {
+               g_source_remove (conn->priv->death_tag);
+               conn->priv->death_tag = 0;
+       }
+}
+
+/**
+ * soup_connection_is_in_use:
+ * @conn: a connection
+ *
+ * Return value: whether or not @conn is being used.
+ **/
+gboolean
+soup_connection_is_in_use (SoupConnection *conn)
+{
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+
+       return conn->priv->in_use;
+}
+
+/**
+ * soup_connection_last_used:
+ * @conn: a #SoupConnection.
+ *
+ * Return value: the last time soup_connection_mark_used() was called
+ * on @conn, or 0 if @conn has not been used yet.
+ */
+time_t
+soup_connection_last_used (SoupConnection *conn)
+{
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+
+       return conn->priv->last_used;
+}
+
+/**
+ * soup_connection_is_new:
+ * @conn: a connection
+ *
+ * Return value: whether or not @conn is "new". (That is, it has not
+ * yet completed a whole HTTP transaction.)
+ **/
+gboolean
+soup_connection_is_new (SoupConnection *conn)
+{
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+
+       return conn->priv->new;
+}
+
+/**
+ * soup_connection_mark_old:
+ * @conn: a #SoupConnection.
+ *
+ * Marks @conn as being no longer "new".
+ * FIXME: some day, this should happen automatically.
+ */
+void
+soup_connection_mark_old (SoupConnection *conn)
+{
+       g_return_if_fail (SOUP_IS_CONNECTION (conn));
+
+       conn->priv->new = FALSE;
+}
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
new file mode 100644 (file)
index 0000000..70d4d4c
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef SOUP_CONNECTION_H
+#define SOUP_CONNECTION_H 1
+
+#include <time.h>
+
+#include <glib-object.h>
+#include <libsoup/soup-socket.h>
+
+#define SOUP_TYPE_CONNECTION            (soup_connection_get_type ())
+#define SOUP_CONNECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONNECTION, SoupConnection))
+#define SOUP_CONNECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONNECTION, SoupConnectionClass))
+#define SOUP_IS_CONNECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONNECTION))
+#define SOUP_IS_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONNECTION))
+#define SOUP_CONNECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONNECTION, SoupConnectionClass))
+
+typedef struct SoupConnectionPrivate SoupConnectionPrivate;
+
+typedef struct {
+       GObject parent;
+
+       SoupConnectionPrivate *priv;
+} SoupConnection;
+
+typedef struct {
+       GObjectClass parent_class;
+
+       /* signals */
+       void (*disconnected) (SoupConnection *);
+} SoupConnectionClass;
+
+GType soup_connection_get_type (void);
+
+
+SoupConnection *soup_connection_new            (SoupSocket     *sock);
+void            soup_connection_start_ssl      (SoupConnection *conn);
+void            soup_connection_disconnect     (SoupConnection *conn);
+gboolean        soup_connection_is_connected   (SoupConnection *conn);
+
+GIOChannel     *soup_connection_get_iochannel  (SoupConnection *conn);
+
+void            soup_connection_set_in_use     (SoupConnection *conn, 
+                                               gboolean        in_use);
+gboolean        soup_connection_is_in_use      (SoupConnection *conn);
+time_t          soup_connection_last_used      (SoupConnection *conn);
+
+gboolean        soup_connection_is_new         (SoupConnection *conn);
+void            soup_connection_mark_old       (SoupConnection *conn);
+
+#endif /*SOUP_CONNECTION_H*/
index c732b8b..14f4cf9 100644 (file)
@@ -2,10 +2,7 @@
 /*
  * soup-context.c: Asyncronous Callback-based HTTP Request Queue.
  *
- * Authors:
- *      Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
+ * Copyright (C) 2000-2003, Ximian, Inc.
  */
 
 #ifdef HAVE_CONFIG_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;
+}
 
-static gint connection_count = 0;
+SOUP_MAKE_TYPE (soup_context, SoupContext, class_init, init, PARENT_TYPE)
 
-static guint most_recently_used_id = 0;
 
 /**
  * soup_context_get:
@@ -50,20 +132,21 @@ static guint most_recently_used_id = 0;
  * Return value: a #SoupContext representing @uri.
  */
 SoupContext *
-soup_context_get (const gchar *uri)
+soup_context_get (const char *uri)
 {
        SoupUri *suri;
-       SoupContext *con;
+       SoupContext *ctx;
 
        g_return_val_if_fail (uri != NULL, NULL);
 
        suri = soup_uri_new (uri);
-       if (!suri) return NULL;
+       if (!suri)
+               return NULL;
 
-       con = soup_context_from_uri (suri);
+       ctx = soup_context_from_uri (suri);
        soup_uri_free (suri);
 
-       return con;
+       return ctx;
 }
 
 /**
@@ -145,15 +228,15 @@ SoupContext *
 soup_context_from_uri (SoupUri *suri)
 {
        SoupHost *serv = NULL;
-       SoupContext *ret = NULL;
+       SoupContext *ctx = NULL;
 
        g_return_val_if_fail (suri != NULL, NULL);
        g_return_val_if_fail (suri->protocol != 0, NULL);
 
-       if (!soup_hosts)
+       if (!soup_hosts) {
                soup_hosts = g_hash_table_new (soup_str_case_hash,
                                               soup_str_case_equal);
-       else
+       else
                serv = g_hash_table_lookup (soup_hosts, suri->host);
 
        if (!serv) {
@@ -162,158 +245,41 @@ soup_context_from_uri (SoupUri *suri)
                g_hash_table_insert (soup_hosts, serv->host, serv);
        }
 
-       if (!serv->contexts)
+       if (!serv->contexts) {
                serv->contexts = g_hash_table_new (soup_context_uri_hash,
                                                   soup_context_uri_equal);
-       else
-               ret = g_hash_table_lookup (serv->contexts, suri);
+       else
+               ctx = g_hash_table_lookup (serv->contexts, suri);
 
-       if (!ret) {
-               ret = g_new0 (SoupContext, 1);
-               ret->server = serv;
-               ret->uri = soup_uri_copy (suri);
-               ret->refcnt = 0;
+       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, ret->uri, ret);
+               g_hash_table_insert (serv->contexts, ctx->priv->uri, ctx);
        }
 
-       soup_context_ref (ret);
-
-       return ret;
-}
-
-/**
- * soup_context_ref:
- * @ctx: a #SoupContext.
- *
- * Adds a reference to @ctx.
- */
-void
-soup_context_ref (SoupContext *ctx)
-{
-       g_return_if_fail (ctx != NULL);
-
-       ctx->refcnt++;
+       return g_object_ref (ctx);
 }
 
 static void
-free_path (gpointer path, gpointer realm, gpointer unused)
-{
-       g_free (path);
-       g_free (realm);
-}
-
-static void
-free_auth (gpointer realm, gpointer auth, gpointer unused)
-{
-       g_free (realm);
-       g_object_unref (auth);
-}
-
-/**
- * soup_context_unref:
- * @ctx: a #SoupContext.
- *
- * Decrement the reference count on @ctx. If the reference count
- * reaches zero, the #SoupContext is freed. If this is the last
- * context for a given server address, any open connections are
- * closed.
- */
-void
-soup_context_unref (SoupContext *ctx)
+connection_disconnected (SoupConnection *conn, gpointer user_data)
 {
-       g_return_if_fail (ctx != NULL);
-
-       --ctx->refcnt;
-
-       if (ctx->refcnt == 0) {
-               SoupHost *serv = ctx->server;
-
-               g_hash_table_remove (serv->contexts, ctx->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);
+       SoupHost *server = user_data;
+       SoupAuth *auth;
 
-                       /* 
-                        * 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, NULL);
-                               g_hash_table_destroy (serv->auths);
-                       }
-
-                       g_hash_table_destroy (serv->contexts);
-                       g_free (serv->host);
-                       g_free (serv);
+       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);
                }
-
-               soup_uri_free (ctx->uri);
-               g_free (ctx);
        }
-}
-
-static void
-connection_free (SoupConnection *conn)
-{
-       g_return_if_fail (conn != NULL);
-
-       conn->server->connections =
-               g_slist_remove (conn->server->connections, conn);
-
-       if (conn->auth)
-               g_object_unref (conn->auth);
-
-       g_io_channel_unref (conn->channel);
-       soup_context_unref (conn->context);
-       g_object_unref (conn->socket);
-       if (conn->death_tag)
-               g_source_remove (conn->death_tag);
-       g_free (conn);
 
+       server->connections = g_slist_remove (server->connections, conn);
        connection_count--;
 }
 
-static gboolean 
-connection_death (GIOChannel*     iochannel,
-                 GIOCondition    condition,
-                 SoupConnection *conn)
-{
-       connection_free (conn);
-       return FALSE;
-}
-
-static void
-soup_connection_set_in_use (SoupConnection *conn, gboolean in_use)
-{
-       if (in_use == conn->in_use)
-               return;
-
-       conn->in_use = in_use;
-       if (!conn->in_use) {
-               GIOChannel *chan;
-
-               chan = soup_connection_get_iochannel (conn);
-               conn->death_tag = 
-                       g_io_add_watch (chan,
-                                       G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-                                       (GIOFunc) connection_death,
-                                       conn);
-               g_io_channel_unref (chan);
-       } else {
-               g_source_remove (conn->death_tag);
-               conn->death_tag = 0;
-       }
-}
-
 struct SoupConnectData {
        SoupContext           *ctx;
        SoupConnectCallbackFn  cb;
@@ -324,22 +290,22 @@ struct SoupConnectData {
 };
 
 static void
-prune_connection_foreach (gchar               *hostname,
-                         SoupHost            *serv,
-                         SoupConnection     **last)
+prune_connection_foreach (gpointer key, gpointer value, gpointer data)
 {
-       GSList *conns = serv->connections;
+       SoupHost *serv = value;
+       SoupConnection **last = data;
+       GSList *conns;
 
-       while (conns) {
+       for (conns = serv->connections; conns; conns = conns->next) {
                SoupConnection *conn = conns->data;
 
-               if (!conn->in_use) {
-                       if (*last == NULL ||
-                           (*last)->last_used_id > conn->last_used_id)
-                               *last = conn;
-               }
+               if (soup_connection_is_in_use (conn))
+                       continue;
 
-               conns = conns->next;
+               if (*last == NULL ||
+                   soup_connection_last_used (*last) >
+                   soup_connection_last_used (conn))
+                       *last = conn;
        }
 }
 
@@ -352,7 +318,8 @@ prune_least_used_connection (void)
                              (GHFunc) prune_connection_foreach, 
                              &last);
        if (last) {
-               connection_free (last);
+               soup_connection_disconnect (last);
+               g_object_unref (last);
                return TRUE;
        }
 
@@ -372,20 +339,21 @@ soup_context_connect_cb (SoupSocket              *socket,
 
        switch (status) {
        case SOUP_SOCKET_CONNECT_ERROR_NONE:
-               new_conn = g_new0 (SoupConnection, 1);
-               new_conn->server = ctx->server;
-               new_conn->socket = socket;
-               new_conn->port = ctx->uri->port;
-               new_conn->keep_alive = TRUE;
-               new_conn->in_use = TRUE;
-               new_conn->new = TRUE;
-               new_conn->last_used_id = 0;
+               new_conn = soup_connection_new (socket);
+               g_object_unref (socket);
+               if (ctx->priv->uri->protocol == SOUP_PROTOCOL_HTTPS)
+                       soup_connection_start_ssl (new_conn);
 
-               new_conn->context = ctx;
-               soup_context_ref (ctx);
+               g_signal_connect (new_conn, "disconnected",
+                                 G_CALLBACK (connection_disconnected),
+                                 ctx->priv->server);
 
-               ctx->server->connections =
-                       g_slist_prepend (ctx->server->connections, new_conn);
+               /* FIXME */
+               g_object_set_data (G_OBJECT (new_conn), "SoupContext-port",
+                                  GUINT_TO_POINTER (ctx->priv->uri->port));
+
+               ctx->priv->server->connections =
+                       g_slist_prepend (ctx->priv->server->connections, new_conn);
 
                (*data->cb) (ctx, 
                             SOUP_CONNECT_ERROR_NONE, 
@@ -407,7 +375,7 @@ soup_context_connect_cb (SoupSocket              *socket,
                 * Check if another connection exists to this server
                 * before reporting error. 
                 */
-               if (ctx->server->connections) {
+               if (ctx->priv->server->connections) {
                        data->timeout_tag =
                                g_timeout_add (
                                        150,
@@ -423,7 +391,7 @@ soup_context_connect_cb (SoupSocket              *socket,
                break;
        }
 
-       soup_context_unref (ctx);
+       g_object_unref (ctx);
        g_free (data);
 }
 
@@ -432,22 +400,17 @@ try_existing_connections (SoupContext           *ctx,
                          SoupConnectCallbackFn  cb,
                          gpointer               user_data)
 {
-       GSList *conns = ctx->server->connections;
+       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 (conn->in_use == FALSE &&
-                   conn->keep_alive == TRUE &&
-                   conn->port == (guint) ctx->uri->port) {
+               if (!soup_connection_is_in_use (conn) &&
+                   port == ctx->priv->uri->port) {
                        /* Set connection to in use */
                        soup_connection_set_in_use (conn, TRUE);
 
-                       /* Reset connection context */
-                       soup_context_ref (ctx);
-                       soup_context_unref (conn->context);
-                       conn->context = ctx;
-
                        /* Issue success callback */
                        (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, conn, user_data);
                        return TRUE;
@@ -463,7 +426,7 @@ static gboolean
 try_create_connection (struct SoupConnectData **dataptr)
 {
        struct SoupConnectData *data = *dataptr;
-       gint conn_limit = soup_get_connection_limit ();
+       int conn_limit = soup_get_connection_limit ();
        gpointer connect_tag;
 
        /* 
@@ -480,8 +443,8 @@ try_create_connection (struct SoupConnectData **dataptr)
        connection_count++;
 
        data->timeout_tag = 0;
-       connect_tag = soup_socket_connect (data->ctx->uri->host,
-                                          data->ctx->uri->port,
+       connect_tag = soup_socket_connect (data->ctx->priv->uri->host,
+                                          data->ctx->priv->uri->port,
                                           soup_context_connect_cb,
                                           data);
        /* 
@@ -502,7 +465,7 @@ retry_connect_timeout_cb (struct SoupConnectData *data)
        if (try_existing_connections (data->ctx, 
                                      data->cb, 
                                      data->user_data)) {
-               soup_context_unref (data->ctx);
+               g_object_unref (data->ctx);
                g_free (data);
                return FALSE;
        }
@@ -539,7 +502,7 @@ soup_context_get_connection (SoupContext           *ctx,
 {
        struct SoupConnectData *data;
 
-       g_return_val_if_fail (ctx != NULL, NULL);
+       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), NULL);
 
        /* Look for an existing unused connection */
        if (try_existing_connections (ctx, cb, user_data))
@@ -549,15 +512,14 @@ soup_context_get_connection (SoupContext           *ctx,
        data->cb = cb;
        data->user_data = user_data;
 
-       data->ctx = ctx;
-       soup_context_ref (ctx);
+       data->ctx = g_object_ref (ctx);
 
-       if (!try_create_connection (&data))
+       if (!try_create_connection (&data)) {
                data->timeout_tag =
                        g_timeout_add (150,
                                       (GSourceFunc) retry_connect_timeout_cb,
                                       data);
-
+       }
        return data;
 }
 
@@ -597,161 +559,11 @@ soup_context_cancel_connect (SoupConnectId tag)
 const SoupUri *
 soup_context_get_uri (SoupContext *ctx)
 {
-       g_return_val_if_fail (ctx != NULL, NULL);
-       return ctx->uri;
-}
-
-/**
- * soup_connection_release:
- * @conn: a #SoupConnection currently in use.
- *
- * Mark the connection represented by @conn as being unused. If the
- * keep-alive flag is not set on the connection, the connection is
- * closed and its resources freed, otherwise the connection is
- * returned to the unused connection pool for the server.
- */
-void
-soup_connection_release (SoupConnection *conn)
-{
-       g_return_if_fail (conn != NULL);
-
-       if (conn->keep_alive) {
-               conn->last_used_id = ++most_recently_used_id;
-               soup_connection_set_in_use (conn, FALSE);
-       } else
-               connection_free (conn);
+       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), NULL);
+       return ctx->priv->uri;
 }
 
 static void
-soup_connection_setup_socket (GIOChannel *channel)
-{
-       int yes = 1, flags = 0, fd = g_io_channel_unix_get_fd (channel);
-
-       setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
-
-       flags = fcntl(fd, F_GETFL, 0);
-       fcntl (fd, F_SETFL, flags | O_NONBLOCK);
-}
-
-/**
- * soup_connection_get_iochannel:
- * @conn: a #SoupConnection.
- *
- * Returns a #GIOChannel used for IO operations on the network
- * connection represented by @conn.
- *
- * Return value: a pointer to the #GIOChannel used for IO on @conn.
- */
-GIOChannel *
-soup_connection_get_iochannel (SoupConnection *conn)
-{
-       g_return_val_if_fail (conn != NULL, NULL);
-
-       if (!conn->channel) {
-               conn->channel = soup_socket_get_iochannel (conn->socket);
-
-               soup_connection_setup_socket (conn->channel);
-
-               if (conn->context->uri->protocol == SOUP_PROTOCOL_HTTPS) {
-                       GIOChannel *ssl_chan;
-
-                       ssl_chan = soup_ssl_get_iochannel (conn->channel);
-                       g_io_channel_unref (conn->channel);
-                       conn->channel = ssl_chan;
-               }
-       }
-
-       g_io_channel_ref (conn->channel);
-
-       return conn->channel;
-}
-
-/**
- * soup_connection_set_keep_alive:
- * @conn: a #SoupConnection.
- * @keep_alive: boolean keep-alive value.
- *
- * Sets the keep-alive flag on the #SoupConnection pointed to by
- * @conn.
- */
-void
-soup_connection_set_keep_alive (SoupConnection *conn, gboolean keep_alive)
-{
-       g_return_if_fail (conn != NULL);
-       conn->keep_alive = keep_alive;
-}
-
-/**
- * soup_connection_is_keep_alive:
- * @conn: a #SoupConnection.
- *
- * Returns the keep-alive flag for the #SoupConnection pointed to by
- * @conn. If this flag is TRUE, the connection will be returned to the
- * pool of unused connections when next soup_connection_release() is
- * called, otherwise the connection will be closed and resources
- * freed.
- *
- * Return value: the keep-alive flag for @conn.
- */
-gboolean
-soup_connection_is_keep_alive (SoupConnection *conn)
-{
-       g_return_val_if_fail (conn != NULL, FALSE);
-       return conn->keep_alive;
-}
-
-/**
- * soup_connection_get_context:
- * @conn: a #SoupConnection.
- *
- * Returns the #SoupContext from which @conn was created, with an
- * added ref.
- *
- * Return value: the #SoupContext associated with @conn. Unref when
- * finished.
- */
-SoupContext *
-soup_connection_get_context (SoupConnection *conn)
-{
-       g_return_val_if_fail (conn != NULL, FALSE);
-       
-       soup_context_ref (conn->context);
-       return conn->context;
-}
-
-/**
- * soup_connection_set_used:
- * @conn: a #SoupConnection.
- *
- * Clears the "new" flag on @conn.
- */
-void
-soup_connection_set_used (SoupConnection *conn)
-{
-       g_return_if_fail (conn != NULL);
-
-       conn->new = FALSE;
-}
-
-/**
- * soup_connection_is_new:
- * @conn: a #SoupConnection.
- *
- * Returns %TRUE if this is the first use of @conn (ie. no response
- * has been read from it yet)
- *
- * Return value: boolean representing whether this is the first time a
- * connection has been used.
- */
-gboolean
-soup_connection_is_new (SoupConnection *conn)
-{
-       g_return_val_if_fail (conn != NULL, FALSE);
-       return conn->new;
-}
-
-
-static void
 get_idle_conns_for_host (gpointer key, gpointer value, gpointer data)
 {
        SoupHost *host = value;
@@ -760,7 +572,7 @@ get_idle_conns_for_host (gpointer key, gpointer value, gpointer data)
 
        for (c = host->connections; c; c = c->next) {
                conn = c->data;
-               if (!conn->in_use)
+               if (!soup_connection_is_in_use (conn))
                        *idle_conns = g_slist_prepend (*idle_conns, conn);
        }
 }
@@ -781,8 +593,10 @@ soup_connection_purge_idle (void)
        idle_conns = NULL;
        g_hash_table_foreach (soup_hosts, get_idle_conns_for_host, &idle_conns);
 
-       for (i = idle_conns; i; i = i->next)
-               connection_free (i->data);
+       for (i = idle_conns; i; i = i->next) {
+               soup_connection_disconnect (i->data);
+               g_object_unref (i->data);
+       }
        g_slist_free (idle_conns);
 }
 
@@ -795,21 +609,28 @@ soup_context_lookup_auth (SoupContext *ctx, SoupMessage *msg)
        char *path, *dir;
        const char *realm;
 
-       g_return_val_if_fail (ctx != NULL, NULL);
+       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), NULL);
+
+       if (ctx->priv->server->ntlm_auths && msg && msg->connection) {
+               SoupAuth *auth;
 
-       if (ctx->server->use_ntlm && msg && msg->connection) {
-               if (!msg->connection->auth)
-                       msg->connection->auth = soup_auth_ntlm_new ();
-               return msg->connection->auth;
+               auth = g_hash_table_lookup (ctx->priv->server->ntlm_auths,
+                                           msg->connection);
+               if (!auth) {
+                       auth = soup_auth_ntlm_new ();
+                       g_hash_table_insert (ctx->priv->server->ntlm_auths,
+                                            msg->connection, auth);
+               }
+               return auth;
        }
 
-       if (!ctx->server->auth_realms)
+       if (!ctx->priv->server->auth_realms)
                return NULL;
 
-       path = g_strdup (ctx->uri->path);
+       path = g_strdup (ctx->priv->uri->path);
        dir = path;
         do {
-                realm = g_hash_table_lookup (ctx->server->auth_realms, path);
+                realm = g_hash_table_lookup (ctx->priv->server->auth_realms, path);
                 if (realm)
                        break;
 
@@ -820,7 +641,7 @@ soup_context_lookup_auth (SoupContext *ctx, SoupMessage *msg)
 
        g_free (path);
        if (realm)
-               return g_hash_table_lookup (ctx->server->auths, realm);
+               return g_hash_table_lookup (ctx->priv->server->auths, realm);
        else
                return NULL;
 }
@@ -829,31 +650,34 @@ static gboolean
 update_auth_internal (SoupContext *ctx, SoupConnection *conn,
                      const GSList *headers, gboolean prior_auth_failed)
 {
-       SoupHost *server = ctx->server;
+       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->use_ntlm && conn && conn->auth) {
-               if (soup_auth_is_authenticated (conn->auth)) {
-                       /* This is a "permission denied", not a
-                        * "password incorrect". There's nothing more
-                        * we can do.
-                        */
-                       return FALSE;
-               }
+       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_object_unref (conn->auth);
-               conn->auth = NULL;
+                       /* 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->uri->authmech);
+       new_auth = soup_auth_new_from_header_list (headers, ctx->priv->uri->authmech);
        if (!new_auth)
                return FALSE;
 
@@ -880,9 +704,10 @@ update_auth_internal (SoupContext *ctx, SoupConnection *conn,
        }
 
        if (SOUP_IS_AUTH_NTLM (new_auth)) {
-               server->use_ntlm = TRUE;
+               if (!server->ntlm_auths)
+                       server->ntlm_auths = g_hash_table_new (NULL, NULL);
                if (conn) {
-                       conn->auth = new_auth;
+                       g_hash_table_insert (server->ntlm_auths, conn, new_auth);
                        return soup_context_authenticate_auth (ctx, new_auth);
                } else {
                        g_object_unref (new_auth);
@@ -899,7 +724,7 @@ update_auth_internal (SoupContext *ctx, SoupConnection *conn,
        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->uri);
+       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,
@@ -938,7 +763,7 @@ soup_context_update_auth (SoupContext *ctx, SoupMessage *msg)
 {
        const GSList *headers;
 
-       g_return_val_if_fail (ctx != NULL, FALSE);
+       g_return_val_if_fail (SOUP_IS_CONTEXT (ctx), FALSE);
        g_return_val_if_fail (msg != NULL, FALSE);
 
        if (msg->errorcode == SOUP_ERROR_PROXY_UNAUTHORIZED) {
@@ -957,7 +782,7 @@ soup_context_preauthenticate (SoupContext *ctx, const char *header)
 {
        GSList *headers;
 
-       g_return_if_fail (ctx != NULL);
+       g_return_if_fail (SOUP_IS_CONTEXT (ctx));
        g_return_if_fail (header != NULL);
 
        headers = g_slist_append (NULL, (char *)header);
@@ -968,7 +793,7 @@ soup_context_preauthenticate (SoupContext *ctx, const char *header)
 gboolean
 soup_context_authenticate_auth (SoupContext *ctx, SoupAuth *auth)
 {
-       const SoupUri *uri = ctx->uri;
+       const SoupUri *uri = ctx->priv->uri;
 
        if (!uri->user && soup_auth_fn) {
                (*soup_auth_fn) (soup_auth_get_scheme_name (auth),
@@ -990,7 +815,7 @@ soup_context_invalidate_auth (SoupContext *ctx, SoupAuth *auth)
        char *realm;
        gpointer key, value;
 
-       g_return_if_fail (ctx != NULL);
+       g_return_if_fail (SOUP_IS_CONTEXT (ctx));
        g_return_if_fail (auth != NULL);
 
        /* Try to just clean up the auth without removing it. */
@@ -1002,10 +827,10 @@ soup_context_invalidate_auth (SoupContext *ctx, SoupAuth *auth)
                                 soup_auth_get_scheme_name (auth),
                                 soup_auth_get_realm (auth));
 
-       if (g_hash_table_lookup_extended (ctx->server->auths, realm,
+       if (g_hash_table_lookup_extended (ctx->priv->server->auths, realm,
                                          &key, &value) &&
            auth == (SoupAuth *)value) {
-               g_hash_table_remove (ctx->server->auths, realm);
+               g_hash_table_remove (ctx->priv->server->auths, realm);
                g_free (key);
                g_object_unref (auth);
        }
index 79853d1..5521f7a 100644 (file)
@@ -1,22 +1,38 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /*
- * soup-context.h: Asyncronous Callback-based HTTP Request Queue.
- *
- * Authors:
- *      Alex Graveley (alex@ximian.com)
- *
- * Copyright (C) 2000-2002, Ximian, Inc.
+ * Copyright (C) 2000-2003, Ximian, Inc.
  */
 
 #ifndef SOUP_CONTEXT_H
 #define SOUP_CONTEXT_H 1
 
-#include <glib.h>
+#include <glib-object.h>
+#include <libsoup/soup-connection.h>
 #include <libsoup/soup-uri.h>
 
-typedef struct _SoupContext SoupContext;
+#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;
+
+typedef struct {
+       GObject parent;
+
+       SoupContextPrivate *priv;
+} SoupContext;
+
+typedef struct {
+       GObjectClass parent_class;
+
+} SoupContextClass;
+
+GType soup_context_get_type (void);
+
 
-typedef struct _SoupConnection SoupConnection;
 
 typedef enum {
        SOUP_CONNECT_ERROR_NONE,
@@ -31,14 +47,10 @@ typedef void (*SoupConnectCallbackFn) (SoupContext          *ctx,
 
 typedef gpointer SoupConnectId;
 
-SoupContext   *soup_context_get               (const gchar          *uri);
+SoupContext   *soup_context_get               (const char           *uri);
 
 SoupContext   *soup_context_from_uri          (SoupUri              *suri);
 
-void           soup_context_ref               (SoupContext          *ctx);
-
-void           soup_context_unref             (SoupContext          *ctx);
-
 SoupConnectId  soup_context_get_connection    (SoupContext          *ctx,
                                               SoupConnectCallbackFn cb,
                                               gpointer              user_data);
@@ -48,25 +60,11 @@ void           soup_context_cancel_connect    (SoupConnectId         tag);
 const SoupUri *soup_context_get_uri           (SoupContext          *ctx);
 
 
-GIOChannel    *soup_connection_get_iochannel  (SoupConnection       *conn);
-
-SoupContext   *soup_connection_get_context    (SoupConnection       *conn);
-
-void           soup_connection_set_keep_alive (SoupConnection       *conn, 
-                                              gboolean              keepalive);
-
-gboolean       soup_connection_is_keep_alive  (SoupConnection       *conn);
-
-void           soup_connection_set_used       (SoupConnection       *conn);
-gboolean       soup_connection_is_new         (SoupConnection       *conn);
-
-void           soup_connection_release        (SoupConnection       *conn);
-
+void           soup_context_preauthenticate   (SoupContext          *ctx,
+                                              const char           *header);
+                                         
 
 void           soup_connection_purge_idle     (void);
 
 
-void           soup_context_preauthenticate   (SoupContext          *ctx,
-                                              const char           *header);
-                                         
 #endif /*SOUP_CONTEXT_H*/
diff --git a/libsoup/soup-marshal.list b/libsoup/soup-marshal.list
new file mode 100644 (file)
index 0000000..fa33740
--- /dev/null
@@ -0,0 +1 @@
+NONE:NONE
index 6c96066..e64ae89 100644 (file)
@@ -148,7 +148,9 @@ release_connection (const SoupDataBuffer *data,
                    gpointer              user_data)
 {
        SoupConnection *conn = user_data;
-       soup_connection_release (conn);
+
+       soup_connection_set_in_use (conn, FALSE);
+       g_object_unref (conn);
 
        if (data->owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (data->body);
@@ -158,8 +160,9 @@ static void
 release_and_close_connection (gboolean headers_done, gpointer user_data)
 {
        SoupConnection *conn = user_data;
-       soup_connection_set_keep_alive (conn, FALSE);
-       soup_connection_release (conn);
+
+       soup_connection_disconnect (conn);
+       g_object_unref (conn);
 }
 
 /**
@@ -175,9 +178,7 @@ soup_message_cleanup (SoupMessage *req)
 {
        g_return_if_fail (req != NULL);
 
-       if (req->connection && 
-           soup_connection_is_keep_alive (req->connection) &&
-           req->priv->read_tag &&
+       if (req->connection && req->priv->read_tag &&
            req->status == SOUP_STATUS_READING_RESPONSE) {
                soup_transfer_read_set_callbacks (req->priv->read_tag,
                                                  NULL,
@@ -206,7 +207,8 @@ soup_message_cleanup (SoupMessage *req)
        }
 
        if (req->connection) {
-               soup_connection_release (req->connection);
+               soup_connection_set_in_use (req->connection, FALSE);
+               g_object_unref (req->connection);
                req->connection = NULL;
        }
 
@@ -217,7 +219,7 @@ static void
 finalize_message (SoupMessage *req)
 {
        if (req->context)
-               soup_context_unref (req->context);
+               g_object_unref (req->context);
 
        if (req->request.owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (req->request.body);
@@ -289,6 +291,23 @@ soup_message_issue_callback (SoupMessage *req)
 }
 
 /**
+ * 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->connection) {
+               soup_connection_disconnect (msg->connection);
+               g_object_unref (msg->connection);
+               msg->connection = NULL;
+       }
+}
+
+/**
  * soup_message_cancel:
  * @req: a #SoupMessage currently being processed.
  * 
@@ -300,11 +319,7 @@ void
 soup_message_cancel (SoupMessage *msg) 
 {
        soup_message_set_error (msg, SOUP_ERROR_CANCELLED);
-
-       /* Kill the connection as a safety measure */
-       if (msg->connection)
-               soup_connection_set_keep_alive (msg->connection, FALSE);
-
+       soup_message_disconnect (msg);
        soup_message_issue_callback (msg);
 }
 
@@ -531,10 +546,7 @@ requeue_read_error (gboolean body_started, gpointer user_data)
 {
        SoupMessage *msg = user_data;
 
-       soup_connection_set_keep_alive (msg->connection, FALSE);
-       soup_connection_release (msg->connection);
-       msg->connection = NULL;
-
+       soup_message_disconnect (msg);
        soup_queue_message (msg, 
                            msg->priv->callback, 
                            msg->priv->user_data);
@@ -550,18 +562,18 @@ requeue_read_finished (const SoupDataBuffer *buf,
        if (buf->owner == SOUP_BUFFER_SYSTEM_OWNED)
                g_free (buf->body);
 
-       soup_connection_set_used (msg->connection);
-       if (!soup_connection_is_keep_alive (msg->connection))
-               requeue_read_error (FALSE, msg);
-       else {
-               msg->connection = NULL;
-
-               soup_queue_message (msg, 
-                                   msg->priv->callback, 
-                                   msg->priv->user_data);
-
-               msg->connection = conn;
+       if (soup_connection_is_connected (conn)) {
+               soup_connection_mark_old (conn);
+       } else {
+               g_object_unref (conn);
+               conn = NULL;
        }
+
+       msg->connection = NULL;
+       soup_queue_message (msg, 
+                           msg->priv->callback, 
+                           msg->priv->user_data);
+       msg->connection = conn;
 }
 
 /**
@@ -576,7 +588,7 @@ soup_message_requeue (SoupMessage *req)
 {
        g_return_if_fail (req != NULL);
 
-       if (req->connection && req->connection->auth && req->priv->read_tag) {
+       if (req->connection && req->priv->read_tag) {
                soup_transfer_read_set_callbacks (req->priv->read_tag,
                                                  NULL,
                                                  NULL,
@@ -683,7 +695,7 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
                goto INVALID_REDIRECT;
 
        soup_message_set_context (msg, new_ctx);
-       soup_context_unref (new_ctx);
+       g_object_unref (new_ctx);
 
        soup_message_requeue (msg);
        return;
@@ -1015,15 +1027,20 @@ soup_message_set_context (SoupMessage       *msg,
 {
        g_return_if_fail (msg != NULL);
 
-       if (msg->context) {
-               if (msg->connection &&
-                   (!new_ctx || (msg->context->server != new_ctx->server)))
+       if (msg->context && new_ctx) {
+               const SoupUri *old, *new;
+
+               old = soup_context_get_uri (msg->context);
+               new = soup_context_get_uri (new_ctx);
+               if (strcmp (old->host, new->host) != 0)
                        soup_message_cleanup (msg);
-               soup_context_unref (msg->context);
-       }
+       } else if (!new_ctx)
+               soup_message_cleanup (msg);
 
        if (new_ctx)
-               soup_context_ref (new_ctx);
+               g_object_ref (new_ctx);
+       if (msg->context)
+               g_object_unref (msg->context);
 
        msg->context = new_ctx;
 }
@@ -1033,10 +1050,7 @@ soup_message_get_context (SoupMessage       *msg)
 {
        g_return_val_if_fail (msg != NULL, NULL);
 
-       if (msg->context)
-               soup_context_ref (msg->context);
-
-       return msg->context;
+       return g_object_ref (msg->context);
 }
 
 void
index 3441394..5eefeba 100644 (file)
@@ -84,6 +84,8 @@ void           soup_message_free                (SoupMessage       *req);
 
 void           soup_message_cancel              (SoupMessage       *req);
 
+void           soup_message_disconnect          (SoupMessage       *req);
+
 SoupErrorClass soup_message_send                (SoupMessage       *msg);
 
 void           soup_message_queue               (SoupMessage       *req, 
index 3ff6990..025e85a 100644 (file)
@@ -39,13 +39,12 @@ static SoupSecurityPolicy ssl_security_level = SOUP_SECURITY_DOMESTIC;
 void
 soup_set_proxy (SoupContext *context)
 {
+       if (context)
+               g_object_ref (context);
        if (proxy_context)
-               soup_context_unref (proxy_context);
+               g_object_unref (proxy_context);
 
        proxy_context = context;
-
-       if (proxy_context)
-               soup_context_ref (proxy_context);
 }
 
 /**
index a65b74f..52ac68d 100644 (file)
@@ -48,9 +48,11 @@ typedef struct {
        gchar      *host;
        GSList     *connections;      /* CONTAINS: SoupConnection */
        GHashTable *contexts;         /* KEY: uri->path, VALUE: SoupContext */
-       gboolean    use_ntlm;
+
        GHashTable *auth_realms;      /* KEY: uri->path, VALUE: scheme:realm */
        GHashTable *auths;            /* KEY: scheme:realm, VALUE: SoupAuth */
+
+       GHashTable *ntlm_auths;       /* KEY: SoupConnection, VALUE: SoupAuth */
 } SoupHost;
 
 struct SoupSocketPrivate {
@@ -66,26 +68,6 @@ struct SoupSocketPrivate {
 #define soup_sockaddr_max sockaddr_in
 #endif
 
-struct _SoupContext {
-       SoupUri      *uri;
-       SoupHost     *server;
-       guint         refcnt;
-};
-
-struct _SoupConnection {
-       SoupHost     *server;
-       SoupContext  *context;
-       GIOChannel   *channel;
-       SoupSocket   *socket;
-       SoupAuth     *auth;
-       guint         port;
-       gboolean      in_use;
-       guint         last_used_id;
-       gboolean      keep_alive;
-       guint         death_tag;
-       gboolean      new;
-};
-
 struct _SoupServer {
        SoupProtocol       proto;
        gint               port;
index 05e7859..2386efa 100644 (file)
@@ -61,8 +61,7 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
        gboolean conn_is_new;
 
        conn_is_new = soup_connection_is_new (req->connection);
-       soup_connection_set_used (req->connection);
-       soup_connection_set_keep_alive (req->connection, FALSE);
+       soup_message_disconnect (req);
 
        switch (req->status) {
        case SOUP_STATUS_IDLE:
@@ -78,7 +77,9 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
        case SOUP_STATUS_READING_RESPONSE:
        case SOUP_STATUS_SENDING_REQUEST:
                if (!body_started) {
-                       if (req->context->uri->protocol == SOUP_PROTOCOL_HTTPS) {
+                       const SoupUri *uri = soup_context_get_uri (req->context);
+
+                       if (uri->protocol == SOUP_PROTOCOL_HTTPS) {
                                /*
                                 * This can happen if the SSL handshake fails
                                 * for some reason (untrustable signatures,
@@ -91,8 +92,6 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
                                        soup_message_issue_callback (req);
                                } else {
                                        req->priv->retries++;
-                                       soup_connection_release (req->connection);
-                                       req->connection = NULL;
                                        soup_message_requeue (req);
                                }
                        } else if (conn_is_new) {
@@ -102,8 +101,6 @@ soup_queue_error_cb (gboolean body_started, gpointer user_data)
                                soup_message_issue_callback (req);
                        } else {
                                /* Must have timed out. Try a new connection */
-                               soup_connection_release (req->connection);
-                               req->connection = NULL;
                                soup_message_requeue (req);
                        }
                } else {
@@ -126,7 +123,7 @@ soup_queue_read_headers_cb (const GString        *headers,
                            gpointer              user_data)
 {
        SoupMessage *req = user_data;
-       const gchar *connection, *length, *enc;
+       const char *length, *enc;
        SoupHttpVersion version;
        GHashTable *resp_hdrs;
        SoupMethodId meth_id;
@@ -150,24 +147,6 @@ soup_queue_read_headers_cb (const GString        *headers,
        req->errorclass = soup_error_get_class (req->errorcode);
 
        /* 
-        * Handle connection persistence
-        * Close connection if:
-        *   - Connection header is "close"
-        *   - HTTP 1.0 and Connection header is not present
-        */
-       connection = soup_message_get_header (resp_hdrs, "Connection");
-
-       if ((connection && !g_strcasecmp (connection, "close")) ||
-           (!connection && version == SOUP_HTTP_1_0))
-               soup_connection_set_keep_alive (req->connection, FALSE);
-
-       /*
-        * Handle successful CONNECT request by keeping connection open
-        */
-       if (meth_id == SOUP_METHOD_ID_CONNECT && !SOUP_MESSAGE_IS_ERROR (req))
-               soup_connection_set_keep_alive (req->connection, TRUE);
-
-       /* 
         * Special case zero body handling for:
         *   - HEAD requests (where content-length must be ignored) 
         *   - CONNECT requests (no body expected) 
@@ -226,7 +205,7 @@ soup_queue_read_headers_cb (const GString        *headers,
        return;
 
  THROW_MALFORMED_HEADER:
-       soup_connection_set_keep_alive (req->connection, FALSE);
+       soup_message_disconnect (req);
        soup_message_issue_callback (req);
        return;
 }
@@ -251,8 +230,16 @@ soup_queue_read_done_cb (const SoupDataBuffer *data,
                         gpointer              user_data)
 {
        SoupMessage *req = user_data;
+       const char *connection;
 
-       soup_connection_set_used (req->connection);
+       /* Handle connection persistence */
+       connection = soup_message_get_header (req->response_headers,
+                                             "Connection");
+       if ((connection && !g_strcasecmp (connection, "close")) ||
+           soup_message_get_http_version (req) == SOUP_HTTP_1_0)
+               soup_message_disconnect (req);
+       else
+               soup_connection_mark_old (req->connection);
 
        req->response.owner = data->owner;
        req->response.length = data->length;
@@ -526,19 +513,9 @@ proxy_https_connect_cb (SoupMessage *msg, gpointer user_data)
        gboolean *ret = user_data;
 
        if (!SOUP_MESSAGE_IS_ERROR (msg)) {
-               /*
-                * Bless the connection to SSL
-                */
-               msg->connection->channel = 
-                       soup_ssl_get_iochannel (msg->connection->channel);
-               
+               soup_connection_start_ssl (msg->connection);            
                *ret = TRUE;
        }
-
-       /*
-        * Avoid releasing the connection on message free
-        */
-       msg->connection = NULL;
 }
 
 static gboolean
@@ -557,18 +534,12 @@ proxy_https_connect (SoupContext    *proxy,
                return FALSE;
 
        connect_msg = soup_message_new (dest_ctx, SOUP_METHOD_CONNECT);
-       connect_msg->connection = conn;
+       connect_msg->connection = g_object_ref (conn);
        soup_message_add_handler (connect_msg, 
                                  SOUP_HANDLER_POST_BODY,
                                  proxy_https_connect_cb,
                                  &ret);
        soup_message_send (connect_msg);
-
-       /*
-        * Avoid releasing the connection on message free
-        */
-       connect_msg->connection = NULL;
-
        soup_message_free (connect_msg);
 
        return ret;
@@ -591,8 +562,7 @@ proxy_connect (SoupContext *ctx, SoupMessage *req, SoupConnection *conn)
        
        /* Handle SOCKS proxy negotiation */
        if ((proto == SOUP_PROTOCOL_SOCKS4 || proto == SOUP_PROTOCOL_SOCKS5)) {
-               soup_connect_socks_proxy (conn, 
-                                         req->context, 
+               soup_connect_socks_proxy (conn, ctx, req->context, 
                                          soup_queue_connect_cb,
                                          req);
                return TRUE;
@@ -607,14 +577,6 @@ proxy_connect (SoupContext *ctx, SoupMessage *req, SoupConnection *conn)
                                SOUP_ERROR_CANT_CONNECT_PROXY,
                                "Unable to create secure data "
                                "tunnel through proxy");
-
-                       /*
-                        * If the tunnelling failed, our connection will
-                        * have been freed by the requeue in
-                        * proxy_https_connect()
-                        */
-                       req->connection = NULL;
-
                        soup_message_issue_callback (req);
                        return TRUE;
                }
@@ -632,6 +594,9 @@ soup_queue_connect_cb (SoupContext          *ctx,
        SoupMessage *req = user_data;
 
        req->priv->connect_tag = NULL;
+       g_object_ref (conn);
+       if (req->connection)
+               g_object_unref (req->connection);
        req->connection = conn;
 
        switch (err) {
@@ -738,7 +703,7 @@ soup_idle_handle_new_requests (gpointer unused)
                req->status = SOUP_STATUS_CONNECTING;
 
                if (req->connection && 
-                   soup_connection_is_keep_alive (req->connection))
+                   soup_connection_is_connected (req->connection))
                        start_request (ctx, req);
                else {
                        gpointer connect_tag;
index 6289394..fdd152d 100644 (file)
@@ -387,7 +387,7 @@ read_headers_cgi (SoupMessage *msg,
                if (!ctx) goto THROW_MALFORMED_HEADER;
 
                soup_message_set_context (msg, ctx);
-               soup_context_unref (ctx);
+               g_object_unref (ctx);
        }
 
        /*
@@ -652,7 +652,7 @@ read_headers_cb (const GString        *headers,
                if (!ctx) goto THROW_MALFORMED_HEADER;
 
                soup_message_set_context (msg, ctx);
-               soup_context_unref (ctx);
+               g_object_unref (ctx);
        }
 
        g_free (req_path);
@@ -726,9 +726,11 @@ call_handler (SoupMessage          *req,
        }
 
        if (hand->callback) {
+               const SoupUri *uri = soup_context_get_uri (req->context);
+
                SoupServerContext servctx = {
                        req,
-                       req->context->uri->path,
+                       uri->path,
                        soup_method_get_id (req->method),
                        auth,
                        server,
index ee8b2c0..19b9949 100644 (file)
@@ -18,6 +18,7 @@
 
 typedef struct {
        SoupConnection        *src_conn;
+       SoupContext           *proxy_ctx;
        
        enum {
                SOCKS_4_DEST_ADDR_LOOKUP,
@@ -41,8 +42,11 @@ typedef struct {
 static void
 socks_data_free (SoupSocksData *sd)
 {
+       if (sd->proxy_ctx)
+               g_object_unref (sd->proxy_ctx);
+
        if (sd->dest_ctx)
-               soup_context_unref (sd->dest_ctx);
+               g_object_unref (sd->dest_ctx);
 
        if (sd->dest_addr)
                g_object_unref (sd->dest_addr);
@@ -77,7 +81,6 @@ soup_socks_write (GIOChannel* iochannel,
                  SoupSocksData *sd)
 {
        const SoupUri *dest_uri, *proxy_uri;
-       SoupContext *proxy_ctx;
        struct sockaddr *sa;
        gboolean finished = FALSE;
        guchar buf[128];
@@ -86,10 +89,7 @@ soup_socks_write (GIOChannel* iochannel,
        GIOError error;
 
        dest_uri = soup_context_get_uri (sd->dest_ctx);
-
-       proxy_ctx = soup_connection_get_context (sd->src_conn);
-       proxy_uri = soup_context_get_uri (proxy_ctx);
-       soup_context_unref (proxy_ctx);
+       proxy_uri = soup_context_get_uri (sd->proxy_ctx);
 
        switch (sd->phase) {
        case SOCKS_4_SEND_DEST_ADDR: 
@@ -282,30 +282,27 @@ soup_lookup_dest_addr_cb (SoupAddress*         inetaddr,
 
 void
 soup_connect_socks_proxy (SoupConnection        *conn, 
+                         SoupContext           *proxy_ctx, 
                          SoupContext           *dest_ctx, 
                          SoupConnectCallbackFn  cb,
                          gpointer               user_data)
 {
        SoupSocksData *sd = NULL;
-       SoupContext *proxy_ctx;
        const SoupUri *dest_uri, *proxy_uri;
        GIOChannel *channel;
 
        if (!soup_connection_is_new (conn)) goto CONNECT_SUCCESS;
        
-       soup_context_ref (dest_ctx);
-       dest_uri = soup_context_get_uri (dest_ctx);
-
-       proxy_ctx = soup_connection_get_context (conn);
-       proxy_uri = soup_context_get_uri (proxy_ctx);
-       soup_context_unref (proxy_ctx);
-
        sd = g_new0 (SoupSocksData, 1);
        sd->src_conn = conn;
-       sd->dest_ctx = dest_ctx;
+       sd->proxy_ctx = g_object_ref (proxy_ctx);
+       sd->dest_ctx = g_object_ref (dest_ctx);
        sd->cb = cb;
        sd->user_data = user_data;
        
+       dest_uri = soup_context_get_uri (dest_ctx);
+       proxy_uri = soup_context_get_uri (proxy_ctx);
+
        if (proxy_uri->protocol == SOUP_PROTOCOL_SOCKS4) {
                soup_address_new (dest_uri->host, 
                                  soup_lookup_dest_addr_cb,
index fd24f9f..7cef445 100644 (file)
@@ -15,6 +15,7 @@
 #include <libsoup/soup-context.h>
 
 void soup_connect_socks_proxy (SoupConnection        *conn, 
+                              SoupContext           *proxy_ctx, 
                               SoupContext           *dest_ctx, 
                               SoupConnectCallbackFn  cb,
                               gpointer               user_data);
index 421aa2a..36147de 100644 (file)
@@ -194,7 +194,7 @@ get_url (const char *url)
 
        pending++;
        soup_message_queue (msg, got_url, soup_uri_new (url));
-       soup_context_unref (ctx);
+       g_object_unref (ctx);
        g_free (url_to_get);
 }