summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@src.gnome.org>2003-08-14 15:02:32 +0000
committerDan Winship <danw@src.gnome.org>2003-08-14 15:02:32 +0000
commit3601be883b932a7b3167ad39cc786a16fd68b834 (patch)
treee8204b11977a14312ce8240ec7806dfb3849691b
parentee5e4f227dbd85e40b2e510f0390d57c690525d4 (diff)
downloadlibsoup-3601be883b932a7b3167ad39cc786a16fd68b834.tar.gz
New, split out from soup-context and made into a GObject.
* 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.
-rw-r--r--ChangeLog41
-rw-r--r--configure.in4
-rw-r--r--libsoup/.cvsignore3
-rw-r--r--libsoup/Makefile.am21
-rw-r--r--libsoup/soup-connection.c304
-rw-r--r--libsoup/soup-connection.h54
-rw-r--r--libsoup/soup-context.c583
-rw-r--r--libsoup/soup-context.h60
-rw-r--r--libsoup/soup-marshal.list1
-rw-r--r--libsoup/soup-message.c94
-rw-r--r--libsoup/soup-message.h2
-rw-r--r--libsoup/soup-misc.c7
-rw-r--r--libsoup/soup-private.h24
-rw-r--r--libsoup/soup-queue.c79
-rw-r--r--libsoup/soup-server.c8
-rw-r--r--libsoup/soup-socks.c27
-rw-r--r--libsoup/soup-socks.h1
-rw-r--r--tests/get.c2
18 files changed, 760 insertions, 555 deletions
diff --git a/ChangeLog b/ChangeLog
index 54f26e13..34f1250c 100644
--- 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
diff --git a/configure.in b/configure.in
index fd176028..8d01009d 100644
--- a/configure.in
+++ b/configure.in
@@ -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 ***
diff --git a/libsoup/.cvsignore b/libsoup/.cvsignore
index 0be24a01..513e1543 100644
--- a/libsoup/.cvsignore
+++ b/libsoup/.cvsignore
@@ -4,4 +4,5 @@
.libs
Makefile
Makefile.in
-libsoup-ssl-proxy
+soup-marshal.c
+soup-marshal.h
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index cf17e6fc..c1d4a2af 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -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
index 00000000..571eafed
--- /dev/null
+++ b/libsoup/soup-connection.c
@@ -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
index 00000000..70d4d4ca
--- /dev/null
+++ b/libsoup/soup-connection.h
@@ -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*/
diff --git a/libsoup/soup-context.c b/libsoup/soup-context.c
index c732b8b4..14f4cf90 100644
--- a/libsoup/soup-context.c
+++ b/libsoup/soup-context.c
@@ -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
@@ -33,10 +30,95 @@
#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);
}
diff --git a/libsoup/soup-context.h b/libsoup/soup-context.h
index 79853d11..5521f7a1 100644
--- a/libsoup/soup-context.h
+++ b/libsoup/soup-context.h
@@ -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
index 00000000..fa33740e
--- /dev/null
+++ b/libsoup/soup-marshal.list
@@ -0,0 +1 @@
+NONE:NONE
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 6c96066f..e64ae89d 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -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
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 34413948..5eefebad 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -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,
diff --git a/libsoup/soup-misc.c b/libsoup/soup-misc.c
index 3ff69903..025e85ac 100644
--- a/libsoup/soup-misc.c
+++ b/libsoup/soup-misc.c
@@ -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);
}
/**
diff --git a/libsoup/soup-private.h b/libsoup/soup-private.h
index a65b74f1..52ac68da 100644
--- a/libsoup/soup-private.h
+++ b/libsoup/soup-private.h
@@ -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;
diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c
index 05e78593..2386efa4 100644
--- a/libsoup/soup-queue.c
+++ b/libsoup/soup-queue.c
@@ -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;
diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c
index 6289394f..fdd152d8 100644
--- a/libsoup/soup-server.c
+++ b/libsoup/soup-server.c
@@ -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,
diff --git a/libsoup/soup-socks.c b/libsoup/soup-socks.c
index ee8b2c01..19b99491 100644
--- a/libsoup/soup-socks.c
+++ b/libsoup/soup-socks.c
@@ -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,
diff --git a/libsoup/soup-socks.h b/libsoup/soup-socks.h
index fd24f9f6..7cef4458 100644
--- a/libsoup/soup-socks.h
+++ b/libsoup/soup-socks.h
@@ -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);
diff --git a/tests/get.c b/tests/get.c
index 421aa2a9..36147de9 100644
--- a/tests/get.c
+++ b/tests/get.c
@@ -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);
}