From 3601be883b932a7b3167ad39cc786a16fd68b834 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 14 Aug 2003 15:02:32 +0000 Subject: 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. --- ChangeLog | 41 ++++ configure.in | 4 +- libsoup/.cvsignore | 3 +- libsoup/Makefile.am | 21 ++ libsoup/soup-connection.c | 304 ++++++++++++++++++++++++ libsoup/soup-connection.h | 54 +++++ libsoup/soup-context.c | 583 ++++++++++++++++------------------------------ libsoup/soup-context.h | 60 +++-- libsoup/soup-marshal.list | 1 + libsoup/soup-message.c | 94 ++++---- libsoup/soup-message.h | 2 + libsoup/soup-misc.c | 7 +- libsoup/soup-private.h | 24 +- libsoup/soup-queue.c | 79 ++----- libsoup/soup-server.c | 8 +- libsoup/soup-socks.c | 27 +-- libsoup/soup-socks.h | 1 + tests/get.c | 2 +- 18 files changed, 760 insertions(+), 555 deletions(-) create mode 100644 libsoup/soup-connection.c create mode 100644 libsoup/soup-connection.h create mode 100644 libsoup/soup-marshal.list diff --git a/ChangeLog b/ChangeLog index 54f26e13..34f1250c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,44 @@ +2003-08-14 Dan Winship + + * 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 * libsoup/soup-error.c: Fix a spelling mistake. 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 +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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 + +#include +#include + +#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,160 +559,10 @@ 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) { @@ -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 +#include +#include #include -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); @@ -288,6 +290,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; @@ -149,24 +146,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) @@ -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 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); } -- cgit v1.2.1