summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXan Lopez <xan@gnome.org>2009-07-23 16:26:51 +0300
committerXan Lopez <xan@gnome.org>2009-07-23 16:26:51 +0300
commit698d9017fb7feab33ed4a36689bb3b86e2b7da80 (patch)
treea98b0ef7007ba86a7ac9763d640b12d2f6ceb2ec
parent5e49d5438a07fdaed7cd58365c9c79548fc55805 (diff)
parentfd5b785fbaad81f99c6c8b8bcb52028990e19120 (diff)
downloadlibsoup-698d9017fb7feab33ed4a36689bb3b86e2b7da80.tar.gz
Merge branch 'master' into cache
Conflicts: libsoup/Makefile.am libsoup/soup-session-private.h libsoup/soup-session.c libsoup/soup.h
-rw-r--r--.gitignore1
-rw-r--r--HACKING66
-rw-r--r--NEWS85
-rwxr-xr-xautogen.sh2
-rw-r--r--configure.in41
-rw-r--r--libsoup/Makefile.am4
-rw-r--r--libsoup/soup-auth-digest.c6
-rw-r--r--libsoup/soup-auth-manager.c54
-rw-r--r--libsoup/soup-connection.c480
-rw-r--r--libsoup/soup-connection.h29
-rw-r--r--libsoup/soup-content-sniffer.c605
-rw-r--r--libsoup/soup-content-sniffer.h57
-rw-r--r--libsoup/soup-dns.c4
-rw-r--r--libsoup/soup-form.c2
-rw-r--r--libsoup/soup-gnutls.c15
-rw-r--r--libsoup/soup-marshal.list1
-rw-r--r--libsoup/soup-message-client-io.c3
-rw-r--r--libsoup/soup-message-headers.c39
-rw-r--r--libsoup/soup-message-io.c93
-rw-r--r--libsoup/soup-message-private.h4
-rw-r--r--libsoup/soup-message-queue.c24
-rw-r--r--libsoup/soup-message-queue.h8
-rw-r--r--libsoup/soup-message.c62
-rw-r--r--libsoup/soup-message.h1
-rw-r--r--libsoup/soup-method.h33
-rw-r--r--libsoup/soup-misc.c57
-rw-r--r--libsoup/soup-misc.h2
-rw-r--r--libsoup/soup-proxy-resolver-gnome.c184
-rw-r--r--libsoup/soup-proxy-resolver-gnome.h1
-rw-r--r--libsoup/soup-proxy-resolver.c164
-rw-r--r--libsoup/soup-proxy-resolver.h4
-rw-r--r--libsoup/soup-proxy-uri-resolver.c97
-rw-r--r--libsoup/soup-proxy-uri-resolver.h57
-rw-r--r--libsoup/soup-session-async.c178
-rw-r--r--libsoup/soup-session-private.h31
-rw-r--r--libsoup/soup-session-sync.c115
-rw-r--r--libsoup/soup-session.c285
-rw-r--r--libsoup/soup-socket.c29
-rw-r--r--libsoup/soup-uri.c82
-rw-r--r--libsoup/soup-uri.h12
-rw-r--r--libsoup/soup-value-utils.h2
-rw-r--r--libsoup/soup-xmlrpc.h2
-rw-r--r--libsoup/soup.h1
-rw-r--r--tests/Makefile.am29
-rw-r--r--tests/auth-test.c72
-rw-r--r--tests/get.c198
-rw-r--r--tests/proxy-test.c2
-rw-r--r--tests/redirect-test.c3
-rw-r--r--tests/resources/atom.xml35
-rw-r--r--tests/resources/home.gifbin0 -> 995 bytes
-rw-r--r--tests/resources/mbox64
-rw-r--r--tests/resources/rss20.xml26
-rw-r--r--tests/resources/test.html10
-rw-r--r--tests/sniffing-test.c481
54 files changed, 2861 insertions, 1081 deletions
diff --git a/.gitignore b/.gitignore
index b0cd3a47..1bb227d1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,7 @@ tests/redirect-test
tests/server-auth-test
tests/simple-httpd
tests/simple-proxy
+tests/sniffing-test
tests/ssl-test
tests/streaming-test
tests/timeout-test
diff --git a/HACKING b/HACKING
new file mode 100644
index 00000000..51269e51
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,66 @@
+CODE STYLE
+----------
+
+Please use the style used by the rest of the code. Among other things,
+this means:
+
+ * Tabs, not spaces, for indentation
+
+ * Put spaces:
+ * around binary operators
+ * between if/while/for/switch and "("
+ * between function name and "("
+ * between ")" and "{"
+ * after ","
+
+ * if/for/while bodies:
+
+ * Single-line bodies should (a) be on their own line, and (b)
+ not have braces around them
+
+ * Multi-line bodies should have braces around them, even if
+ the body is only a single statement and the braces are not
+ syntactically necessary.
+
+ * Eg:
+
+ for (i = 0; i < len; i++) {
+ if (find (i, something))
+ break;
+ else {
+ function_with_big_name (i, something,
+ something_else);
+ }
+ }
+
+ * C89, not C99. (In particular, don't declare variables in the
+ middle of blocks.)
+
+ * Do not use gint, gchar, glong, and gshort. (Other g-types, such
+ as gpointer and the unsigned types are fine.)
+
+CORRECTNESS
+-----------
+
+ * libsoup builds with lots of -W options by default, and should
+ not print any warnings while compiling (unless they're caused by
+ #included files from other projects, eg, proxy.h). You can use
+ "make > /dev/null" to do a full compile showing only the
+ warnings/errors, to make sure your patch does not introduce any
+ more.
+
+ * There are a number of regression tests in the tests/ directory.
+ Running "make check" will run all of them (or at least, all of
+ the ones that it can run based on what software you have
+ installed. Eg, some tests require apache to be installed.) You
+ should run "make check" before submitting a patch that could
+ potentially change libsoup's behavior. ("make check" will warn
+ you if it was not able to run all of the tests. If you are
+ making extensive changes, or changing very low-level functions,
+ you may want to install all of the optional pieces so you can
+ run all of the regression tests.)
+
+ * libsoup ought to build correctly from outside its source tree,
+ so if you make large changes to the Makefiles, try a "make
+ distcheck" to verify that an out-of-source-tree build still
+ works.
diff --git a/NEWS b/NEWS
index 26a8f0cc..6c3524b5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,86 @@
+Changes in libsoup from 2.27.2 to 2.27.4:
+
+ * Added SoupContentSniffer and the "content-sniffed" signal on
+ SoupMessage, to do Content-Type sniffing per the HTML5 /
+ draft-abarth-mime-sniff algorithm. [#572589, Gustavo Noronha
+ Silva]
+
+ * Updated the earlier SoupSession timeout fixes ([#574414],
+ [#578928]) so that async connect() also times out [#588177,
+ Mark Nauwelaerts] and SSL works on Windows again [#587910,
+ Fridrich Strba].
+
+ * Fixed the behavior on a 301 response to a POST to match
+ real-world usage rather than what the spec says. (We were
+ doing the right thing on 302 and 303, but had missed 301.)
+ [#586692]
+
+ * Changed configure so that if GNUTLS isn't found then it
+ errors out, rather than silently building an SSL-less
+ libsoup. Configure with --disable-ssl if you actually don't
+ want SSL. [#584955]
+
+Changes in libsoup from 2.27.1 to 2.27.2:
+
+ * Replaced SoupProxyResolver with SoupProxyURIResolver, which
+ is a bit simpler, works with non-HTTP URIs (and so could be
+ used by gvfsd-ftp) and supports proxy auth correctly.
+ [#580051]
+
+ * Fixed SoupSession to not try to resolve http server
+ hostnames when it's just going to pass the hostname off to a
+ proxy server anyway. This fixes things on hosts that use a
+ proxy for everything and have no working DNS config
+ [#577532] and also makes WebKitGTK behave more like other
+ browsers in terms of per-host connection limits (we now
+ limit connections based on hostname rather than on IP
+ address).
+
+ We also no longer set the AI_CANONNAME flag when calling
+ getaddrinfo(), which saves us a little bit of unnecessary
+ network traffic. [Pointed out by Christophe Gillette on the
+ mailing list.]
+
+ * libsoup now always uses SSL 3.0 (not TLS 1.0 or 1.1) for
+ https URIs, to work around problems with older servers that
+ don't implement the (apparently quite confusing) TLS/SSL
+ compatibility rules correctly. Makes a bunch of
+ previously-inaccessible sites now accessible in WebKitGTK
+ (notably PayPal) [#581342]. Will eventually be revisited, to
+ first try TLS 1.1 and fall back if that fails.
+
+ * Fixed Digest auth to (recent) Apple CalDAV servers.
+ [#583091]
+
+ * Changed the way the SoupSession "authenticate" signal works
+ a bit. We now never emit "authenticate" before sending a
+ request, even if we know for sure that it's going to fail,
+ because this makes the semantics of the authenticate handler
+ too complicated (and because we'll only get into this
+ situation if a previous call to the authenticate handler
+ failed anyway). Fixes problems in WebKitGTK when you cancel
+ a password dialog, and then later try to load the page
+ again. [#583462, mostly figured out by Gustavo Noronha
+ Silva].
+
+ * Fixed a bug in the CRLF-vs-LF patch (#571283) that caused
+ libsoup to fail to parse the response headers (returning
+ SOUP_STATUS_MALFORMED) if a CR LF got split across two
+ read()s. [#582002]
+
+ * Allow using PUT in soup_form_request_for_data(), to work
+ with certain broken web APIs. [#581860, Ross Burton]. Also,
+ fixed a problem with empty POST bodies that made some parts
+ of gmail not work in WebKitGTK.
+
+ * Applied some minor bugfixes to configure.in and autogen.sh
+ [#583911, #583942]. Fixed configure.in to not use gcc
+ warning options that the installed version of gcc doesn't
+ recognize [#578851].
+
+ * Added G_GNUC_NULL_TERMINATED and G_GNUC_PRINTF to a few
+ methods that should have had them. [#581754, Ross Burton]
+
Changes in libsoup from 2.26.1 to 2.27.1:
* SOUP_SESSION_TIMEOUT now works properly with
@@ -27,7 +110,7 @@ Changes in libsoup from 2.26.1 to 2.27.1:
* Fix a crash when cancelling a message from a "restarted"
handler, and updated a regression test to notice the
- underlying cause. [#380193]
+ underlying cause. [#580193]
* Completing the API updates for #576760 from 2.26.1,
soup_message_headers_get() is now marked deprecated in favor
diff --git a/autogen.sh b/autogen.sh
index 8b977268..78d640dd 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -5,7 +5,7 @@ srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
PKG_NAME="libsoup"
-REQUIRED_AUTOMAKE_VERSION=1.6
+REQUIRED_AUTOMAKE_VERSION=1.9
(test -f $srcdir/configure.in \
&& test -f $srcdir/libsoup.doap \
diff --git a/configure.in b/configure.in
index 3ed1a4b1..60616052 100644
--- a/configure.in
+++ b/configure.in
@@ -3,7 +3,7 @@ dnl *** Initialize automake and set version ***
dnl *******************************************
AC_PREREQ(2.53)
-AC_INIT(libsoup, 2.27.1)
+AC_INIT(libsoup, 2.27.4)
AC_CONFIG_SRCDIR(libsoup-2.4.pc.in)
AM_INIT_AUTOMAKE([foreign])
@@ -143,22 +143,23 @@ dnl **********************************
dnl Allow autogening even without AM_PATH_LIBGCRYPT available
m4_ifdef([AM_PATH_LIBGCRYPT],,[m4_define(AM_PATH_LIBGCRYPT,)])
AC_ARG_ENABLE(ssl,
- AS_HELP_STRING([--enable-ssl], [Turn on Secure Sockets Layer support (default=yes)]),,
+ AS_HELP_STRING([--disable-ssl], [Disable SSL/TLS support (not recommended)]),,
enable_ssl=auto)
+have_ssl=no
if test "$enable_ssl" != "no"; then
PKG_CHECK_MODULES(LIBGNUTLS, gnutls,
[AM_PATH_LIBGCRYPT([], have_ssl=yes, have_ssl=no)], have_ssl=no)
- if test "$have_ssl" = "yes"; then
- AC_DEFINE(HAVE_SSL, 1, [Defined if you have SSL support])
- SSL_REQUIREMENT="gnutls"
+fi
+if test "$have_ssl" = "yes"; then
+ AC_DEFINE(HAVE_SSL, 1, [Defined if you have SSL support])
+ SSL_REQUIREMENT="gnutls"
+else
+ if test "$enable_ssl" = "no"; then
+ AC_MSG_WARN(Disabling SSL support);
else
- if test "$enable_ssl" = "auto"; then
- AC_MSG_WARN(Disabling SSL support);
- enable_ssl=no;
- else
- AC_MSG_ERROR(Could not configure SSL support);
- fi
+ AC_MSG_ERROR([Could not configure SSL support.
+Pass "--disable-ssl" if you really want to build without SSL support]);
fi
fi
@@ -241,8 +242,20 @@ if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
-Wall -Wstrict-prototypes -Wmissing-declarations \
-Wmissing-prototypes -Wnested-externs -Wpointer-arith \
-Wdeclaration-after-statement -Wformat=2 -Winit-self \
- -Wmissing-include-dirs -Wundef -Waggregate-return \
- -Wmissing-format-attribute"
+ -Waggregate-return -Wmissing-format-attribute"
+
+ for option in -Wmissing-include-dirs -Wundef; do
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $option"
+ AC_MSG_CHECKING([whether gcc understands $option])
+ AC_TRY_COMPILE([], [],
+ has_option=yes,
+ has_option=no,)
+ AC_MSG_RESULT($has_option)
+ if test $has_option = no; then
+ CFLAGS="$SAVE_CFLAGS"
+ fi
+ done
fi
if test "$os_win32" != yes; then
@@ -317,7 +330,7 @@ if test "$APACHE_HTTPD" != "no" -a -n "$APACHE_MODULE_DIR" -a -n "$APACHE_SSL_MO
fi
else
have_apache=0
- if test "$APACHE_HTTPD" == "no" -o -z "$APACHE_MODULE_DIR"; then
+ if test "$APACHE_HTTPD" = "no" -o -z "$APACHE_MODULE_DIR"; then
MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES apache"
else
MISSING_REGRESSION_TEST_PACKAGES="$MISSING_REGRESSION_TEST_PACKAGES mod_ssl"
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index ef05b729..ad9a7359 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -56,6 +56,7 @@ soup_headers = \
soup-auth-domain-basic.h \
soup-auth-domain-digest.h \
soup-cache.h \
+ soup-content-sniffer.h \
soup-cookie.h \
soup-cookie-jar.h \
soup-cookie-jar-text.h \
@@ -71,6 +72,7 @@ soup_headers = \
soup-multipart.h \
soup-portability.h \
soup-proxy-resolver.h \
+ soup-proxy-uri-resolver.h \
soup-server.h \
soup-session.h \
soup-session-async.h \
@@ -120,6 +122,7 @@ libsoup_2_4_la_SOURCES = \
soup-cache.c \
soup-connection.h \
soup-connection.c \
+ soup-content-sniffer.c \
soup-cookie.c \
soup-cookie-jar.c \
soup-cookie-jar-text.c \
@@ -148,6 +151,7 @@ libsoup_2_4_la_SOURCES = \
soup-proxy-resolver.c \
soup-proxy-resolver-static.h \
soup-proxy-resolver-static.c \
+ soup-proxy-uri-resolver.c \
soup-server.c \
soup-session.c \
soup-session-async.c \
diff --git a/libsoup/soup-auth-digest.c b/libsoup/soup-auth-digest.c
index 665bac76..4fb3f815 100644
--- a/libsoup/soup-auth-digest.c
+++ b/libsoup/soup-auth-digest.c
@@ -421,7 +421,7 @@ get_authorization (SoupAuth *auth, SoupMessage *msg)
{
SoupAuthDigestPrivate *priv = SOUP_AUTH_DIGEST_GET_PRIVATE (auth);
char response[33], *token;
- char *url;
+ char *url, *algorithm;
GString *out;
SoupURI *uri;
@@ -444,6 +444,10 @@ get_authorization (SoupAuth *auth, SoupMessage *msg)
g_string_append (out, ", ");
soup_header_g_string_append_param (out, "uri", url);
g_string_append (out, ", ");
+ algorithm = soup_auth_digest_get_algorithm (priv->algorithm);
+ soup_header_g_string_append_param (out, "algorithm", algorithm);
+ g_free (algorithm);
+ g_string_append (out, ", ");
soup_header_g_string_append_param (out, "response", response);
if (priv->opaque) {
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
index 92ffae88..5d1d1bcd 100644
--- a/libsoup/soup-auth-manager.c
+++ b/libsoup/soup-auth-manager.c
@@ -16,9 +16,11 @@
#include "soup-headers.h"
#include "soup-marshal.h"
#include "soup-message-private.h"
+#include "soup-message-queue.h"
#include "soup-path-map.h"
#include "soup-session.h"
#include "soup-session-feature.h"
+#include "soup-session-private.h"
#include "soup-uri.h"
static void soup_auth_manager_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
@@ -53,7 +55,7 @@ typedef struct {
#define SOUP_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate))
typedef struct {
- SoupAddress *addr;
+ SoupURI *uri;
SoupPathMap *auth_realms; /* path -> scheme:realm */
GHashTable *auths; /* scheme:realm -> SoupAuth */
} SoupAuthHost;
@@ -64,8 +66,8 @@ soup_auth_manager_init (SoupAuthManager *manager)
SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
priv->auth_types = g_ptr_array_new ();
- priv->auth_hosts = g_hash_table_new (soup_address_hash_by_name,
- soup_address_equal_by_name);
+ priv->auth_hosts = g_hash_table_new (soup_uri_host_hash,
+ soup_uri_host_equal);
}
static gboolean
@@ -78,7 +80,7 @@ foreach_free_host (gpointer key, gpointer value, gpointer data)
if (host->auths)
g_hash_table_destroy (host->auths);
- g_object_unref (host->addr);
+ soup_uri_free (host->uri);
g_slice_free (SoupAuthHost, host);
return TRUE;
@@ -318,15 +320,15 @@ static SoupAuthHost *
get_auth_host_for_message (SoupAuthManagerPrivate *priv, SoupMessage *msg)
{
SoupAuthHost *host;
- SoupAddress *addr = soup_message_get_address (msg);
+ SoupURI *uri = soup_message_get_uri (msg);
- host = g_hash_table_lookup (priv->auth_hosts, addr);
+ host = g_hash_table_lookup (priv->auth_hosts, uri);
if (host)
return host;
host = g_slice_new0 (SoupAuthHost);
- host->addr = g_object_ref (addr);
- g_hash_table_insert (priv->auth_hosts, host->addr, host);
+ host->uri = soup_uri_copy_host (uri);
+ g_hash_table_insert (priv->auth_hosts, host->uri, host);
return host;
}
@@ -354,7 +356,7 @@ lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
static gboolean
authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
SoupMessage *msg, gboolean prior_auth_failed,
- gboolean proxy)
+ gboolean proxy, gboolean can_interact)
{
SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
SoupURI *uri;
@@ -363,24 +365,32 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
return TRUE;
if (proxy) {
- g_object_get (G_OBJECT (priv->session),
- SOUP_SESSION_PROXY_URI, &uri,
- NULL);
- /* FIXME: temporary workaround for proxy auth brokenness */
+ SoupMessageQueue *queue;
+ SoupMessageQueueItem *item;
+
+ queue = soup_session_get_queue (priv->session);
+ item = soup_message_queue_lookup (queue, msg);
+ if (item) {
+ uri = soup_connection_get_proxy_uri (item->conn);
+ soup_message_queue_item_unref (item);
+ } else
+ uri = NULL;
+
if (!uri)
return FALSE;
} else
- uri = soup_uri_copy (soup_message_get_uri (msg));
+ uri = soup_message_get_uri (msg);
if (uri->password && !prior_auth_failed) {
soup_auth_authenticate (auth, uri->user, uri->password);
- soup_uri_free (uri);
return TRUE;
}
- soup_uri_free (uri);
- soup_auth_manager_emit_authenticate (manager, msg, auth,
- prior_auth_failed);
+ if (can_interact) {
+ soup_auth_manager_emit_authenticate (manager, msg, auth,
+ prior_auth_failed);
+ }
+
return soup_auth_is_authenticated (auth);
}
@@ -449,7 +459,7 @@ update_auth (SoupMessage *msg, gpointer manager)
/* If we need to authenticate, try to do it. */
authenticate_auth (manager, auth, msg,
- prior_auth_failed, FALSE);
+ prior_auth_failed, FALSE, TRUE);
}
static void
@@ -484,7 +494,7 @@ update_proxy_auth (SoupMessage *msg, gpointer manager)
/* If we need to authenticate, try to do it. */
authenticate_auth (manager, priv->proxy_auth, msg,
- prior_auth_failed, TRUE);
+ prior_auth_failed, TRUE, TRUE);
}
static void
@@ -525,12 +535,12 @@ request_started (SoupSessionFeature *feature, SoupSession *session,
SoupAuth *auth;
auth = lookup_auth (priv, msg);
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE))
+ if (!auth || !authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE))
auth = NULL;
soup_message_set_auth (msg, auth);
auth = priv->proxy_auth;
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE))
+ if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE))
auth = NULL;
soup_message_set_proxy_auth (msg, auth);
}
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index 99ec6c22..0d731c00 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -27,31 +27,18 @@
#include "soup-ssl.h"
#include "soup-uri.h"
-typedef enum {
- SOUP_CONNECTION_MODE_DIRECT,
- SOUP_CONNECTION_MODE_PROXY,
- SOUP_CONNECTION_MODE_TUNNEL
-} SoupConnectionMode;
-
typedef struct {
SoupSocket *socket;
- /* proxy_addr is the address of the proxy server we are
- * connected to, if any. server_addr is the address of the
- * origin server. conn_addr is the uri of the host we are
- * actually directly connected to, which will be proxy_addr if
- * there's a proxy and server_addr if not.
- */
- SoupAddress *proxy_addr, *server_addr, *conn_addr;
+ SoupAddress *remote_addr, *tunnel_addr;
+ SoupURI *proxy_uri;
gpointer ssl_creds;
- SoupConnectionMode mode;
-
GMainContext *async_context;
SoupMessage *cur_req;
+ SoupConnectionState state;
time_t last_used;
- gboolean connected, in_use;
guint io_timeout, idle_timeout;
GSource *idle_timeout_src;
} SoupConnectionPrivate;
@@ -60,9 +47,7 @@ typedef struct {
G_DEFINE_TYPE (SoupConnection, soup_connection, G_TYPE_OBJECT)
enum {
- CONNECT_RESULT,
DISCONNECTED,
- REQUEST_STARTED,
LAST_SIGNAL
};
@@ -71,8 +56,9 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
- PROP_SERVER_ADDRESS,
- PROP_PROXY_ADDRESS,
+ PROP_REMOTE_ADDRESS,
+ PROP_TUNNEL_ADDRESS,
+ PROP_PROXY_URI,
PROP_SSL_CREDS,
PROP_ASYNC_CONTEXT,
PROP_TIMEOUT,
@@ -87,7 +73,6 @@ static void get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec);
static void stop_idle_timer (SoupConnectionPrivate *priv);
-static void send_request (SoupConnection *conn, SoupMessage *req);
static void clear_current_request (SoupConnection *conn);
static void
@@ -101,10 +86,10 @@ finalize (GObject *object)
{
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
- if (priv->proxy_addr)
- g_object_unref (priv->proxy_addr);
- if (priv->server_addr)
- g_object_unref (priv->server_addr);
+ if (priv->remote_addr)
+ g_object_unref (priv->remote_addr);
+ if (priv->tunnel_addr)
+ g_object_unref (priv->tunnel_addr);
if (priv->async_context)
g_main_context_unref (priv->async_context);
@@ -135,9 +120,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
g_type_class_add_private (connection_class, sizeof (SoupConnectionPrivate));
- /* virtual method definition */
- connection_class->send_request = send_request;
-
/* virtual method override */
object_class->dispose = dispose;
object_class->finalize = finalize;
@@ -145,16 +127,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
object_class->get_property = get_property;
/* signals */
-
- signals[CONNECT_RESULT] =
- g_signal_new ("connect_result",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (SoupConnectionClass, connect_result),
- NULL, NULL,
- soup_marshal_NONE__INT,
- G_TYPE_NONE, 1,
- G_TYPE_INT);
signals[DISCONNECTED] =
g_signal_new ("disconnected",
G_OBJECT_CLASS_TYPE (object_class),
@@ -163,32 +135,30 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
NULL, NULL,
soup_marshal_NONE__NONE,
G_TYPE_NONE, 0);
- signals[REQUEST_STARTED] =
- g_signal_new ("request-started",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (SoupConnectionClass, request_started),
- NULL, NULL,
- soup_marshal_NONE__OBJECT,
- G_TYPE_NONE, 1,
- SOUP_TYPE_MESSAGE);
/* properties */
g_object_class_install_property (
- object_class, PROP_SERVER_ADDRESS,
- g_param_spec_object (SOUP_CONNECTION_SERVER_ADDRESS,
- "Server address",
- "The address of the HTTP origin server for this connection",
+ object_class, PROP_REMOTE_ADDRESS,
+ g_param_spec_object (SOUP_CONNECTION_REMOTE_ADDRESS,
+ "Remote address",
+ "The address of the HTTP or proxy server",
SOUP_TYPE_ADDRESS,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
- object_class, PROP_PROXY_ADDRESS,
- g_param_spec_object (SOUP_CONNECTION_PROXY_ADDRESS,
- "Proxy address",
- "The address of the HTTP Proxy to use for this connection",
+ object_class, PROP_TUNNEL_ADDRESS,
+ g_param_spec_object (SOUP_CONNECTION_TUNNEL_ADDRESS,
+ "Tunnel address",
+ "The address of the HTTPS server this tunnel connects to",
SOUP_TYPE_ADDRESS,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
+ object_class, PROP_PROXY_URI,
+ g_param_spec_boxed (SOUP_CONNECTION_PROXY_URI,
+ "Proxy URI",
+ "URI of the HTTP proxy this connection connects to",
+ SOUP_TYPE_URI,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (
object_class, PROP_SSL_CREDS,
g_param_spec_pointer (SOUP_CONNECTION_SSL_CREDENTIALS,
"SSL credentials",
@@ -217,32 +187,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
}
-/**
- * soup_connection_new:
- * @propname1: name of first property to set
- * @...: value of @propname1, followed by additional property/value pairs
- *
- * Creates an HTTP connection. There are three possibilities:
- *
- * If you set %SOUP_CONNECTION_SERVER_ADDRESS and not
- * %SOUP_CONNECTION_PROXY_ADDRESS, this will be a direct connection to
- * the indicated origin server.
- *
- * If you set %SOUP_CONNECTION_PROXY_ADDRESS and not
- * %SOUP_CONNECTION_SSL_CREDENTIALS, this will be a standard proxy
- * connection, which can be used for requests to multiple origin
- * servers.
- *
- * If you set %SOUP_CONNECTION_SERVER_ADDRESS,
- * %SOUP_CONNECTION_PROXY_ADDRESS, and
- * %SOUP_CONNECTION_SSL_CREDENTIALS, this will be a tunnel through the
- * proxy to the origin server.
- *
- * You must call soup_connection_connect_async() or
- * soup_connection_connect_sync() to connect it after creating it.
- *
- * Return value: the new connection (not yet ready for use).
- **/
SoupConnection *
soup_connection_new (const char *propname1, ...)
{
@@ -264,31 +208,20 @@ set_property (GObject *object, guint prop_id,
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
switch (prop_id) {
- case PROP_SERVER_ADDRESS:
- priv->server_addr = g_value_dup_object (value);
- goto changed_connection;
-
- case PROP_PROXY_ADDRESS:
- priv->proxy_addr = g_value_dup_object (value);
- goto changed_connection;
-
+ case PROP_REMOTE_ADDRESS:
+ priv->remote_addr = g_value_dup_object (value);
+ break;
+ case PROP_TUNNEL_ADDRESS:
+ priv->tunnel_addr = g_value_dup_object (value);
+ break;
+ case PROP_PROXY_URI:
+ if (priv->proxy_uri)
+ soup_uri_free (priv->proxy_uri);
+ priv->proxy_uri = g_value_dup_boxed (value);
+ break;
case PROP_SSL_CREDS:
priv->ssl_creds = g_value_get_pointer (value);
- goto changed_connection;
-
- changed_connection:
- if (priv->proxy_addr) {
- priv->conn_addr = priv->proxy_addr;
- if (priv->server_addr && priv->ssl_creds)
- priv->mode = SOUP_CONNECTION_MODE_TUNNEL;
- else
- priv->mode = SOUP_CONNECTION_MODE_PROXY;
- } else {
- priv->conn_addr = priv->server_addr;
- priv->mode = SOUP_CONNECTION_MODE_DIRECT;
- }
break;
-
case PROP_ASYNC_CONTEXT:
priv->async_context = g_value_get_pointer (value);
if (priv->async_context)
@@ -313,11 +246,14 @@ get_property (GObject *object, guint prop_id,
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
switch (prop_id) {
- case PROP_SERVER_ADDRESS:
- g_value_set_object (value, priv->server_addr);
+ case PROP_REMOTE_ADDRESS:
+ g_value_set_object (value, priv->remote_addr);
break;
- case PROP_PROXY_ADDRESS:
- g_value_set_object (value, priv->proxy_addr);
+ case PROP_TUNNEL_ADDRESS:
+ g_value_set_object (value, priv->tunnel_addr);
+ break;
+ case PROP_PROXY_URI:
+ g_value_set_boxed (value, priv->proxy_uri);
break;
case PROP_SSL_CREDS:
g_value_set_pointer (value, priv->ssl_creds);
@@ -375,7 +311,9 @@ set_current_request (SoupConnectionPrivate *priv, SoupMessage *req)
soup_message_set_io_status (req, SOUP_MESSAGE_IO_STATUS_RUNNING);
priv->cur_req = req;
- priv->in_use = TRUE;
+ if (priv->state == SOUP_CONNECTION_IDLE ||
+ req->method != SOUP_METHOD_CONNECT)
+ priv->state = SOUP_CONNECTION_IN_USE;
g_object_add_weak_pointer (G_OBJECT (req), (gpointer)&priv->cur_req);
}
@@ -384,7 +322,8 @@ clear_current_request (SoupConnection *conn)
{
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- priv->in_use = FALSE;
+ if (priv->state == SOUP_CONNECTION_IN_USE)
+ priv->state = SOUP_CONNECTION_IDLE;
start_idle_timer (conn);
if (priv->cur_req) {
SoupMessage *cur_req = priv->cur_req;
@@ -408,144 +347,41 @@ socket_disconnected (SoupSocket *sock, gpointer conn)
soup_connection_disconnect (conn);
}
-static SoupMessage *
-connect_message (SoupConnectionPrivate *priv)
-{
- SoupURI *uri;
- SoupMessage *msg;
-
- uri = soup_uri_new (NULL);
- soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
- soup_uri_set_host (uri, soup_address_get_name (priv->server_addr));
- soup_uri_set_port (uri, soup_address_get_port (priv->server_addr));
- soup_uri_set_path (uri, "");
- msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
- soup_uri_free (uri);
-
- return msg;
-}
-
-static void
-tunnel_connect_finished (SoupMessage *msg, gpointer user_data)
-{
- SoupConnection *conn = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- guint status = msg->status_code;
-
- clear_current_request (conn);
-
- if (SOUP_STATUS_IS_SUCCESSFUL (status) && priv->ssl_creds) {
- const char *server_name =
- soup_address_get_name (priv->server_addr);
- if (soup_socket_start_proxy_ssl (priv->socket, server_name,
- NULL))
- priv->connected = TRUE;
- else
- status = SOUP_STATUS_SSL_FAILED;
- } else if (SOUP_STATUS_IS_REDIRECTION (status)) {
- /* Oops, the proxy thinks we're a web browser. */
- status = SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED;
- }
-
- if (priv->proxy_addr)
- status = soup_status_proxify (status);
- g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
- g_object_unref (msg);
-}
-
-static void
-tunnel_connect_restarted (SoupMessage *msg, gpointer user_data)
-{
- SoupConnection *conn = user_data;
- guint status = msg->status_code;
-
- /* We only allow one restart: if another one happens, treat
- * it as "finished".
- */
- g_signal_handlers_disconnect_by_func (msg, tunnel_connect_restarted, conn);
- g_signal_connect (msg, "restarted",
- G_CALLBACK (tunnel_connect_finished), conn);
-
- if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
- /* Our parent session has handled the authentication
- * and attempted to restart the message.
- */
- if (soup_message_is_keepalive (msg)) {
- /* Connection is still open, so just send the
- * message again.
- */
- soup_connection_send_request (conn, msg);
- } else {
- /* Tell the session to try again. */
- soup_message_set_status (msg, SOUP_STATUS_TRY_AGAIN);
- soup_message_finished (msg);
- }
- } else
- soup_message_finished (msg);
-}
+typedef struct {
+ SoupConnection *conn;
+ SoupConnectionCallback callback;
+ gpointer callback_data;
+} SoupConnectionAsyncConnectData;
static void
socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
{
- SoupConnection *conn = user_data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ SoupConnectionAsyncConnectData *data = user_data;
+ SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
if (!SOUP_STATUS_IS_SUCCESSFUL (status))
goto done;
- if (priv->mode == SOUP_CONNECTION_MODE_DIRECT && priv->ssl_creds) {
+ if (priv->ssl_creds && !priv->tunnel_addr) {
if (!soup_socket_start_ssl (sock, NULL)) {
status = SOUP_STATUS_SSL_FAILED;
goto done;
}
}
- if (priv->mode == SOUP_CONNECTION_MODE_TUNNEL) {
- SoupMessage *connect_msg = connect_message (priv);
-
- g_signal_connect (connect_msg, "restarted",
- G_CALLBACK (tunnel_connect_restarted), conn);
- g_signal_connect (connect_msg, "finished",
- G_CALLBACK (tunnel_connect_finished), conn);
-
- soup_connection_send_request (conn, connect_msg);
- return;
- }
+ g_signal_connect (priv->socket, "disconnected",
+ G_CALLBACK (socket_disconnected), data->conn);
- priv->connected = TRUE;
- start_idle_timer (conn);
+ priv->state = SOUP_CONNECTION_IDLE;
+ start_idle_timer (data->conn);
done:
- if (priv->proxy_addr)
- status = soup_status_proxify (status);
- g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
-}
-
-/* from soup-misc.c... will eventually go away */
-guint soup_signal_connect_once (gpointer instance, const char *detailed_signal,
- GCallback c_handler, gpointer data);
-
-static void
-address_resolved (SoupAddress *addr, guint status, gpointer data)
-{
- SoupConnection *conn = data;
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
-
- if (status != SOUP_STATUS_OK) {
- socket_connect_result (NULL, status, conn);
- return;
+ if (data->callback) {
+ if (priv->proxy_uri != NULL)
+ status = soup_status_proxify (status);
+ data->callback (data->conn, status, data->callback_data);
}
-
- priv->socket =
- soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, addr,
- SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
- SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
- SOUP_SOCKET_TIMEOUT, priv->io_timeout,
- NULL);
- soup_socket_connect_async (priv->socket, NULL,
- socket_connect_result, conn);
- g_signal_connect (priv->socket, "disconnected",
- G_CALLBACK (socket_disconnected), conn);
+ g_slice_free (SoupConnectionAsyncConnectData, data);
}
/**
@@ -561,19 +397,28 @@ soup_connection_connect_async (SoupConnection *conn,
SoupConnectionCallback callback,
gpointer user_data)
{
+ SoupConnectionAsyncConnectData *data;
SoupConnectionPrivate *priv;
g_return_if_fail (SOUP_IS_CONNECTION (conn));
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
g_return_if_fail (priv->socket == NULL);
- if (callback) {
- soup_signal_connect_once (conn, "connect_result",
- G_CALLBACK (callback), user_data);
- }
+ priv->state = SOUP_CONNECTION_CONNECTING;
+
+ data = g_slice_new (SoupConnectionAsyncConnectData);
+ data->conn = conn;
+ data->callback = callback;
+ data->callback_data = user_data;
- soup_address_resolve_async (priv->conn_addr, priv->async_context, NULL,
- address_resolved, conn);
+ priv->socket =
+ soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
+ SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
+ SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
+ SOUP_SOCKET_TIMEOUT, priv->io_timeout,
+ NULL);
+ soup_socket_connect_async (priv->socket, NULL,
+ socket_connect_result, data);
}
/**
@@ -594,12 +439,10 @@ soup_connection_connect_sync (SoupConnection *conn)
priv = SOUP_CONNECTION_GET_PRIVATE (conn);
g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
- status = soup_address_resolve_sync (priv->conn_addr, NULL);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status))
- goto fail;
+ priv->state = SOUP_CONNECTION_CONNECTING;
priv->socket =
- soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->conn_addr,
+ soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, priv->remote_addr,
SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_creds,
SOUP_SOCKET_FLAG_NONBLOCKING, FALSE,
SOUP_SOCKET_TIMEOUT, priv->io_timeout,
@@ -613,43 +456,15 @@ soup_connection_connect_sync (SoupConnection *conn)
g_signal_connect (priv->socket, "disconnected",
G_CALLBACK (socket_disconnected), conn);
- if (priv->mode == SOUP_CONNECTION_MODE_DIRECT && priv->ssl_creds) {
+ if (priv->ssl_creds && !priv->tunnel_addr) {
if (!soup_socket_start_ssl (priv->socket, NULL)) {
status = SOUP_STATUS_SSL_FAILED;
goto fail;
}
}
- if (priv->mode == SOUP_CONNECTION_MODE_TUNNEL) {
- SoupMessage *connect_msg = connect_message (priv);
-
- soup_connection_send_request (conn, connect_msg);
- status = connect_msg->status_code;
-
- if (status == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
- SOUP_MESSAGE_IS_STARTING (connect_msg)) {
- if (soup_message_is_keepalive (connect_msg)) {
- /* Try once more */
- soup_connection_send_request (conn, connect_msg);
- status = connect_msg->status_code;
- } else
- status = SOUP_STATUS_TRY_AGAIN;
- }
-
- g_object_unref (connect_msg);
-
- if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- const char *server_name =
- soup_address_get_name (priv->server_addr);
- if (!soup_socket_start_proxy_ssl (priv->socket,
- server_name,
- NULL))
- status = SOUP_STATUS_SSL_FAILED;
- }
- }
-
if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
- priv->connected = TRUE;
+ priv->state = SOUP_CONNECTION_IDLE;
start_idle_timer (conn);
} else {
fail:
@@ -659,12 +474,36 @@ soup_connection_connect_sync (SoupConnection *conn)
}
}
- if (priv->proxy_addr)
+ if (priv->proxy_uri != NULL)
status = soup_status_proxify (status);
- g_signal_emit (conn, signals[CONNECT_RESULT], 0, status);
return status;
}
+SoupAddress *
+soup_connection_get_tunnel_addr (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ return priv->tunnel_addr;
+}
+
+gboolean
+soup_connection_start_ssl (SoupConnection *conn)
+{
+ SoupConnectionPrivate *priv;
+ const char *server_name;
+
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+
+ server_name = soup_address_get_name (priv->tunnel_addr ?
+ priv->tunnel_addr :
+ priv->remote_addr);
+ return soup_socket_start_proxy_ssl (priv->socket, server_name, NULL);
+}
/**
* soup_connection_disconnect:
@@ -691,10 +530,10 @@ soup_connection_disconnect (SoupConnection *conn)
priv->socket = NULL;
/* Don't emit "disconnected" if we aren't yet connected */
- if (!priv->connected)
+ if (priv->state < SOUP_CONNECTION_IDLE)
return;
- priv->connected = FALSE;
+ priv->state = SOUP_CONNECTION_DISCONNECTED;
if (priv->cur_req &&
priv->cur_req->status_code == SOUP_STATUS_IO_ERROR &&
@@ -748,21 +587,33 @@ soup_connection_get_socket (SoupConnection *conn)
return SOUP_CONNECTION_GET_PRIVATE (conn)->socket;
}
-/**
- * soup_connection_is_in_use:
- * @conn: a connection
- *
- * Tests whether or not @conn is in use.
- *
- * Return value: %TRUE if there is currently a request being processed
- * on @conn.
- **/
-gboolean
-soup_connection_is_in_use (SoupConnection *conn)
+SoupURI *
+soup_connection_get_proxy_uri (SoupConnection *conn)
{
- g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL);
- return SOUP_CONNECTION_GET_PRIVATE (conn)->in_use;
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->proxy_uri;
+}
+
+SoupConnectionState
+soup_connection_get_state (SoupConnection *conn)
+{
+ g_return_val_if_fail (SOUP_IS_CONNECTION (conn),
+ SOUP_CONNECTION_DISCONNECTED);
+
+ return SOUP_CONNECTION_GET_PRIVATE (conn)->state;
+}
+
+void
+soup_connection_set_state (SoupConnection *conn, SoupConnectionState state)
+{
+ g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ g_return_if_fail (state > SOUP_CONNECTION_NEW &&
+ state < SOUP_CONNECTION_DISCONNECTED);
+
+ SOUP_CONNECTION_GET_PRIVATE (conn)->state = state;
+ if (state == SOUP_CONNECTION_IDLE)
+ clear_current_request (conn);
}
/**
@@ -782,20 +633,6 @@ soup_connection_last_used (SoupConnection *conn)
return SOUP_CONNECTION_GET_PRIVATE (conn)->last_used;
}
-static void
-send_request (SoupConnection *conn, SoupMessage *req)
-{
- SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
-
- if (req != priv->cur_req) {
- set_current_request (priv, req);
- g_signal_emit (conn, signals[REQUEST_STARTED], 0, req);
- }
-
- soup_message_send_request (req, priv->socket, conn,
- priv->mode == SOUP_CONNECTION_MODE_PROXY);
-}
-
/**
* soup_connection_send_request:
* @conn: a #SoupConnection
@@ -807,42 +644,15 @@ send_request (SoupConnection *conn, SoupMessage *req)
void
soup_connection_send_request (SoupConnection *conn, SoupMessage *req)
{
- g_return_if_fail (SOUP_IS_CONNECTION (conn));
- g_return_if_fail (SOUP_IS_MESSAGE (req));
- g_return_if_fail (SOUP_CONNECTION_GET_PRIVATE (conn)->socket != NULL);
-
- SOUP_CONNECTION_GET_CLASS (conn)->send_request (conn, req);
-}
-
-/**
- * soup_connection_reserve:
- * @conn: a #SoupConnection
- *
- * Marks @conn as "in use" despite not actually having a message on
- * it. This is used by #SoupSession to keep it from accidentally
- * trying to queue two messages on the same connection from different
- * threads at the same time.
- **/
-void
-soup_connection_reserve (SoupConnection *conn)
-{
- g_return_if_fail (SOUP_IS_CONNECTION (conn));
-
- SOUP_CONNECTION_GET_PRIVATE (conn)->in_use = TRUE;
-}
+ SoupConnectionPrivate *priv;
-/**
- * soup_connection_release:
- * @conn: a #SoupConnection
- *
- * Marks @conn as not "in use". This can be used to cancel the effect
- * of a soup_connection_reserve(). It is not necessary to call this
- * after soup_connection_send_request().
- **/
-void
-soup_connection_release (SoupConnection *conn)
-{
g_return_if_fail (SOUP_IS_CONNECTION (conn));
+ g_return_if_fail (SOUP_IS_MESSAGE (req));
+ priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+ g_return_if_fail (priv->state != SOUP_CONNECTION_NEW && priv->state != SOUP_CONNECTION_DISCONNECTED);
- clear_current_request (conn);
+ if (req != priv->cur_req)
+ set_current_request (priv, req);
+ soup_message_send_request (req, priv->socket, conn,
+ priv->proxy_uri != NULL);
}
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index 180ab28d..68a36abb 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -28,13 +28,8 @@ typedef struct {
GObjectClass parent_class;
/* signals */
- void (*connect_result) (SoupConnection *, guint);
void (*disconnected) (SoupConnection *);
- void (*request_started) (SoupConnection *, SoupMessage *);
-
- /* methods */
- void (*send_request) (SoupConnection *, SoupMessage *);
} SoupConnectionClass;
GType soup_connection_get_type (void);
@@ -44,9 +39,17 @@ typedef void (*SoupConnectionCallback) (SoupConnection *conn,
guint status,
gpointer data);
-
-#define SOUP_CONNECTION_SERVER_ADDRESS "server_address"
-#define SOUP_CONNECTION_PROXY_ADDRESS "proxy-address"
+typedef enum {
+ SOUP_CONNECTION_NEW,
+ SOUP_CONNECTION_CONNECTING,
+ SOUP_CONNECTION_IDLE,
+ SOUP_CONNECTION_IN_USE,
+ SOUP_CONNECTION_DISCONNECTED
+} SoupConnectionState;
+
+#define SOUP_CONNECTION_REMOTE_ADDRESS "remote-address"
+#define SOUP_CONNECTION_TUNNEL_ADDRESS "tunnel-address"
+#define SOUP_CONNECTION_PROXY_URI "proxy-uri"
#define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
#define SOUP_CONNECTION_ASYNC_CONTEXT "async-context"
#define SOUP_CONNECTION_TIMEOUT "timeout"
@@ -59,20 +62,22 @@ void soup_connection_connect_async (SoupConnection *conn,
SoupConnectionCallback callback,
gpointer user_data);
guint soup_connection_connect_sync (SoupConnection *conn);
+SoupAddress *soup_connection_get_tunnel_addr(SoupConnection *conn);
+gboolean soup_connection_start_ssl (SoupConnection *conn);
void soup_connection_disconnect (SoupConnection *conn);
SoupSocket *soup_connection_get_socket (SoupConnection *conn);
+SoupURI *soup_connection_get_proxy_uri (SoupConnection *conn);
-gboolean soup_connection_is_in_use (SoupConnection *conn);
+SoupConnectionState soup_connection_get_state (SoupConnection *conn);
+void soup_connection_set_state (SoupConnection *conn,
+ SoupConnectionState state);
time_t soup_connection_last_used (SoupConnection *conn);
void soup_connection_send_request (SoupConnection *conn,
SoupMessage *req);
-void soup_connection_reserve (SoupConnection *conn);
-void soup_connection_release (SoupConnection *conn);
-
G_END_DECLS
#endif /* SOUP_CONNECTION_H */
diff --git a/libsoup/soup-content-sniffer.c b/libsoup/soup-content-sniffer.c
new file mode 100644
index 00000000..bf971368
--- /dev/null
+++ b/libsoup/soup-content-sniffer.c
@@ -0,0 +1,605 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-content-sniffer.c
+ *
+ * Copyright (C) 2009 Gustavo Noronha Silva.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gio/gio.h>
+
+#include "soup-content-sniffer.h"
+#include "soup-enum-types.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-content-sniffer
+ * @short_description: Content sniffing for #SoupSession
+ *
+ * A #SoupContentSniffer tries to detect the actual content type of
+ * the files that are being downloaded by looking at some of the data
+ * before the #SoupMessage emits its #SoupMessage::got-headers signal.
+ * #SoupContentSniffer implements #SoupSessionFeature, so you can add
+ * content sniffing to a session with soup_session_add_feature() or
+ * soup_session_add_feature_by_type().
+ *
+ * Since: 2.27.3
+ **/
+
+static char *sniff (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer, GHashTable **params);
+static gsize get_buffer_size (SoupContentSniffer *sniffer);
+
+static void soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+static void request_queued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+static void request_unqueued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+
+G_DEFINE_TYPE_WITH_CODE (SoupContentSniffer, soup_content_sniffer, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+ soup_content_sniffer_session_feature_init))
+
+static void
+soup_content_sniffer_init (SoupContentSniffer *content_sniffer)
+{
+}
+
+static void
+soup_content_sniffer_class_init (SoupContentSnifferClass *content_sniffer_class)
+{
+ content_sniffer_class->sniff = sniff;
+ content_sniffer_class->get_buffer_size = get_buffer_size;
+}
+
+static void
+soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+ gpointer interface_data)
+{
+ feature_interface->request_queued = request_queued;
+ feature_interface->request_unqueued = request_unqueued;
+}
+
+/**
+ * soup_content_sniffer_new:
+ *
+ * Creates a new #SoupContentSniffer.
+ *
+ * Returns: a new #SoupContentSniffer
+ *
+ * Since: 2.27.3
+ **/
+SoupContentSniffer *
+soup_content_sniffer_new ()
+{
+ return g_object_new (SOUP_TYPE_CONTENT_SNIFFER, NULL);
+}
+
+char *
+soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
+ SoupMessage *msg, SoupBuffer *buffer,
+ GHashTable **params)
+{
+ g_return_val_if_fail (SOUP_IS_CONTENT_SNIFFER (sniffer), NULL);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ return SOUP_CONTENT_SNIFFER_GET_CLASS (sniffer)->sniff (sniffer, msg, buffer, params);
+}
+
+/* This table is based on the HTML5 spec;
+ * See 2.7.4 Content-Type sniffing: unknown type
+ */
+typedef struct {
+ /* @has_ws is TRUE if @pattern contains "generic" whitespace */
+ gboolean has_ws;
+ const char *mask;
+ const char *pattern;
+ guint pattern_length;
+ const char *sniffed_type;
+ gboolean scriptable;
+} SoupContentSnifferPattern;
+
+static SoupContentSnifferPattern types_table[] = {
+ { FALSE,
+ "\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF\xDF\xFF\xDF\xDF\xDF\xDF",
+ "\x3C\x21\x44\x4F\x43\x54\x59\x50\x45\x20\x48\x54\x4D\x4C",
+ 14,
+ "text/html",
+ TRUE },
+
+ { TRUE,
+ "\xFF\xFF\xDF\xDF\xDF\xDF",
+ " \x3C\x48\x54\x4D\x4C",
+ 5,
+ "text/html",
+ TRUE },
+
+ { TRUE,
+ "\xFF\xFF\xDF\xDF\xDF\xDF",
+ " \x3C\x48\x45\x41\x44",
+ 5,
+ "text/html",
+ TRUE },
+
+ { TRUE,
+ "\xFF\xFF\xDF\xDF\xDF\xDF\xDF\xDF",
+ " \x3C\x53\x43\x52\x49\x50\x54",
+ 7,
+ "text/html",
+ TRUE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\xFF\xFF",
+ "\x25\x50\x44\x46\x2D",
+ 5,
+ "application/pdf",
+ TRUE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ "\x25\x21\x50\x53\x2D\x41\x64\x6F\x62\x65\x2D",
+ 11,
+ "application/postscript",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\x00\x00",
+ "\xFE\xFF\x00\x00",
+ 4,
+ "text/plain",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\x00\x00",
+ "\xFF\xFF\x00\x00",
+ 4,
+ "text/plain",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\x00",
+ "\xEF\xBB\xBF\x00",
+ 4,
+ "text/plain",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\xFF\xFF\xFF",
+ "\x47\x49\x46\x38\x37\x61",
+ 6,
+ "image/gif",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\xFF\xFF\xFF",
+ "\x47\x49\x46\x38\x39\x61",
+ 6,
+ "image/gif",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",
+ "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A",
+ 8,
+ "image/png",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\xFF",
+ "\xFF\xD8\xFF",
+ 3,
+ "image/jpeg",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF",
+ "\x42\x4D",
+ 2,
+ "image/bmp",
+ FALSE },
+
+ { FALSE,
+ "\xFF\xFF\xFF\xFF",
+ "\x00\x00\x01\x00",
+ 4,
+ "image/vnd.microsoft.icon",
+ FALSE }
+};
+
+/* Whether a given byte looks like it might be part of binary content.
+ * Source: HTML5 spec; borrowed from the Chromium mime sniffer code,
+ * which is BSD-licensed
+ */
+static char byte_looks_binary[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, /* 0x00 - 0x0F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, /* 0x10 - 0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x6F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xC0 - 0xCF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xD0 - 0xDF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xE0 - 0xEF */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xF0 - 0xFF */
+};
+
+static char *
+sniff_gio (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer)
+{
+ SoupURI *uri;
+ char *uri_path;
+ char *content_type;
+ char *mime_type;
+ gboolean uncertain;
+
+ uri = soup_message_get_uri (msg);
+ uri_path = soup_uri_to_string (uri, TRUE);
+
+ content_type= g_content_type_guess (uri_path, (const guchar*)buffer->data, buffer->length, &uncertain);
+ mime_type = g_content_type_get_mime_type (content_type);
+
+ g_free (uri_path);
+ g_free (content_type);
+
+ return mime_type;
+}
+
+/* HTML5: 2.7.4 Content-Type sniffing: unknown type */
+static char*
+sniff_unknown (SoupContentSniffer *sniffer, SoupMessage *msg,
+ SoupBuffer *buffer, gboolean for_text_or_binary)
+{
+ const char *resource = buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ char *gio_guess;
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
+ SoupContentSnifferPattern *type_row = &(types_table[i]);
+
+ /* The scriptable types should be skiped for the text
+ * or binary path, but considered for other paths */
+ if (for_text_or_binary && type_row->scriptable)
+ continue;
+
+ if (type_row->has_ws) {
+ int index_stream = 0;
+ int index_pattern = 0;
+ gboolean skip_row = FALSE;
+
+ while (index_stream < resource_length) {
+ /* Skip insignificant white space ("WS" in the spec) */
+ if (type_row->pattern[index_pattern] == ' ') {
+ if (resource[index_stream] == '\x09' ||
+ resource[index_stream] == '\x0a' ||
+ resource[index_stream] == '\x0c' ||
+ resource[index_stream] == '\x0d' ||
+ resource[index_stream] == '\x20')
+ index_stream++;
+ else
+ index_pattern++;
+ } else {
+ if ((type_row->mask[index_pattern] & resource[index_stream]) != type_row->pattern[index_pattern]) {
+ skip_row = TRUE;
+ break;
+ }
+ index_pattern++;
+ index_stream++;
+ }
+ }
+
+ if (skip_row)
+ continue;
+
+ if (index_pattern > type_row->pattern_length)
+ return g_strdup (type_row->sniffed_type);
+ } else {
+ int j;
+
+ if (resource_length < type_row->pattern_length)
+ continue;
+
+ for (j = 0; j < type_row->pattern_length; j++) {
+ if ((type_row->mask[j] & resource[j]) != type_row->pattern[j])
+ break;
+ }
+
+ /* This means our comparison above matched completely */
+ if (j == type_row->pattern_length)
+ return g_strdup (type_row->sniffed_type);
+ }
+ }
+
+ /* The spec allows us to use platform sniffing to find out
+ * about other types that are not covered, but we need to be
+ * careful to not escalate privileges, if on text or binary.
+ */
+ gio_guess = sniff_gio (sniffer, msg, buffer);
+
+ if (for_text_or_binary) {
+ for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
+ SoupContentSnifferPattern *type_row = &(types_table[i]);
+
+ if (!g_ascii_strcasecmp (type_row->sniffed_type, gio_guess) &&
+ type_row->scriptable) {
+ g_free (gio_guess);
+ gio_guess = NULL;
+ break;
+ }
+ }
+ }
+
+ if (gio_guess)
+ return gio_guess;
+
+ return g_strdup ("application/octet-stream");
+}
+
+/* HTML5: 2.7.3 Content-Type sniffing: text or binary */
+static char*
+sniff_text_or_binary (SoupContentSniffer *sniffer, SoupMessage *msg,
+ SoupBuffer *buffer)
+{
+ const char *resource = buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ gboolean looks_binary = FALSE;
+ int i;
+
+ /* Detecting UTF-16BE, UTF-16LE, or UTF-8 BOMs means it's text/plain */
+ if (resource_length >= 4) {
+ if ((resource[0] == 0xFE && resource[1] == 0xFF) ||
+ (resource[0] == 0xFF && resource[1] == 0xFE) ||
+ (resource[0] == 0xEF && resource[1] == 0xBB && resource[2] == 0xBF))
+ return g_strdup ("text/plain");
+ }
+
+ /* Look to see if any of the first n bytes looks binary */
+ for (i = 0; i < resource_length; i++) {
+ if (byte_looks_binary[(unsigned char)resource[i]]) {
+ looks_binary = TRUE;
+ break;
+ }
+ }
+
+ if (!looks_binary)
+ return g_strdup ("text/plain");
+
+ return sniff_unknown (sniffer, msg, buffer, TRUE);
+}
+
+static char*
+sniff_images (SoupContentSniffer *sniffer, SoupMessage *msg,
+ SoupBuffer *buffer, const char *content_type)
+{
+ const char *resource = buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (types_table); i++) {
+ SoupContentSnifferPattern *type_row = &(types_table[i]);
+
+ if (resource_length < type_row->pattern_length)
+ continue;
+
+ if (!g_str_has_prefix (type_row->sniffed_type, "image/"))
+ continue;
+
+ /* All of the image types use all-\xFF for the mask,
+ * so we can just memcmp.
+ */
+ if (memcmp (type_row->pattern, resource, type_row->pattern_length) == 0)
+ return g_strdup (type_row->sniffed_type);
+ }
+
+ return g_strdup (content_type);
+}
+
+static char*
+sniff_feed_or_html (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer)
+{
+ const char *resource = buffer->data;
+ int resource_length = MIN (512, buffer->length);
+ int pos = 0;
+
+ if (resource_length < 3)
+ goto text_html;
+
+ /* Skip a leading UTF-8 BOM */
+ if (resource[0] == 0xEF && resource[1] == 0xBB && resource[2] == 0xBF)
+ pos = 3;
+
+ look_for_tag:
+ if (pos > resource_length)
+ goto text_html;
+
+ /* Skip insignificant white space */
+ while ((resource[pos] == '\x09') ||
+ (resource[pos] == '\x20') ||
+ (resource[pos] == '\x0A') ||
+ (resource[pos] == '\x0D')) {
+ pos++;
+
+ if (pos > resource_length)
+ goto text_html;
+ }
+
+ /* != < */
+ if (resource[pos] != '\x3C')
+ return g_strdup ("text/html");
+
+ pos++;
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+
+ /* Skipping comments */
+ if ((resource[pos] == '\x2D') ||
+ (resource[pos+1] == '\x2D') ||
+ (resource[pos+2] == '\x3E')) {
+ pos = pos + 3;
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+
+ while ((resource[pos] != '\x2D') &&
+ (resource[pos+1] != '\x2D') &&
+ (resource[pos+2] != '\x3E')) {
+ pos++;
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+ }
+
+ goto look_for_tag;
+ }
+
+ if (pos > resource_length)
+ goto text_html;
+
+ /* == ! */
+ if (resource[pos] == '\x21') {
+ do {
+ pos++;
+
+ if (pos > resource_length)
+ goto text_html;
+ } while (resource[pos] != '\x3E');
+
+ pos++;
+
+ goto look_for_tag;
+ } else if (resource[pos] == '\x3F') { /* ? */
+ do {
+ pos++;
+
+ if ((pos + 1) > resource_length)
+ goto text_html;
+ } while ((resource[pos] != '\x3F') &&
+ (resource[pos+1] != '\x3E'));
+
+ pos = pos + 2;
+
+ goto look_for_tag;
+ }
+
+ if ((pos + 2) > resource_length)
+ goto text_html;
+
+ if ((resource[pos] == '\x72') &&
+ (resource[pos+1] == '\x73') &&
+ (resource[pos+2] == '\x73'))
+ return g_strdup ("application/rss+xml");
+
+ if ((pos + 3) > resource_length)
+ goto text_html;
+
+ if ((resource[pos] == '\x66') &&
+ (resource[pos+1] == '\x65') &&
+ (resource[pos+2] == '\x65') &&
+ (resource[pos+3] == '\x64'))
+ return g_strdup ("application/atom+xml");
+
+ text_html:
+ return g_strdup ("text/html");
+}
+
+static char*
+sniff (SoupContentSniffer *sniffer, SoupMessage *msg, SoupBuffer *buffer, GHashTable **params)
+{
+ const char *content_type_with_params;
+ const char *content_type;
+
+ content_type = soup_message_headers_get_content_type (msg->response_headers, params);
+ content_type_with_params = soup_message_headers_get_one (msg->response_headers, "Content-Type");
+
+
+ /* These comparisons are done in an ASCII-case-insensitive
+ * manner because the spec requires it */
+ if ((content_type == NULL) ||
+ !g_ascii_strcasecmp (content_type, "unknown/unknown") ||
+ !g_ascii_strcasecmp (content_type, "application/unknown") ||
+ !g_ascii_strcasecmp (content_type, "*/*"))
+ return sniff_unknown (sniffer, msg, buffer, FALSE);
+
+ if (g_str_has_suffix (content_type, "+xml") ||
+ !g_ascii_strcasecmp (content_type, "text/xml") ||
+ !g_ascii_strcasecmp (content_type, "application/xml"))
+ return g_strdup (content_type);
+
+ /* 2.7.5 Content-Type sniffing: image
+ * The spec says:
+ *
+ * If the resource's official type is "image/svg+xml", then
+ * the sniffed type of the resource is its official type (an
+ * XML type)
+ *
+ * The XML case is handled by the if above; if you refactor
+ * this code, keep this in mind.
+ */
+ if (!g_ascii_strncasecmp (content_type, "image/", 6))
+ return sniff_images (sniffer, msg, buffer, content_type);
+
+ /* If we got text/plain, use text_or_binary */
+ if (g_str_equal (content_type_with_params, "text/plain") ||
+ g_str_equal (content_type_with_params, "text/plain; charset=ISO-8859-1") ||
+ g_str_equal (content_type_with_params, "text/plain; charset=iso-8859-1") ||
+ g_str_equal (content_type_with_params, "text/plain; charset=UTF-8")) {
+ return sniff_text_or_binary (sniffer, msg, buffer);
+ }
+
+ if (!g_ascii_strcasecmp (content_type, "text/html"))
+ return sniff_feed_or_html (sniffer, msg, buffer);
+
+ return g_strdup (content_type);
+}
+
+static gsize
+get_buffer_size (SoupContentSniffer *sniffer)
+{
+ return 512;
+}
+
+static void
+soup_content_sniffer_got_headers_cb (SoupMessage *msg, SoupContentSniffer *sniffer)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupContentSnifferClass *content_sniffer_class = SOUP_CONTENT_SNIFFER_GET_CLASS (sniffer);
+
+ priv->bytes_for_sniffing = content_sniffer_class->get_buffer_size (sniffer);
+}
+
+static void
+request_queued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ priv->sniffer = g_object_ref (feature);
+ g_signal_connect (msg, "got-headers",
+ G_CALLBACK (soup_content_sniffer_got_headers_cb),
+ feature);
+}
+
+static void
+request_unqueued (SoupSessionFeature *feature, SoupSession *session,
+ SoupMessage *msg)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ g_object_unref (priv->sniffer);
+ priv->sniffer = NULL;
+
+ g_signal_handlers_disconnect_by_func (msg, soup_content_sniffer_got_headers_cb, feature);
+}
diff --git a/libsoup/soup-content-sniffer.h b/libsoup/soup-content-sniffer.h
new file mode 100644
index 00000000..a8aa9156
--- /dev/null
+++ b/libsoup/soup-content-sniffer.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Gustavo Noronha Silva.
+ */
+
+#ifndef SOUP_CONTENT_SNIFFER_H
+#define SOUP_CONTENT_SNIFFER_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONTENT_SNIFFER (soup_content_sniffer_get_type ())
+#define SOUP_CONTENT_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONTENT_SNIFFER, SoupContentSniffer))
+#define SOUP_CONTENT_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONTENT_SNIFFER, SoupContentSnifferClass))
+#define SOUP_IS_CONTENT_SNIFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONTENT_SNIFFER))
+#define SOUP_IS_CONTENT_SNIFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONTENT_SNIFFER))
+#define SOUP_CONTENT_SNIFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONTENT_SNIFFER, SoupContentSnifferClass))
+
+typedef struct _SoupContentSnifferPrivate SoupContentSnifferPrivate;
+
+typedef struct {
+ GObject parent;
+
+ SoupContentSnifferPrivate *priv;
+} SoupContentSniffer;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ char* (*sniff) (SoupContentSniffer *sniffer,
+ SoupMessage *msg,
+ SoupBuffer *buffer,
+ GHashTable **params);
+ gsize (*get_buffer_size) (SoupContentSniffer *sniffer);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+ void (*_libsoup_reserved5) (void);
+} SoupContentSnifferClass;
+
+GType soup_content_sniffer_get_type (void);
+
+SoupContentSniffer *soup_content_sniffer_new (void);
+
+char *soup_content_sniffer_sniff (SoupContentSniffer *sniffer,
+ SoupMessage *msg,
+ SoupBuffer *buffer,
+ GHashTable **params);
+
+G_END_DECLS
+
+#endif /* SOUP_CONTENT_SNIFFER_H */
diff --git a/libsoup/soup-dns.c b/libsoup/soup-dns.c
index cb4cee5a..68a2e745 100644
--- a/libsoup/soup-dns.c
+++ b/libsoup/soup-dns.c
@@ -338,9 +338,7 @@ resolve_address (SoupDNSCacheEntry *entry)
memset (&hints, 0, sizeof (struct addrinfo));
# ifdef AI_ADDRCONFIG
- hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
-# else
- hints.ai_flags = AI_CANONNAME;
+ hints.ai_flags = AI_ADDRCONFIG;
# endif
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
diff --git a/libsoup/soup-form.c b/libsoup/soup-form.c
index a2103f78..7263890a 100644
--- a/libsoup/soup-form.c
+++ b/libsoup/soup-form.c
@@ -366,7 +366,7 @@ soup_form_request_for_data (const char *method, const char *uri_string,
msg = soup_message_new_from_uri (method, uri);
- if (!strcmp (method, "POST")) {
+ if (!strcmp (method, "POST") || !strcmp (method, "PUT")) {
soup_message_set_request (
msg, SOUP_FORM_MIME_TYPE_URLENCODED,
SOUP_MEMORY_TAKE,
diff --git a/libsoup/soup-gnutls.c b/libsoup/soup-gnutls.c
index b5a5f32a..0326c6ba 100644
--- a/libsoup/soup-gnutls.c
+++ b/libsoup/soup-gnutls.c
@@ -390,8 +390,12 @@ soup_gnutls_pull_func (gnutls_transport_ptr_t transport_data,
SoupGNUTLSChannel *chan = transport_data;
ssize_t nread;
- nread = read (chan->sockfd, buf, buflen);
+ nread = recv (chan->sockfd, buf, buflen, 0);
+#ifdef G_OS_WIN32
+ chan->eagain = (nread == SOCKET_ERROR && WSAGetLastError () == WSAEWOULDBLOCK);
+#else
chan->eagain = (nread == -1 && errno == EAGAIN);
+#endif
return nread;
}
@@ -402,8 +406,12 @@ soup_gnutls_push_func (gnutls_transport_ptr_t transport_data,
SoupGNUTLSChannel *chan = transport_data;
ssize_t nwrote;
- nwrote = write (chan->sockfd, buf, buflen);
+ nwrote = send (chan->sockfd, buf, buflen, 0);
+#ifdef G_OS_WIN32
+ chan->eagain = (nwrote == SOCKET_ERROR && WSAGetLastError () == WSAEWOULDBLOCK);
+#else
chan->eagain = (nwrote == -1 && errno == EAGAIN);
+#endif
return nwrote;
}
@@ -446,7 +454,8 @@ soup_ssl_wrap_iochannel (GIOChannel *sock, gboolean non_blocking,
if (ret)
goto THROW_CREATE_ERROR;
- if (gnutls_set_default_priority (session) != 0)
+ /* See http://bugzilla.gnome.org/show_bug.cgi?id=581342 */
+ if (gnutls_priority_set_direct (session, "NORMAL:!VERS-TLS1.1:!VERS-TLS1.0", NULL) != 0)
goto THROW_CREATE_ERROR;
if (gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE,
diff --git a/libsoup/soup-marshal.list b/libsoup/soup-marshal.list
index 1a43570a..d0c53efa 100644
--- a/libsoup/soup-marshal.list
+++ b/libsoup/soup-marshal.list
@@ -6,3 +6,4 @@ NONE:OBJECT,OBJECT
NONE:OBJECT,POINTER
NONE:BOXED,BOXED
NONE:OBJECT,OBJECT,BOOLEAN
+NONE:STRING,BOXED
diff --git a/libsoup/soup-message-client-io.c b/libsoup/soup-message-client-io.c
index ebf144c7..81599229 100644
--- a/libsoup/soup-message-client-io.c
+++ b/libsoup/soup-message-client-io.c
@@ -109,7 +109,8 @@ get_request_headers (SoupMessage *req, GString *header,
*encoding = soup_message_headers_get_encoding (req->request_headers);
if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
*encoding == SOUP_ENCODING_NONE) &&
- req->request_body->length > 0 &&
+ (req->request_body->length > 0 ||
+ soup_message_headers_get_one (req->request_headers, "Content-Type")) &&
!soup_message_headers_get_content_length (req->request_headers)) {
*encoding = SOUP_ENCODING_CONTENT_LENGTH;
soup_message_headers_set_content_length (req->request_headers,
diff --git a/libsoup/soup-message-headers.c b/libsoup/soup-message-headers.c
index f0abb789..6acca0c9 100644
--- a/libsoup/soup-message-headers.c
+++ b/libsoup/soup-message-headers.c
@@ -33,6 +33,7 @@
typedef void (*SoupHeaderSetter) (SoupMessageHeaders *, const char *);
static const char *intern_header_name (const char *name, SoupHeaderSetter *setter);
+static void clear_special_headers (SoupMessageHeaders *hdrs);
typedef struct {
const char *name;
@@ -98,7 +99,6 @@ soup_message_headers_free (SoupMessageHeaders *hdrs)
g_array_free (hdrs->array, TRUE);
if (hdrs->concat)
g_hash_table_destroy (hdrs->concat);
- g_free (hdrs->content_type);
g_slice_free (SoupMessageHeaders, hdrs);
}
}
@@ -137,7 +137,7 @@ soup_message_headers_clear (SoupMessageHeaders *hdrs)
if (hdrs->concat)
g_hash_table_remove_all (hdrs->concat);
- hdrs->encoding = -1;
+ clear_special_headers (hdrs);
}
/**
@@ -226,6 +226,20 @@ find_header (SoupHeader *hdr_array, const char *interned_name, int nth)
return -1;
}
+static int
+find_last_header (SoupHeader *hdr_array, guint length, const char *interned_name, int nth)
+{
+ int i;
+
+ for (i = length; i >= 0; i--) {
+ if (hdr_array[i].name == interned_name) {
+ if (nth-- == 0)
+ return i;
+ }
+ }
+ return -1;
+}
+
/**
* soup_message_headers_remove:
* @hdrs: a #SoupMessageHeaders
@@ -277,12 +291,15 @@ const char *
soup_message_headers_get_one (SoupMessageHeaders *hdrs, const char *name)
{
SoupHeader *hdr_array = (SoupHeader *)(hdrs->array->data);
+ guint hdr_length = hdrs->array->len;
int index;
g_return_val_if_fail (name != NULL, NULL);
name = intern_header_name (name, NULL);
- index = find_header (hdr_array, name, 0);
+
+ index = find_last_header (hdr_array, hdr_length, name, 0);
+
return (index == -1) ? NULL : hdr_array[index].value;
}
@@ -531,6 +548,22 @@ intern_header_name (const char *name, SoupHeaderSetter *setter)
return interned;
}
+static void
+clear_special_headers (SoupMessageHeaders *hdrs)
+{
+ SoupHeaderSetter setter;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ /* Make sure header_setters has been initialized */
+ intern_header_name ("", NULL);
+
+ g_hash_table_iter_init (&iter, header_setters);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ setter = value;
+ setter (hdrs, NULL);
+ }
+}
/* Specific headers */
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index 5364868f..74c934f1 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -53,6 +53,9 @@ typedef struct {
SoupMessageBody *read_body;
goffset read_length;
+ gboolean need_content_sniffed, need_got_chunk;
+ SoupMessageBody *sniff_data;
+
SoupMessageIOState write_state;
SoupEncoding write_encoding;
GString *write_buf;
@@ -105,6 +108,9 @@ soup_message_io_cleanup (SoupMessage *msg)
if (io->write_chunk)
soup_buffer_free (io->write_chunk);
+ if (io->sniff_data)
+ soup_message_body_free (io->sniff_data);
+
g_slice_free (SoupMessageIOData, io);
}
@@ -151,7 +157,7 @@ soup_message_io_stop (SoupMessage *msg)
else if (io->conn) {
SoupConnection *conn = io->conn;
io->conn = NULL;
- soup_connection_release (conn);
+ soup_connection_set_state (conn, SOUP_CONNECTION_IDLE);
g_object_unref (conn);
}
}
@@ -207,6 +213,55 @@ io_disconnected (SoupSocket *sock, SoupMessage *msg)
io_error (sock, msg, NULL);
}
+static gboolean
+io_handle_sniffing (SoupMessage *msg, gboolean done_reading)
+{
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ SoupMessageIOData *io = priv->io_data;
+ SoupBuffer *sniffed_buffer;
+ char *sniffed_mime_type;
+ GHashTable *params = NULL;
+
+ if (!priv->sniffer)
+ return TRUE;
+
+ if (!io->sniff_data) {
+ io->sniff_data = soup_message_body_new ();
+ io->need_content_sniffed = TRUE;
+ }
+
+ if (io->need_content_sniffed) {
+ if (io->sniff_data->length < priv->bytes_for_sniffing &&
+ !done_reading)
+ return TRUE;
+
+ io->need_content_sniffed = FALSE;
+ sniffed_buffer = soup_message_body_flatten (io->sniff_data);
+ sniffed_mime_type = soup_content_sniffer_sniff (priv->sniffer, msg, sniffed_buffer, &params);
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_content_sniffed (msg, sniffed_mime_type, params);
+ g_free (sniffed_mime_type);
+ if (params)
+ g_hash_table_destroy (params);
+ if (sniffed_buffer)
+ soup_buffer_free (sniffed_buffer);
+ SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
+ }
+
+ if (io->need_got_chunk) {
+ io->need_got_chunk = FALSE;
+ sniffed_buffer = soup_message_body_flatten (io->sniff_data);
+
+ SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
+ soup_message_got_chunk (msg, sniffed_buffer);
+ soup_buffer_free (sniffed_buffer);
+ SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
+ }
+
+ return TRUE;
+}
+
/* Reads data from io->sock into io->read_meta_buf. If @to_blank is
* %TRUE, it reads up until a blank line ("CRLF CRLF" or "LF LF").
* Otherwise, it reads up until a single CRLF or LF.
@@ -257,7 +312,15 @@ read_metadata (SoupMessage *msg, gboolean to_blank)
if (got_lf) {
if (!to_blank)
break;
- if (nread == 1 || (nread == 2 && read_buf[0] == '\r'))
+ if (nread == 1 &&
+ !strncmp ((char *)io->read_meta_buf->data +
+ io->read_meta_buf->len - 2,
+ "\n\n", 2))
+ break;
+ else if (nread == 2 &&
+ !strncmp ((char *)io->read_meta_buf->data +
+ io->read_meta_buf->len - 3,
+ "\n\r\n", 3))
break;
}
}
@@ -286,6 +349,9 @@ read_body_chunk (SoupMessage *msg)
GError *error = NULL;
SoupBuffer *buffer;
+ if (!io_handle_sniffing (msg, FALSE))
+ return FALSE;
+
while (read_to_eof || io->read_length > 0) {
if (priv->chunk_allocator) {
buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data);
@@ -316,6 +382,14 @@ read_body_chunk (SoupMessage *msg)
io->read_length -= nread;
+ if (io->need_content_sniffed) {
+ soup_message_body_append_buffer (io->sniff_data, buffer);
+ io->need_got_chunk = TRUE;
+ if (!io_handle_sniffing (msg, FALSE))
+ return FALSE;
+ continue;
+ }
+
SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
soup_message_got_chunk (msg, buffer);
soup_buffer_free (buffer);
@@ -774,6 +848,21 @@ io_read (SoupSocket *sock, SoupMessage *msg)
return;
got_body:
+ if (!io_handle_sniffing (msg, TRUE)) {
+ /* If the message was paused (as opposed to
+ * cancelled), we need to make sure we wind up
+ * back here when it's unpaused, even if it
+ * was doing a chunked or EOF-terminated read
+ * before.
+ */
+ if (io == priv->io_data) {
+ io->read_state = SOUP_MESSAGE_IO_STATE_BODY;
+ io->read_encoding = SOUP_ENCODING_CONTENT_LENGTH;
+ io->read_length = 0;
+ }
+ return;
+ }
+
io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index f47251a7..5c88cbc0 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -9,6 +9,7 @@
#include "soup-message.h"
#include "soup-auth.h"
#include "soup-connection.h"
+#include "soup-content-sniffer.h"
typedef enum {
SOUP_MESSAGE_IO_STATUS_IDLE,
@@ -29,6 +30,9 @@ typedef struct {
guint msg_flags;
gboolean server_side;
+ SoupContentSniffer *sniffer;
+ gsize bytes_for_sniffing;
+
SoupHTTPVersion http_version, orig_http_version;
SoupURI *uri;
diff --git a/libsoup/soup-message-queue.c b/libsoup/soup-message-queue.c
index 7e73c807..bce5a16d 100644
--- a/libsoup/soup-message-queue.c
+++ b/libsoup/soup-message-queue.c
@@ -11,6 +11,7 @@
#endif
#include "soup-message-queue.h"
+#include "soup-uri.h"
/**
* SECTION:soup-message-queue
@@ -55,6 +56,18 @@ soup_message_queue_destroy (SoupMessageQueue *queue)
g_slice_free (SoupMessageQueue, queue);
}
+static void
+queue_message_restarted (SoupMessage *msg, gpointer user_data)
+{
+ SoupMessageQueueItem *item = user_data;
+
+ if (item->proxy_addr) {
+ g_object_unref (item->proxy_addr);
+ item->proxy_addr = NULL;
+ }
+ item->resolved_proxy_addr = FALSE;
+}
+
/**
* soup_message_queue_append:
* @queue: a #SoupMessageQueue
@@ -81,6 +94,9 @@ soup_message_queue_append (SoupMessageQueue *queue, SoupMessage *msg,
item->callback_data = user_data;
item->cancellable = g_cancellable_new ();
+ g_signal_connect (msg, "restarted",
+ G_CALLBACK (queue_message_restarted), item);
+
/* Note: the initial ref_count of 1 represents the caller's
* ref; the queue's own ref is indicated by the absence of the
* "removed" flag.
@@ -145,12 +161,16 @@ soup_message_queue_item_unref (SoupMessageQueueItem *item)
g_mutex_unlock (item->queue->mutex);
/* And free it */
+ g_signal_handlers_disconnect_by_func (item->msg,
+ queue_message_restarted, item);
g_object_unref (item->msg);
g_object_unref (item->cancellable);
- if (item->msg_addr)
- g_object_unref (item->msg_addr);
if (item->proxy_addr)
g_object_unref (item->proxy_addr);
+ if (item->proxy_uri)
+ soup_uri_free (item->proxy_uri);
+ if (item->conn)
+ g_object_unref (item->conn);
g_slice_free (SoupMessageQueueItem, item);
}
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index 4a9ae8f0..b7bc5d12 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -9,6 +9,7 @@
#include <glib.h>
#include <gio/gio.h>
+#include <libsoup/soup-connection.h>
#include <libsoup/soup-message.h>
#include <libsoup/soup-session.h>
@@ -26,15 +27,16 @@ struct SoupMessageQueueItem {
gpointer callback_data;
GCancellable *cancellable;
- SoupAddress *msg_addr, *proxy_addr;
+ SoupAddress *proxy_addr;
+ SoupURI *proxy_uri;
+ SoupConnection *conn;
- guint resolving_msg_addr : 1;
guint resolving_proxy_addr : 1;
guint resolved_proxy_addr : 1;
/*< private >*/
guint removed : 1;
- guint ref_count : 28;
+ guint ref_count : 29;
SoupMessageQueueItem *prev, *next;
};
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 68a21c1e..f614946b 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -99,6 +99,7 @@ enum {
GOT_HEADERS,
GOT_CHUNK,
GOT_BODY,
+ CONTENT_SNIFFED,
RESTARTED,
FINISHED,
@@ -402,6 +403,44 @@ soup_message_class_init (SoupMessageClass *message_class)
G_TYPE_NONE, 0);
/**
+ * SoupMessage::content-sniffed:
+ * @msg: the message
+ * @type: the content type that we got from sniffing
+ * @params: a #GHashTable with the parameters
+ *
+ * This signal is emitted after %got-headers, and before the
+ * first %got-chunk. If content sniffing is disabled, or no
+ * content sniffing will be performed, due to the sniffer
+ * deciding to trust the Content-Type sent by the server, this
+ * signal is emitted immediately after %got_headers, and @type
+ * is %NULL.
+ *
+ * If the #SoupContentSniffer feature is enabled, and the
+ * sniffer decided to perform sniffing, the first %got_chunk
+ * emission may be delayed, so that the sniffer has enough
+ * data to correctly sniff the content. It notified the
+ * library user that the content has been sniffed, and allows
+ * it to change the header contents in the message, if
+ * desired.
+ *
+ * After this signal is emitted, the data that was spooled so
+ * that sniffing could be done is delivered on the first
+ * emission of %got_chunk.
+ *
+ * Since: 2.27.3
+ **/
+ signals[CONTENT_SNIFFED] =
+ g_signal_new ("content_sniffed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ soup_marshal_NONE__STRING_BOXED,
+ G_TYPE_NONE, 2,
+ G_TYPE_STRING,
+ G_TYPE_HASH_TABLE);
+
+ /**
* SoupMessage::restarted:
* @msg: the message
*
@@ -858,6 +897,24 @@ soup_message_got_body (SoupMessage *msg)
g_signal_emit (msg, signals[GOT_BODY], 0);
}
+/**
+ * soup_message_content_sniffed:
+ * @msg: a #SoupMessage
+ * @type: a string with the sniffed content type
+ * @params: a #GHashTable with the parameters
+ *
+ * Emits the %content_sniffed signal, indicating that the IO layer
+ * finished sniffing the content type for @msg. If content sniffing
+ * will not be performed, due to the sniffer deciding to trust the
+ * Content-Type sent by the server, this signal is emitted immediately
+ * after %got_headers, with %NULL as @content_type.
+ **/
+void
+soup_message_content_sniffed (SoupMessage *msg, const char *content_type, GHashTable *params)
+{
+ g_signal_emit (msg, signals[CONTENT_SNIFFED], 0, content_type, params);
+}
+
static void
restarted (SoupMessage *req)
{
@@ -896,7 +953,10 @@ finished (SoupMessage *req)
void
soup_message_finished (SoupMessage *msg)
{
- g_signal_emit (msg, signals[FINISHED], 0);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+ if (priv->io_status != SOUP_MESSAGE_IO_STATUS_FINISHED)
+ g_signal_emit (msg, signals[FINISHED], 0);
}
static void
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 1b850beb..b940ac64 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -155,6 +155,7 @@ void soup_message_got_informational (SoupMessage *msg);
void soup_message_got_headers (SoupMessage *msg);
void soup_message_got_chunk (SoupMessage *msg, SoupBuffer *chunk);
void soup_message_got_body (SoupMessage *msg);
+void soup_message_content_sniffed (SoupMessage *msg, const char *content_type, GHashTable *params);
void soup_message_restarted (SoupMessage *msg);
void soup_message_finished (SoupMessage *msg);
diff --git a/libsoup/soup-method.h b/libsoup/soup-method.h
index f646fd2e..e716b8e5 100644
--- a/libsoup/soup-method.h
+++ b/libsoup/soup-method.h
@@ -7,6 +7,7 @@
#define SOUP_METHOD_H 1
#include <libsoup/soup-types.h>
+#include <libsoup/soup-misc.h>
G_BEGIN_DECLS
@@ -33,24 +34,26 @@ G_BEGIN_DECLS
* </programlisting></informalexample>
**/
+#define _SOUP_INTERN_METHOD(method) (_SOUP_ATOMIC_INTERN_STRING (_SOUP_METHOD_##method, #method))
+
/* HTTP/1.1 methods */
-#define SOUP_METHOD_OPTIONS (_SOUP_METHOD_OPTIONS ? _SOUP_METHOD_OPTIONS : (_SOUP_METHOD_OPTIONS = g_intern_static_string ("OPTIONS")))
-#define SOUP_METHOD_GET (_SOUP_METHOD_GET ? _SOUP_METHOD_GET : (_SOUP_METHOD_GET = g_intern_static_string ("GET")))
-#define SOUP_METHOD_HEAD (_SOUP_METHOD_HEAD ? _SOUP_METHOD_HEAD : (_SOUP_METHOD_HEAD = g_intern_static_string ("HEAD")))
-#define SOUP_METHOD_POST (_SOUP_METHOD_POST ? _SOUP_METHOD_POST : (_SOUP_METHOD_POST = g_intern_static_string ("POST")))
-#define SOUP_METHOD_PUT (_SOUP_METHOD_PUT ? _SOUP_METHOD_PUT : (_SOUP_METHOD_PUT = g_intern_static_string ("PUT")))
-#define SOUP_METHOD_DELETE (_SOUP_METHOD_DELETE ? _SOUP_METHOD_DELETE : (_SOUP_METHOD_DELETE = g_intern_static_string ("DELETE")))
-#define SOUP_METHOD_TRACE (_SOUP_METHOD_TRACE ? _SOUP_METHOD_TRACE : (_SOUP_METHOD_TRACE = g_intern_static_string ("TRACE")))
-#define SOUP_METHOD_CONNECT (_SOUP_METHOD_CONNECT ? _SOUP_METHOD_CONNECT : (_SOUP_METHOD_CONNECT = g_intern_static_string ("CONNECT")))
+#define SOUP_METHOD_OPTIONS _SOUP_INTERN_METHOD (OPTIONS)
+#define SOUP_METHOD_GET _SOUP_INTERN_METHOD (GET)
+#define SOUP_METHOD_HEAD _SOUP_INTERN_METHOD (HEAD)
+#define SOUP_METHOD_POST _SOUP_INTERN_METHOD (POST)
+#define SOUP_METHOD_PUT _SOUP_INTERN_METHOD (PUT)
+#define SOUP_METHOD_DELETE _SOUP_INTERN_METHOD (DELETE)
+#define SOUP_METHOD_TRACE _SOUP_INTERN_METHOD (TRACE)
+#define SOUP_METHOD_CONNECT _SOUP_INTERN_METHOD (CONNECT)
/* WebDAV methods */
-#define SOUP_METHOD_PROPFIND (_SOUP_METHOD_PROPFIND ? _SOUP_METHOD_PROPFIND : (_SOUP_METHOD_PROPFIND = g_intern_static_string ("PROPFIND")))
-#define SOUP_METHOD_PROPPATCH (_SOUP_METHOD_PROPPATCH ? _SOUP_METHOD_PROPPATCH : (_SOUP_METHOD_PROPPATCH = g_intern_static_string ("PROPPATCH")))
-#define SOUP_METHOD_MKCOL (_SOUP_METHOD_MKCOL ? _SOUP_METHOD_MKCOL : (_SOUP_METHOD_MKCOL = g_intern_static_string ("MKCOL")))
-#define SOUP_METHOD_COPY (_SOUP_METHOD_COPY ? _SOUP_METHOD_COPY : (_SOUP_METHOD_COPY = g_intern_static_string ("COPY")))
-#define SOUP_METHOD_MOVE (_SOUP_METHOD_MOVE ? _SOUP_METHOD_MOVE : (_SOUP_METHOD_MOVE = g_intern_static_string ("MOVE")))
-#define SOUP_METHOD_LOCK (_SOUP_METHOD_LOCK ? _SOUP_METHOD_LOCK : (_SOUP_METHOD_LOCK = g_intern_static_string ("LOCK")))
-#define SOUP_METHOD_UNLOCK (_SOUP_METHOD_UNLOCK ? _SOUP_METHOD_UNLOCK : (_SOUP_METHOD_UNLOCK = g_intern_static_string ("UNLOCK")))
+#define SOUP_METHOD_PROPFIND _SOUP_INTERN_METHOD (PROPFIND)
+#define SOUP_METHOD_PROPPATCH _SOUP_INTERN_METHOD (PROPPATCH)
+#define SOUP_METHOD_MKCOL _SOUP_INTERN_METHOD (MKCOL)
+#define SOUP_METHOD_COPY _SOUP_INTERN_METHOD (COPY)
+#define SOUP_METHOD_MOVE _SOUP_INTERN_METHOD (MOVE)
+#define SOUP_METHOD_LOCK _SOUP_INTERN_METHOD (LOCK)
+#define SOUP_METHOD_UNLOCK _SOUP_INTERN_METHOD (UNLOCK)
/* Do not use these variables directly; use the macros above, which
* ensure that they get initialized properly.
diff --git a/libsoup/soup-misc.c b/libsoup/soup-misc.c
index d67345b2..5e99476f 100644
--- a/libsoup/soup-misc.c
+++ b/libsoup/soup-misc.c
@@ -56,63 +56,6 @@ soup_str_case_equal (gconstpointer v1,
return g_ascii_strcasecmp (string1, string2) == 0;
}
-typedef struct {
- gpointer instance;
- guint signal_id;
-} SoupSignalOnceData;
-
-static void
-signal_once_object_destroyed (gpointer ssod, GObject *ex_object)
-{
- g_slice_free (SoupSignalOnceData, ssod);
-}
-
-static void
-signal_once_metamarshal (GClosure *closure, GValue *return_value,
- guint n_param_values, const GValue *param_values,
- gpointer invocation_hint, gpointer marshal_data)
-{
- SoupSignalOnceData *ssod = marshal_data;
-
- closure->marshal (closure, return_value, n_param_values,
- param_values, invocation_hint,
- ((GCClosure *)closure)->callback);
-
- if (g_signal_handler_is_connected (ssod->instance, ssod->signal_id))
- g_signal_handler_disconnect (ssod->instance, ssod->signal_id);
- g_object_weak_unref (G_OBJECT (ssod->instance), signal_once_object_destroyed, ssod);
- g_slice_free (SoupSignalOnceData, ssod);
-}
-
-/* No longer prototyped in soup-misc.h, because it's only used by
- * soup-connection.c, and will be going away once that usage is removed.
- */
-guint soup_signal_connect_once (gpointer instance, const char *detailed_signal,
- GCallback c_handler, gpointer data);
-
-guint
-soup_signal_connect_once (gpointer instance, const char *detailed_signal,
- GCallback c_handler, gpointer data)
-{
- SoupSignalOnceData *ssod;
- GClosure *closure;
-
- g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
- g_return_val_if_fail (detailed_signal != NULL, 0);
- g_return_val_if_fail (c_handler != NULL, 0);
-
- ssod = g_slice_new0 (SoupSignalOnceData);
- ssod->instance = instance;
- g_object_weak_ref (G_OBJECT (instance), signal_once_object_destroyed, ssod);
-
- closure = g_cclosure_new (c_handler, data, NULL);
- g_closure_set_meta_marshal (closure, ssod, signal_once_metamarshal);
-
- ssod->signal_id = g_signal_connect_closure (instance, detailed_signal,
- closure, FALSE);
- return ssod->signal_id;
-}
-
/**
* soup_add_io_watch:
* @async_context: the #GMainContext to dispatch the I/O watch in, or
diff --git a/libsoup/soup-misc.h b/libsoup/soup-misc.h
index 162ddacd..f8dde104 100644
--- a/libsoup/soup-misc.h
+++ b/libsoup/soup-misc.h
@@ -33,6 +33,8 @@ guint soup_str_case_hash (gconstpointer key);
gboolean soup_str_case_equal (gconstpointer v1,
gconstpointer v2);
+#define _SOUP_ATOMIC_INTERN_STRING(variable, value) (g_once_init_enter ((gsize *)&variable) ? (g_once_init_leave ((gsize *)&variable, GPOINTER_TO_SIZE (g_intern_static_string (value))), variable) : variable)
+
/* SSL stuff */
extern const gboolean soup_ssl_supported;
diff --git a/libsoup/soup-proxy-resolver-gnome.c b/libsoup/soup-proxy-resolver-gnome.c
index 4371d5a6..1873fbbb 100644
--- a/libsoup/soup-proxy-resolver-gnome.c
+++ b/libsoup/soup-proxy-resolver-gnome.c
@@ -13,8 +13,7 @@
#include <stdlib.h>
#include "soup-proxy-resolver-gnome.h"
-#include "soup-address.h"
-#include "soup-dns.h"
+#include "soup-proxy-uri-resolver.h"
#include "soup-message.h"
#include "soup-misc.h"
#include "soup-session-feature.h"
@@ -37,15 +36,16 @@ typedef enum {
G_LOCK_DEFINE_STATIC (resolver_gnome);
static SoupProxyResolverGNOMEMode proxy_mode;
static GConfClient *gconf_client;
+static char *proxy_user, *proxy_password;
static pxProxyFactory *libproxy_factory;
static GThreadPool *libproxy_threadpool;
-static void soup_proxy_resolver_gnome_interface_init (SoupProxyResolverInterface *proxy_resolver_interface);
+static void soup_proxy_resolver_gnome_interface_init (SoupProxyURIResolverInterface *proxy_resolver_interface);
G_DEFINE_TYPE_EXTENDED (SoupProxyResolverGNOME, soup_proxy_resolver_gnome, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, NULL)
- G_IMPLEMENT_INTERFACE (SOUP_TYPE_PROXY_RESOLVER, soup_proxy_resolver_gnome_interface_init))
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_PROXY_URI_RESOLVER, soup_proxy_resolver_gnome_interface_init))
static void gconf_value_changed (GConfClient *client, const char *key,
GConfValue *value, gpointer user_data);
@@ -53,16 +53,16 @@ static void update_proxy_settings (void);
static void libproxy_threadpool_func (gpointer thread_data, gpointer user_data);
-static void get_proxy_async (SoupProxyResolver *proxy_resolver,
- SoupMessage *msg,
- GMainContext *async_context,
- GCancellable *cancellable,
- SoupProxyResolverCallback callback,
- gpointer user_data);
-static guint get_proxy_sync (SoupProxyResolver *proxy_resolver,
- SoupMessage *msg,
- GCancellable *cancellable,
- SoupAddress **addr);
+static void get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data);
+static guint get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri);
typedef struct {
GMutex *lock;
@@ -149,16 +149,19 @@ soup_proxy_resolver_gnome_class_init (SoupProxyResolverGNOMEClass *gconf_class)
}
static void
-soup_proxy_resolver_gnome_interface_init (SoupProxyResolverInterface *proxy_resolver_interface)
+soup_proxy_resolver_gnome_interface_init (SoupProxyURIResolverInterface *proxy_uri_resolver_interface)
{
- proxy_resolver_interface->get_proxy_async = get_proxy_async;
- proxy_resolver_interface->get_proxy_sync = get_proxy_sync;
+ proxy_uri_resolver_interface->get_proxy_uri_async = get_proxy_uri_async;
+ proxy_uri_resolver_interface->get_proxy_uri_sync = get_proxy_uri_sync;
}
#define SOUP_GCONF_PROXY_MODE "/system/proxy/mode"
#define SOUP_GCONF_PROXY_AUTOCONFIG_URL "/system/proxy/autoconfig_url"
#define SOUP_GCONF_HTTP_PROXY_HOST "/system/http_proxy/host"
#define SOUP_GCONF_HTTP_PROXY_PORT "/system/http_proxy/port"
+#define SOUP_GCONF_HTTP_USE_AUTH "/system/http_proxy/use_authentication"
+#define SOUP_GCONF_HTTP_PROXY_USER "/system/http_proxy/authentication_user"
+#define SOUP_GCONF_HTTP_PROXY_PASSWORD "/system/http_proxy/authentication_password"
#define SOUP_GCONF_HTTPS_PROXY_HOST "/system/proxy/secure_host"
#define SOUP_GCONF_HTTPS_PROXY_PORT "/system/proxy/secure_port"
#define SOUP_GCONF_USE_SAME_PROXY "/system/http_proxy/use_same_proxy"
@@ -179,6 +182,16 @@ update_proxy_settings (void)
/* resolver_gnome is locked */
+ if (proxy_user) {
+ g_free (proxy_user);
+ proxy_user = NULL;
+ }
+ if (proxy_password) {
+ memset (proxy_password, 0, strlen (proxy_password));
+ g_free (proxy_password);
+ proxy_password = NULL;
+ }
+
/* Get new settings */
mode = gconf_client_get_string (
gconf_client, SOUP_GCONF_PROXY_MODE, NULL);
@@ -244,6 +257,13 @@ update_proxy_settings (void)
}
g_free (host);
}
+
+ if (gconf_client_get_bool (gconf_client, SOUP_GCONF_HTTP_USE_AUTH, NULL)) {
+ proxy_user = gconf_client_get_string (
+ gconf_client, SOUP_GCONF_HTTP_PROXY_USER, NULL);
+ proxy_password = gconf_client_get_string (
+ gconf_client, SOUP_GCONF_HTTP_PROXY_PASSWORD, NULL);
+ }
}
ignore = gconf_client_get_list (
@@ -304,33 +324,31 @@ gconf_value_changed (GConfClient *client, const char *key,
}
static guint
-get_proxy_for_message (SoupMessage *msg, SoupAddress **addr)
+get_proxy_for_uri (SoupURI *uri, SoupURI **proxy_uri)
{
- char *msg_uri, **proxies;
- SoupURI *proxy_uri;
+ char *uristr, **proxies;
gboolean got_proxy;
int i;
+ *proxy_uri = NULL;
+
/* resolver_gnome is locked */
- msg_uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
- proxies = px_proxy_factory_get_proxies (libproxy_factory, msg_uri);
- g_free (msg_uri);
+ uristr = soup_uri_to_string (uri, FALSE);
+ proxies = px_proxy_factory_get_proxies (libproxy_factory, uristr);
+ g_free (uristr);
- if (!proxies) {
- *addr = NULL;
+ if (!proxies)
return SOUP_STATUS_OK;
- }
got_proxy = FALSE;
for (i = 0; proxies[i]; i++) {
if (!strcmp (proxies[i], "direct://")) {
- proxy_uri = NULL;
got_proxy = TRUE;
break;
}
if (strncmp (proxies[i], "http://", 7) == 0) {
- proxy_uri = soup_uri_new (proxies[i]);
+ *proxy_uri = soup_uri_new (proxies[i]);
got_proxy = TRUE;
break;
}
@@ -339,55 +357,45 @@ get_proxy_for_message (SoupMessage *msg, SoupAddress **addr)
free (proxies[i]);
free (proxies);
- if (!got_proxy) {
- *addr = NULL;
- return SOUP_STATUS_CANT_RESOLVE_PROXY;
- }
+ if (got_proxy) {
+ if (*proxy_uri && proxy_user) {
+ soup_uri_set_user (*proxy_uri, proxy_user);
+ soup_uri_set_password (*proxy_uri, proxy_password);
+ }
- if (proxy_uri) {
- *addr = soup_address_new (proxy_uri->host, proxy_uri->port);
- soup_uri_free (proxy_uri);
+ return SOUP_STATUS_OK;
} else
- *addr = NULL;
- return SOUP_STATUS_OK;
+ return SOUP_STATUS_CANT_RESOLVE_PROXY;
}
typedef struct {
- SoupProxyResolver *proxy_resolver;
- SoupMessage *msg;
- SoupAddress *addr;
+ SoupProxyURIResolver *proxy_uri_resolver;
+ SoupURI *uri, *proxy_uri;
GMainContext *async_context;
GCancellable *cancellable;
guint status;
- SoupProxyResolverCallback callback;
+ SoupProxyURIResolverCallback callback;
gpointer user_data;
} SoupGNOMEAsyncData;
-static void
-resolved_address (SoupAddress *addr, guint status, gpointer data)
+static gboolean
+resolved_proxy (gpointer data)
{
SoupGNOMEAsyncData *sgad = data;
- sgad->callback (sgad->proxy_resolver, sgad->msg,
- soup_status_proxify (status), addr,
- sgad->user_data);
- g_object_unref (sgad->proxy_resolver);
- g_object_unref (sgad->msg);
+ sgad->callback (sgad->proxy_uri_resolver, sgad->status,
+ sgad->proxy_uri, sgad->user_data);
+ g_object_unref (sgad->proxy_uri_resolver);
+ if (sgad->uri)
+ soup_uri_free (sgad->uri);
if (sgad->async_context)
g_main_context_unref (sgad->async_context);
if (sgad->cancellable)
g_object_unref (sgad->cancellable);
- if (addr)
- g_object_unref (addr);
+ if (sgad->proxy_uri)
+ soup_uri_free (sgad->proxy_uri);
g_slice_free (SoupGNOMEAsyncData, sgad);
-}
-static gboolean
-idle_resolved_address (gpointer data)
-{
- SoupGNOMEAsyncData *sgad = data;
-
- resolved_address (sgad->addr, sgad->status, data);
return FALSE;
}
@@ -396,36 +404,35 @@ libproxy_threadpool_func (gpointer user_data, gpointer thread_data)
{
SoupGNOMEAsyncData *sgad = user_data;
- /* We don't just call get_libproxy_proxy_for_message here,
- * since it's possible that the proxy mode has changed...
+ /* We don't just call get_proxy_for_uri here, since it's
+ * possible that the proxy mode has changed...
*/
- sgad->status = get_proxy_sync (NULL, sgad->msg, sgad->cancellable,
- &sgad->addr);
- soup_add_completion (sgad->async_context, idle_resolved_address, sgad);
+ sgad->status = get_proxy_uri_sync (sgad->proxy_uri_resolver,
+ sgad->uri, sgad->cancellable,
+ &sgad->proxy_uri);
+ soup_add_completion (sgad->async_context, resolved_proxy, sgad);
}
static void
-get_proxy_async (SoupProxyResolver *proxy_resolver,
- SoupMessage *msg,
- GMainContext *async_context,
- GCancellable *cancellable,
- SoupProxyResolverCallback callback,
- gpointer user_data)
+get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
{
SoupGNOMEAsyncData *sgad;
sgad = g_slice_new0 (SoupGNOMEAsyncData);
- sgad->proxy_resolver = g_object_ref (proxy_resolver);
- sgad->msg = g_object_ref (msg);
+ sgad->proxy_uri_resolver = g_object_ref (proxy_uri_resolver);
sgad->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- sgad->async_context = async_context ? g_main_context_ref (async_context) : NULL;
sgad->callback = callback;
sgad->user_data = user_data;
G_LOCK (resolver_gnome);
switch (proxy_mode) {
case SOUP_PROXY_RESOLVER_GNOME_MODE_NONE:
- sgad->addr = NULL;
+ sgad->proxy_uri = NULL;
sgad->status = SOUP_STATUS_OK;
break;
@@ -433,48 +440,37 @@ get_proxy_async (SoupProxyResolver *proxy_resolver,
/* We know libproxy won't do PAC or WPAD in this case,
* so we can make a "blocking" call to it.
*/
- sgad->status = get_proxy_for_message (msg, &sgad->addr);
+ sgad->status = get_proxy_for_uri (uri, &sgad->proxy_uri);
break;
case SOUP_PROXY_RESOLVER_GNOME_MODE_AUTO:
/* FIXME: cancellable */
+ sgad->uri = soup_uri_copy (uri);
+ sgad->async_context = async_context ? g_main_context_ref (async_context) : NULL;
g_thread_pool_push (libproxy_threadpool, sgad, NULL);
G_UNLOCK (resolver_gnome);
return;
}
G_UNLOCK (resolver_gnome);
- if (sgad->addr) {
- soup_address_resolve_async (sgad->addr, async_context,
- cancellable, resolved_address,
- sgad);
- } else
- soup_add_idle (async_context, idle_resolved_address, sgad);
+ soup_add_completion (async_context, resolved_proxy, sgad);
}
static guint
-get_proxy_sync (SoupProxyResolver *proxy_resolver,
- SoupMessage *msg,
- GCancellable *cancellable,
- SoupAddress **addr)
+get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri)
{
guint status;
G_LOCK (resolver_gnome);
if (proxy_mode == SOUP_PROXY_RESOLVER_GNOME_MODE_NONE) {
- *addr = NULL;
+ *proxy_uri = NULL;
status = SOUP_STATUS_OK;
} else
- status = get_proxy_for_message (msg, addr);
+ status = get_proxy_for_uri (uri, proxy_uri);
G_UNLOCK (resolver_gnome);
- if (!*addr || status != SOUP_STATUS_OK)
- return status;
-
- status = soup_address_resolve_sync (*addr, cancellable);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- g_object_unref (*addr);
- *addr = NULL;
- }
- return soup_status_proxify (status);
+ return status;
}
diff --git a/libsoup/soup-proxy-resolver-gnome.h b/libsoup/soup-proxy-resolver-gnome.h
index 9f29842c..2f879216 100644
--- a/libsoup/soup-proxy-resolver-gnome.h
+++ b/libsoup/soup-proxy-resolver-gnome.h
@@ -6,7 +6,6 @@
#ifndef SOUP_PROXY_RESOLVER_GNOME_H
#define SOUP_PROXY_RESOLVER_GNOME_H 1
-#include "soup-proxy-resolver.h"
#include "soup-gnome-features.h"
/* SOUP_TYPE_PROXY_RESOLVER_GNOME and soup_proxy_resolver_gnome_get_type()
diff --git a/libsoup/soup-proxy-resolver.c b/libsoup/soup-proxy-resolver.c
index 630c10c1..33a908c0 100644
--- a/libsoup/soup-proxy-resolver.c
+++ b/libsoup/soup-proxy-resolver.c
@@ -10,7 +10,14 @@
#endif
#include "soup-proxy-resolver.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-address.h"
+#include "soup-message.h"
#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+static void soup_proxy_resolver_interface_init (GTypeInterface *interface);
+static void soup_proxy_resolver_uri_resolver_interface_init (SoupProxyURIResolverInterface *uri_resolver_interface);
GType
soup_proxy_resolver_get_type (void)
@@ -22,7 +29,7 @@ soup_proxy_resolver_get_type (void)
g_type_register_static_simple (G_TYPE_INTERFACE,
g_intern_static_string ("SoupProxyResolver"),
sizeof (SoupProxyResolverInterface),
- (GClassInitFunc)NULL,
+ (GClassInitFunc)soup_proxy_resolver_interface_init,
0,
(GInstanceInitFunc)NULL,
(GTypeFlags) 0);
@@ -33,20 +40,44 @@ soup_proxy_resolver_get_type (void)
return g_define_type_id__volatile;
}
-/**
- * soup_proxy_resolver_get_proxy_async:
- * @proxy_resolver: the #SoupProxyResolver
- * @msg: the #SoupMessage you want a proxy for
- * @async_context: the #GMainContext to invoke @callback in
- * @cancellable: a #GCancellable, or %NULL
- * @callback: callback to invoke with the proxy address
- * @user_data: data for @callback
- *
- * Asynchronously determines a proxy server address to use for @msg
- * and calls @callback.
- *
- * Since: 2.26
- **/
+static void
+proxy_resolver_interface_check (gpointer func_data, gpointer g_iface)
+{
+ GTypeInterface *interface = g_iface;
+ GTypeClass *klass;
+
+ if (interface->g_type != SOUP_TYPE_PROXY_RESOLVER)
+ return;
+
+ klass = g_type_class_peek (interface->g_instance_type);
+ /* If the class hasn't already declared that it implements
+ * SoupProxyURIResolver, add our own compat implementation.
+ */
+ if (!g_type_interface_peek (klass, SOUP_TYPE_PROXY_URI_RESOLVER)) {
+ const GInterfaceInfo uri_resolver_interface_info = {
+ (GInterfaceInitFunc) soup_proxy_resolver_uri_resolver_interface_init, NULL, NULL
+ };
+ g_type_add_interface_static (interface->g_instance_type,
+ SOUP_TYPE_PROXY_URI_RESOLVER,
+ &uri_resolver_interface_info);
+ }
+}
+
+
+static void
+soup_proxy_resolver_interface_init (GTypeInterface *interface)
+{
+ /* Add an interface_check where we can kludgily add the
+ * SoupProxyURIResolver interface to all SoupProxyResolvers.
+ * (SoupProxyResolver can't just implement
+ * SoupProxyURIResolver itself because interface types can't
+ * implement other interfaces.) This is an ugly hack, but it
+ * only gets used if someone actually creates a
+ * SoupProxyResolver...
+ */
+ g_type_add_interface_check (NULL, proxy_resolver_interface_check);
+}
+
void
soup_proxy_resolver_get_proxy_async (SoupProxyResolver *proxy_resolver,
SoupMessage *msg,
@@ -61,22 +92,6 @@ soup_proxy_resolver_get_proxy_async (SoupProxyResolver *proxy_resolver,
callback, user_data);
}
-/**
- * soup_proxy_resolver_get_proxy_sync:
- * @proxy_resolver: the #SoupProxyResolver
- * @msg: the #SoupMessage you want a proxy for
- * @cancellable: a #GCancellable, or %NULL
- * @addr: on return, will contain the proxy address
- *
- * Synchronously determines a proxy server address to use for @msg. If
- * @msg should be sent via proxy, *@addr will be set to the address of
- * the proxy, else it will be set to %NULL.
- *
- * Return value: SOUP_STATUS_OK if successful, or a transport-level
- * error.
- *
- * Since: 2.26
- **/
guint
soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
SoupMessage *msg,
@@ -86,3 +101,90 @@ soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
return SOUP_PROXY_RESOLVER_GET_CLASS (proxy_resolver)->
get_proxy_sync (proxy_resolver, msg, cancellable, addr);
}
+
+/* SoupProxyURIResolver implementation */
+
+static SoupURI *
+uri_from_address (SoupAddress *addr)
+{
+ SoupURI *proxy_uri;
+
+ proxy_uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (proxy_uri, SOUP_URI_SCHEME_HTTP);
+ soup_uri_set_host (proxy_uri, soup_address_get_name (addr));
+ soup_uri_set_port (proxy_uri, soup_address_get_port (addr));
+ return proxy_uri;
+}
+
+typedef struct {
+ SoupProxyURIResolverCallback callback;
+ gpointer user_data;
+} ProxyURIResolverAsyncData;
+
+static void
+compat_got_proxy (SoupProxyResolver *proxy_resolver,
+ SoupMessage *msg, guint status, SoupAddress *proxy_addr,
+ gpointer user_data)
+{
+ ProxyURIResolverAsyncData *purad = user_data;
+ SoupURI *proxy_uri;
+
+ proxy_uri = proxy_addr ? uri_from_address (proxy_addr) : NULL;
+ purad->callback (SOUP_PROXY_URI_RESOLVER (proxy_resolver),
+ status, proxy_uri, purad->user_data);
+ g_object_unref (msg);
+ if (proxy_uri)
+ soup_uri_free (proxy_uri);
+ g_slice_free (ProxyURIResolverAsyncData, purad);
+}
+
+static void
+compat_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri, GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
+{
+ SoupMessage *dummy_msg;
+ ProxyURIResolverAsyncData *purad;
+
+ dummy_msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+
+ purad = g_slice_new (ProxyURIResolverAsyncData);
+ purad->callback = callback;
+ purad->user_data = user_data;
+
+ soup_proxy_resolver_get_proxy_async (
+ SOUP_PROXY_RESOLVER (proxy_uri_resolver), dummy_msg,
+ async_context, cancellable,
+ compat_got_proxy, purad);
+}
+
+static guint
+compat_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri, GCancellable *cancellable,
+ SoupURI **proxy_uri)
+{
+ SoupMessage *dummy_msg;
+ SoupAddress *proxy_addr = NULL;
+ guint status;
+
+ dummy_msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
+ status = soup_proxy_resolver_get_proxy_sync (
+ SOUP_PROXY_RESOLVER (proxy_uri_resolver), dummy_msg,
+ cancellable, &proxy_addr);
+ g_object_unref (dummy_msg);
+ if (!proxy_addr)
+ return status;
+
+ *proxy_uri = uri_from_address (proxy_addr);
+ g_object_unref (proxy_addr);
+ return status;
+}
+
+static void
+soup_proxy_resolver_uri_resolver_interface_init (SoupProxyURIResolverInterface *uri_resolver_interface)
+{
+ uri_resolver_interface->get_proxy_uri_async = compat_get_proxy_uri_async;
+ uri_resolver_interface->get_proxy_uri_sync = compat_get_proxy_uri_sync;
+}
diff --git a/libsoup/soup-proxy-resolver.h b/libsoup/soup-proxy-resolver.h
index f91f3e14..161ae762 100644
--- a/libsoup/soup-proxy-resolver.h
+++ b/libsoup/soup-proxy-resolver.h
@@ -11,6 +11,8 @@
G_BEGIN_DECLS
+#ifndef LIBSOUP_DISABLE_DEPRECATED
+
#define SOUP_TYPE_PROXY_RESOLVER (soup_proxy_resolver_get_type ())
#define SOUP_PROXY_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_RESOLVER, SoupProxyResolver))
#define SOUP_PROXY_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_RESOLVER, SoupProxyResolverInterface))
@@ -48,6 +50,8 @@ guint soup_proxy_resolver_get_proxy_sync (SoupProxyResolver *proxy_resolver,
GCancellable *cancellable,
SoupAddress **addr);
+#endif
+
G_END_DECLS
#endif /*SOUP_PROXY_RESOLVER_H*/
diff --git a/libsoup/soup-proxy-uri-resolver.c b/libsoup/soup-proxy-uri-resolver.c
new file mode 100644
index 00000000..bb86f182
--- /dev/null
+++ b/libsoup/soup-proxy-uri-resolver.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-proxy-uri-resolver.c: HTTP proxy resolver interface, take 2
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-proxy-uri-resolver.h"
+#include "soup-session-feature.h"
+
+GType
+soup_proxy_uri_resolver_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ GType g_define_type_id =
+ g_type_register_static_simple (G_TYPE_INTERFACE,
+ g_intern_static_string ("SoupProxyURIResolver"),
+ sizeof (SoupProxyURIResolverInterface),
+ (GClassInitFunc)NULL,
+ 0,
+ (GInstanceInitFunc)NULL,
+ (GTypeFlags) 0);
+ g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+ return g_define_type_id__volatile;
+}
+
+/**
+ * SoupProxyURIResolverCallback:
+ * @resolver: the #SoupProxyURIResolver
+ * @status: a #SoupKnownStatusCode
+ * @proxy_uri: the resolved proxy URI, or %NULL
+ * @user_data: data passed to soup_proxy_uri_resolver_get_proxy_uri_async()
+ *
+ * Callback for soup_proxy_uri_resolver_get_proxy_uri_async()
+ **/
+
+/**
+ * soup_proxy_uri_resolver_get_proxy_uri_async:
+ * @proxy_uri_resolver: the #SoupProxyURIResolver
+ * @uri: the #SoupURI you want a proxy for
+ * @async_context: the #GMainContext to invoke @callback in
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to invoke with the proxy address
+ * @user_data: data for @callback
+ *
+ * Asynchronously determines a proxy URI to use for @msg and calls
+ * @callback.
+ *
+ * Since: 2.26.3
+ **/
+void
+soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data)
+{
+ SOUP_PROXY_URI_RESOLVER_GET_CLASS (proxy_uri_resolver)->
+ get_proxy_uri_async (proxy_uri_resolver, uri,
+ async_context, cancellable,
+ callback, user_data);
+}
+
+/**
+ * soup_proxy_uri_resolver_get_proxy_uri_sync:
+ * @proxy_uri_resolver: the #SoupProxyURIResolver
+ * @uri: the #SoupURI you want a proxy for
+ * @cancellable: a #GCancellable, or %NULL
+ * @proxy_uri: on return, will contain the proxy URI
+ *
+ * Synchronously determines a proxy URI to use for @uri. If @uri
+ * should be sent via proxy, *@proxy_uri will be set to the URI of the
+ * proxy, else it will be set to %NULL.
+ *
+ * Return value: %SOUP_STATUS_OK if successful, or a transport-level
+ * error.
+ *
+ * Since: 2.26.3
+ **/
+guint
+soup_proxy_uri_resolver_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri)
+{
+ return SOUP_PROXY_URI_RESOLVER_GET_CLASS (proxy_uri_resolver)->
+ get_proxy_uri_sync (proxy_uri_resolver, uri, cancellable, proxy_uri);
+}
diff --git a/libsoup/soup-proxy-uri-resolver.h b/libsoup/soup-proxy-uri-resolver.h
new file mode 100644
index 00000000..97476cb0
--- /dev/null
+++ b/libsoup/soup-proxy-uri-resolver.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef SOUP_PROXY_URI_RESOLVER_H
+#define SOUP_PROXY_URI_RESOLVER_H 1
+
+#include <libsoup/soup-proxy-resolver.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_PROXY_URI_RESOLVER (soup_proxy_uri_resolver_get_type ())
+#define SOUP_PROXY_URI_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_PROXY_URI_RESOLVER, SoupProxyURIResolver))
+#define SOUP_PROXY_URI_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_PROXY_URI_RESOLVER, SoupProxyURIResolverInterface))
+#define SOUP_IS_PROXY_URI_RESOLVER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_PROXY_URI_RESOLVER))
+#define SOUP_IS_PROXY_URI_RESOLVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_PROXY_URI_RESOLVER))
+#define SOUP_PROXY_URI_RESOLVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), SOUP_TYPE_PROXY_URI_RESOLVER, SoupProxyURIResolverInterface))
+
+typedef struct _SoupProxyURIResolver SoupProxyURIResolver;
+
+typedef void (*SoupProxyURIResolverCallback) (SoupProxyURIResolver *,
+ guint, SoupURI *, gpointer);
+
+typedef struct {
+ GTypeInterface base;
+
+ /* virtual methods */
+ void (*get_proxy_uri_async) (SoupProxyURIResolver *, SoupURI *,
+ GMainContext *, GCancellable *,
+ SoupProxyURIResolverCallback, gpointer);
+ guint (*get_proxy_uri_sync) (SoupProxyURIResolver *, SoupURI *,
+ GCancellable *, SoupURI **);
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupProxyURIResolverInterface;
+
+GType soup_proxy_uri_resolver_get_type (void);
+
+void soup_proxy_uri_resolver_get_proxy_uri_async (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GMainContext *async_context,
+ GCancellable *cancellable,
+ SoupProxyURIResolverCallback callback,
+ gpointer user_data);
+guint soup_proxy_uri_resolver_get_proxy_uri_sync (SoupProxyURIResolver *proxy_uri_resolver,
+ SoupURI *uri,
+ GCancellable *cancellable,
+ SoupURI **proxy_uri);
+
+G_END_DECLS
+
+#endif /*SOUP_PROXY_URI_RESOLVER_H*/
diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 97d4f2f1..c57d3037 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -15,6 +15,8 @@
#include "soup-address.h"
#include "soup-message-private.h"
#include "soup-misc.h"
+#include "soup-proxy-uri-resolver.h"
+#include "soup-uri.h"
/**
* SECTION:soup-session-async
@@ -108,71 +110,69 @@ soup_session_async_new_with_options (const char *optname1, ...)
return session;
}
-
-static void
-resolved_msg_addr (SoupAddress *addr, guint status, gpointer user_data)
+static gboolean
+item_failed (SoupMessageQueueItem *item, guint status)
{
- SoupMessageQueueItem *item = user_data;
- SoupSession *session = item->session;
-
if (item->removed) {
- /* Message was cancelled before its address resolved */
soup_message_queue_item_unref (item);
- return;
+ return TRUE;
}
if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- soup_session_cancel_message (session, item->msg, status);
+ if (status != SOUP_STATUS_CANCELLED)
+ soup_session_cancel_message (item->session, item->msg, status);
soup_message_queue_item_unref (item);
- return;
+ return TRUE;
}
- item->msg_addr = g_object_ref (addr);
- item->resolving_msg_addr = FALSE;
-
- soup_message_queue_item_unref (item);
-
- /* If we got here we know session still exists */
- run_queue ((SoupSessionAsync *)session);
+ return FALSE;
}
static void
-resolve_msg_addr (SoupMessageQueueItem *item)
+resolved_proxy_addr (SoupAddress *addr, guint status, gpointer user_data)
{
- if (item->resolving_msg_addr)
+ SoupMessageQueueItem *item = user_data;
+ SoupSession *session = item->session;
+
+ if (item_failed (item, status))
return;
- item->resolving_msg_addr = TRUE;
- soup_message_queue_item_ref (item);
- soup_address_resolve_async (soup_message_get_address (item->msg),
- soup_session_get_async_context (item->session),
- item->cancellable,
- resolved_msg_addr, item);
+ item->proxy_addr = g_object_ref (addr);
+ item->resolving_proxy_addr = FALSE;
+ item->resolved_proxy_addr = TRUE;
+
+ soup_message_queue_item_unref (item);
+
+ /* If we got here we know session still exists */
+ run_queue ((SoupSessionAsync *)session);
}
static void
-resolved_proxy_addr (SoupProxyResolver *proxy_resolver, SoupMessage *msg,
- guint status, SoupAddress *proxy_addr, gpointer user_data)
+resolved_proxy_uri (SoupProxyURIResolver *proxy_resolver,
+ guint status, SoupURI *proxy_uri, gpointer user_data)
{
SoupMessageQueueItem *item = user_data;
SoupSession *session = item->session;
- if (item->removed) {
- /* Message was cancelled before its proxy addr resolved */
- soup_message_queue_item_unref (item);
+ if (item_failed (item, status))
return;
- }
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- soup_session_cancel_message (session, item->msg, status);
- soup_message_queue_item_unref (item);
+ if (proxy_uri) {
+ SoupAddress *proxy_addr;
+
+ item->proxy_uri = soup_uri_copy (proxy_uri);
+ proxy_addr = soup_address_new (proxy_uri->host,
+ proxy_uri->port);
+ soup_address_resolve_async (proxy_addr,
+ soup_session_get_async_context (session),
+ item->cancellable,
+ resolved_proxy_addr, item);
+ g_object_unref (proxy_addr);
return;
}
item->resolving_proxy_addr = FALSE;
item->resolved_proxy_addr = TRUE;
- item->proxy_addr = proxy_addr ? g_object_ref (proxy_addr) : NULL;
-
soup_message_queue_item_unref (item);
/* If we got here we know session still exists */
@@ -181,17 +181,17 @@ resolved_proxy_addr (SoupProxyResolver *proxy_resolver, SoupMessage *msg,
static void
resolve_proxy_addr (SoupMessageQueueItem *item,
- SoupProxyResolver *proxy_resolver)
+ SoupProxyURIResolver *proxy_resolver)
{
if (item->resolving_proxy_addr)
return;
item->resolving_proxy_addr = TRUE;
soup_message_queue_item_ref (item);
- soup_proxy_resolver_get_proxy_async (proxy_resolver, item->msg,
- soup_session_get_async_context (item->session),
- item->cancellable,
- resolved_proxy_addr, item);
+ soup_proxy_uri_resolver_get_proxy_uri_async (
+ proxy_resolver, soup_message_get_uri (item->msg),
+ soup_session_get_async_context (item->session),
+ item->cancellable, resolved_proxy_uri, item);
}
static void
@@ -203,12 +203,68 @@ connection_closed (SoupConnection *conn, gpointer session)
do_idle_run_queue (session);
}
+typedef struct {
+ SoupSession *session;
+ SoupConnection *conn;
+ SoupMessageQueueItem *item;
+} SoupSessionAsyncTunnelData;
+
+static void
+tunnel_connected (SoupMessage *msg, gpointer user_data)
+{
+ SoupSessionAsyncTunnelData *data = user_data;
+
+ if (SOUP_MESSAGE_IS_STARTING (msg)) {
+ soup_session_send_queue_item (data->session, data->item, data->conn);
+ return;
+ }
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ soup_session_connection_failed (data->session, data->conn,
+ msg->status_code);
+ goto done;
+ }
+
+ if (!soup_connection_start_ssl (data->conn)) {
+ soup_session_connection_failed (data->session, data->conn,
+ SOUP_STATUS_SSL_FAILED);
+ goto done;
+ }
+
+ g_signal_connect (data->conn, "disconnected",
+ G_CALLBACK (connection_closed), data->session);
+ soup_connection_set_state (data->conn, SOUP_CONNECTION_IDLE);
+
+ do_idle_run_queue (data->session);
+
+done:
+ g_object_unref (data->session);
+ soup_message_queue_item_unref (data->item);
+ g_slice_free (SoupSessionAsyncTunnelData, data);
+}
+
static void
-got_connection (SoupConnection *conn, guint status, gpointer user_data)
+got_connection (SoupConnection *conn, guint status, gpointer session)
{
- SoupSession *session = user_data;
+ SoupAddress *tunnel_addr;
if (status == SOUP_STATUS_OK) {
+ tunnel_addr = soup_connection_get_tunnel_addr (conn);
+ if (tunnel_addr) {
+ SoupSessionAsyncTunnelData *data;
+
+ data = g_slice_new (SoupSessionAsyncTunnelData);
+ data->session = session;
+ data->conn = conn;
+ data->item = soup_session_make_connect_message (session, tunnel_addr);
+ g_signal_connect (data->item->msg, "finished",
+ G_CALLBACK (tunnel_connected), data);
+ g_signal_connect (data->item->msg, "restarted",
+ G_CALLBACK (tunnel_connected), data);
+ soup_session_send_queue_item (session, data->item, conn);
+ return;
+ }
+
g_signal_connect (conn, "disconnected",
G_CALLBACK (connection_closed), session);
@@ -222,8 +278,9 @@ got_connection (SoupConnection *conn, guint status, gpointer user_data)
* idle pool and then just run the queue and see what
* happens.
*/
- soup_connection_release (conn);
- }
+ soup_connection_set_state (conn, SOUP_CONNECTION_IDLE);
+ } else
+ soup_session_connection_failed (session, conn, status);
/* Even if the connection failed, we run the queue, since
* there may have been messages waiting for the connection
@@ -239,13 +296,12 @@ run_queue (SoupSessionAsync *sa)
SoupSession *session = SOUP_SESSION (sa);
SoupMessageQueue *queue = soup_session_get_queue (session);
SoupMessageQueueItem *item;
- SoupProxyResolver *proxy_resolver =
+ SoupProxyURIResolver *proxy_resolver =
soup_session_get_proxy_resolver (session);
SoupMessage *msg;
SoupMessageIOStatus cur_io_status = SOUP_MESSAGE_IO_STATUS_CONNECTING;
SoupConnection *conn;
gboolean try_pruning = TRUE, should_prune = FALSE;
- gboolean is_new;
try_again:
for (item = soup_message_queue_first (queue);
@@ -253,30 +309,29 @@ run_queue (SoupSessionAsync *sa)
item = soup_message_queue_next (queue, item)) {
msg = item->msg;
+ /* CONNECT messages are handled specially */
+ if (msg->method == SOUP_METHOD_CONNECT)
+ continue;
+
if (soup_message_get_io_status (msg) != cur_io_status ||
soup_message_io_in_progress (msg))
continue;
- if (!item->msg_addr) {
- resolve_msg_addr (item);
- continue;
- }
if (proxy_resolver && !item->resolved_proxy_addr) {
resolve_proxy_addr (item, proxy_resolver);
continue;
}
- conn = soup_session_get_connection (session, msg,
- item->proxy_addr,
- &should_prune, &is_new);
+ conn = soup_session_get_connection (session, item,
+ &should_prune);
if (!conn)
continue;
- if (is_new) {
+ if (soup_connection_get_state (conn) == SOUP_CONNECTION_NEW) {
soup_connection_connect_async (conn, got_connection,
g_object_ref (session));
} else
- soup_connection_send_request (conn, msg);
+ soup_session_send_queue_item (session, item, conn);
}
if (item)
soup_message_queue_item_unref (item);
@@ -303,17 +358,6 @@ request_restarted (SoupMessage *req, gpointer user_data)
{
SoupMessageQueueItem *item = user_data;
- if (item->msg_addr &&
- item->msg_addr != soup_message_get_address (item->msg)) {
- g_object_unref (item->msg_addr);
- item->msg_addr = NULL;
- }
- if (item->proxy_addr) {
- g_object_unref (item->proxy_addr);
- item->proxy_addr = NULL;
- }
- item->resolved_proxy_addr = FALSE;
-
run_queue ((SoupSessionAsync *)item->session);
}
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
index 015704fa..591161f4 100644
--- a/libsoup/soup-session-private.h
+++ b/libsoup/soup-session-private.h
@@ -10,22 +10,29 @@
#include "soup-session.h"
#include "soup-connection.h"
#include "soup-message-queue.h"
-#include "soup-proxy-resolver.h"
+#include "soup-proxy-uri-resolver.h"
G_BEGIN_DECLS
/* "protected" methods for subclasses */
-SoupMessageQueue *soup_session_get_queue (SoupSession *session);
-
-SoupConnection *soup_session_get_connection (SoupSession *session,
- SoupMessage *msg,
- SoupAddress *proxy,
- gboolean *try_pruning,
- gboolean *is_new);
-gboolean soup_session_try_prune_connection (SoupSession *session);
-
-SoupProxyResolver *soup_session_get_proxy_resolver (SoupSession *session);
-SoupCache *soup_session_get_cache (SoupSession *session);
+SoupMessageQueue *soup_session_get_queue (SoupSession *session);
+
+SoupMessageQueueItem *soup_session_make_connect_message (SoupSession *session,
+ SoupAddress *server_addr);
+SoupConnection *soup_session_get_connection (SoupSession *session,
+ SoupMessageQueueItem *item,
+ gboolean *try_pruning);
+gboolean soup_session_try_prune_connection (SoupSession *session);
+void soup_session_connection_failed (SoupSession *session,
+ SoupConnection *conn,
+ guint status);
+
+SoupProxyURIResolver *soup_session_get_proxy_resolver (SoupSession *session);
+
+void soup_session_send_queue_item (SoupSession *session,
+ SoupMessageQueueItem *item,
+ SoupConnection *conn);
+SoupCache *soup_session_get_cache (SoupSession *session);
G_END_DECLS
diff --git a/libsoup/soup-session-sync.c b/libsoup/soup-session-sync.c
index bb8e336b..8c2e56ba 100644
--- a/libsoup/soup-session-sync.c
+++ b/libsoup/soup-session-sync.c
@@ -14,7 +14,9 @@
#include "soup-session-private.h"
#include "soup-address.h"
#include "soup-message-private.h"
+#include "soup-proxy-uri-resolver.h"
#include "soup-misc.h"
+#include "soup-uri.h"
/**
* SECTION:soup-session-sync
@@ -127,54 +129,93 @@ soup_session_sync_new_with_options (const char *optname1, ...)
return session;
}
+static gboolean
+tunnel_connect (SoupSession *session, SoupConnection *conn,
+ SoupAddress *tunnel_addr)
+{
+ SoupMessageQueueItem *item;
+ guint status;
+
+ item = soup_session_make_connect_message (session, tunnel_addr);
+ do
+ soup_session_send_queue_item (session, item, conn);
+ while (SOUP_MESSAGE_IS_STARTING (item->msg));
+
+ status = item->msg->status_code;
+ soup_message_queue_item_unref (item);
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (!soup_connection_start_ssl (conn))
+ status = SOUP_STATUS_SSL_FAILED;
+ }
+
+ if (SOUP_STATUS_IS_SUCCESSFUL (status))
+ return TRUE;
+ else {
+ soup_session_connection_failed (session, conn, status);
+ return FALSE;
+ }
+}
+
static SoupConnection *
-wait_for_connection (SoupSession *session, SoupMessage *msg)
+wait_for_connection (SoupMessageQueueItem *item)
{
+ SoupSession *session = item->session;
+ SoupMessage *msg = item->msg;
SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (session);
- gboolean try_pruning = FALSE, is_new = FALSE;
- SoupProxyResolver *proxy_resolver;
- SoupAddress *proxy_addr = NULL;
+ gboolean try_pruning = FALSE;
+ SoupProxyURIResolver *proxy_resolver;
+ SoupAddress *tunnel_addr;
SoupConnection *conn;
guint status;
proxy_resolver = soup_session_get_proxy_resolver (session);
- g_mutex_lock (priv->lock);
-
- try_again:
- if (proxy_resolver) {
- status = soup_proxy_resolver_get_proxy_sync (proxy_resolver, msg, NULL, &proxy_addr);
+ if (proxy_resolver && !item->resolved_proxy_addr) {
+ status = soup_proxy_uri_resolver_get_proxy_uri_sync (
+ proxy_resolver, soup_message_get_uri (msg),
+ item->cancellable, &item->proxy_uri);
if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- g_mutex_unlock (priv->lock);
- soup_session_cancel_message (session, msg, status);
+ if (status != SOUP_STATUS_CANCELLED)
+ soup_session_cancel_message (session, msg, status);
return NULL;
}
+
+ if (item->proxy_uri) {
+ item->proxy_addr = soup_address_new (
+ item->proxy_uri->host, item->proxy_uri->port);
+ status = soup_address_resolve_sync (item->proxy_addr,
+ item->cancellable);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ if (status != SOUP_STATUS_CANCELLED)
+ soup_session_cancel_message (session, msg, status);
+ return NULL;
+ }
+ }
+
+ item->resolved_proxy_addr = TRUE;
}
- conn = soup_session_get_connection (session, msg, proxy_addr,
- &try_pruning, &is_new);
- if (proxy_addr)
- g_object_unref (proxy_addr);
+ g_mutex_lock (priv->lock);
+
+ try_again:
+ conn = soup_session_get_connection (session, item, &try_pruning);
if (conn) {
- if (is_new) {
+ if (soup_connection_get_state (conn) == SOUP_CONNECTION_NEW) {
status = soup_connection_connect_sync (conn);
- /* If the connection attempt fails, SoupSession
- * will notice, unref conn, and set an error
- * status on msg. So all we need to do is just
- * not return the no-longer-valid connection.
- */
-
- if (status == SOUP_STATUS_TRY_AGAIN)
- goto try_again;
- else if (!SOUP_STATUS_IS_SUCCESSFUL (status))
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
+ soup_session_connection_failed (session, conn, status);
conn = NULL;
- else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
+ } else if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED) {
/* Message was cancelled while we were
* connecting.
*/
soup_connection_disconnect (conn);
conn = NULL;
- }
+ } else if ((tunnel_addr = soup_connection_get_tunnel_addr (conn))) {
+ if (!tunnel_connect (session, conn, tunnel_addr))
+ conn = NULL;
+ }
}
g_mutex_unlock (priv->lock);
@@ -200,29 +241,17 @@ static void
process_queue_item (SoupMessageQueueItem *item)
{
SoupSessionSyncPrivate *priv = SOUP_SESSION_SYNC_GET_PRIVATE (item->session);
- SoupMessage *msg = item->msg;
SoupConnection *conn;
- SoupAddress *addr;
- guint status;
do {
- /* Resolve address */
- addr = soup_message_get_address (msg);
- status = soup_address_resolve_sync (addr, item->cancellable);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- if (status != SOUP_STATUS_CANCELLED)
- soup_session_cancel_message (item->session, msg, status);
- break;
- }
-
- /* Get a connection */
- conn = wait_for_connection (item->session, msg);
+ conn = wait_for_connection (item);
if (!conn)
break;
- soup_connection_send_request (conn, msg);
+ soup_session_send_queue_item (item->session, item, conn);
g_cond_broadcast (priv->cond);
- } while (soup_message_get_io_status (msg) != SOUP_MESSAGE_IO_STATUS_FINISHED);
+ } while (soup_message_get_io_status (item->msg) !=
+ SOUP_MESSAGE_IO_STATUS_FINISHED);
}
static gboolean
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index b8841b30..992e3055 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -23,7 +23,9 @@
#include "soup-marshal.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
+#include "soup-misc.h"
#include "soup-proxy-resolver-static.h"
+#include "soup-proxy-uri-resolver.h"
#include "soup-session.h"
#include "soup-session-feature.h"
#include "soup-session-private.h"
@@ -58,6 +60,7 @@
**/
typedef struct {
+ SoupURI *uri;
SoupAddress *addr;
GSList *connections; /* CONTAINS: SoupConnection */
@@ -65,7 +68,7 @@ typedef struct {
} SoupSessionHost;
typedef struct {
- SoupProxyResolver *proxy_resolver;
+ SoupProxyURIResolver *proxy_resolver;
SoupCache *cache;
char *ssl_ca_file;
@@ -78,7 +81,7 @@ typedef struct {
GSList *features;
SoupAuthManager *auth_manager;
- GHashTable *hosts; /* SoupAddress -> SoupSessionHost */
+ GHashTable *hosts; /* char* -> SoupSessionHost */
GHashTable *conns; /* SoupConnection -> SoupSessionHost */
guint num_conns;
guint max_conns, max_conns_per_host;
@@ -155,8 +158,9 @@ soup_session_init (SoupSession *session)
priv->queue = soup_message_queue_new (session);
priv->host_lock = g_mutex_new ();
- priv->hosts = g_hash_table_new (soup_address_hash_by_ip,
- soup_address_equal_by_ip);
+ priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
+ soup_uri_host_equal,
+ NULL, (GDestroyNotify)free_host);
priv->conns = g_hash_table_new (NULL, NULL);
priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
@@ -172,28 +176,6 @@ soup_session_init (SoupSession *session)
soup_session_add_feature (session, SOUP_SESSION_FEATURE (priv->auth_manager));
}
-static gboolean
-foreach_free_host (gpointer key, gpointer host, gpointer data)
-{
- free_host (host);
- return TRUE;
-}
-
-static void
-cleanup_hosts (SoupSessionPrivate *priv)
-{
- GHashTable *old_hosts;
-
- g_mutex_lock (priv->host_lock);
- old_hosts = priv->hosts;
- priv->hosts = g_hash_table_new (soup_address_hash_by_ip,
- soup_address_equal_by_ip);
- g_mutex_unlock (priv->host_lock);
-
- g_hash_table_foreach_remove (old_hosts, foreach_free_host, NULL);
- g_hash_table_destroy (old_hosts);
-}
-
static void
dispose (GObject *object)
{
@@ -201,7 +183,6 @@ dispose (GObject *object)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
soup_session_abort (session);
- cleanup_hosts (priv);
while (priv->features)
soup_session_remove_feature (session, priv->features->data);
@@ -645,14 +626,13 @@ set_property (GObject *object, guint prop_id,
if (uri) {
soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
- priv->proxy_resolver = soup_proxy_resolver_static_new (uri);
+ priv->proxy_resolver = SOUP_PROXY_URI_RESOLVER (soup_proxy_resolver_static_new (uri));
soup_session_add_feature (session, SOUP_SESSION_FEATURE (priv->proxy_resolver));
g_object_unref (priv->proxy_resolver);
} else if (priv->proxy_resolver && SOUP_IS_PROXY_RESOLVER_STATIC (priv->proxy_resolver))
soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER);
soup_session_abort (session);
- cleanup_hosts (priv);
break;
case PROP_MAX_CONNS:
priv->max_conns = g_value_get_int (value);
@@ -674,13 +654,9 @@ set_property (GObject *object, guint prop_id,
g_free (priv->ssl_ca_file);
priv->ssl_ca_file = g_strdup (new_ca_file);
- if (ca_file_changed) {
- if (priv->ssl_creds) {
- soup_ssl_free_client_credentials (priv->ssl_creds);
- priv->ssl_creds = NULL;
- }
-
- cleanup_hosts (priv);
+ if (ca_file_changed && priv->ssl_creds) {
+ soup_ssl_free_client_credentials (priv->ssl_creds);
+ priv->ssl_creds = NULL;
}
break;
@@ -798,12 +774,14 @@ soup_session_get_async_context (SoupSession *session)
/* Hosts */
static SoupSessionHost *
-soup_session_host_new (SoupSession *session, SoupAddress *addr)
+soup_session_host_new (SoupSession *session, SoupURI *uri)
{
SoupSessionHost *host;
host = g_slice_new0 (SoupSessionHost);
- host->addr = g_object_ref (addr);
+ host->uri = soup_uri_copy_host (uri);
+ host->addr = soup_address_new (host->uri->host, host->uri->port);
+
return host;
}
@@ -816,17 +794,14 @@ get_host_for_message (SoupSession *session, SoupMessage *msg)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupSessionHost *host;
- SoupAddress *addr = soup_message_get_address (msg);
-
- if (!soup_address_is_resolved (addr))
- return NULL;
+ SoupURI *uri = soup_message_get_uri (msg);
- host = g_hash_table_lookup (priv->hosts, addr);
+ host = g_hash_table_lookup (priv->hosts, uri);
if (host)
return host;
- host = soup_session_host_new (session, addr);
- g_hash_table_insert (priv->hosts, host->addr, host);
+ host = soup_session_host_new (session, uri);
+ g_hash_table_insert (priv->hosts, host->uri, host);
return host;
}
@@ -841,6 +816,7 @@ free_host (SoupSessionHost *host)
soup_connection_disconnect (conn);
}
+ soup_uri_free (host->uri);
g_object_unref (host->addr);
g_slice_free (SoupSessionHost, host);
}
@@ -871,7 +847,9 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
(msg->status_code == SOUP_STATUS_FOUND &&
- !SOUP_METHOD_IS_SAFE (msg->method))) {
+ !SOUP_METHOD_IS_SAFE (msg->method)) ||
+ (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY &&
+ msg->method == SOUP_METHOD_POST)) {
/* Redirect using a GET */
g_object_set (msg,
SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
@@ -924,26 +902,29 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
soup_session_requeue_message (session, msg);
}
-static void
-connection_started_request (SoupConnection *conn, SoupMessage *msg,
- gpointer data)
+void
+soup_session_send_queue_item (SoupSession *session,
+ SoupMessageQueueItem *item,
+ SoupConnection *conn)
{
- SoupSession *session = data;
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ if (item->conn) {
+ if (item->conn != conn) {
+ g_object_unref (item->conn);
+ item->conn = g_object_ref (conn);
+ }
+ } else
+ item->conn = g_object_ref (conn);
+
if (priv->user_agent) {
- soup_message_headers_replace (msg->request_headers,
+ soup_message_headers_replace (item->msg->request_headers,
"User-Agent", priv->user_agent);
}
- /* Kludge to deal with the fact that CONNECT msgs come from the
- * SoupConnection rather than being queued normally.
- */
- if (msg->method == SOUP_METHOD_CONNECT)
- g_signal_emit (session, signals[REQUEST_QUEUED], 0, msg);
-
g_signal_emit (session, signals[REQUEST_STARTED], 0,
- msg, soup_connection_get_socket (conn));
+ item->msg, soup_connection_get_socket (conn));
+ soup_connection_send_request (conn, item->msg);
}
gboolean
@@ -963,7 +944,7 @@ soup_session_try_prune_connection (SoupSession *session)
/* Don't prune a connection that is currently in use,
* or hasn't been used yet.
*/
- if (!soup_connection_is_in_use (conn) &&
+ if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE &&
soup_connection_last_used (conn) > 0)
g_ptr_array_add (conns, g_object_ref (conn));
}
@@ -1006,32 +987,21 @@ connection_disconnected (SoupConnection *conn, gpointer user_data)
g_object_unref (conn);
}
-static void
-connect_result (SoupConnection *conn, guint status, gpointer user_data)
+void
+soup_session_connection_failed (SoupSession *session,
+ SoupConnection *conn, guint status)
{
- SoupSession *session = user_data;
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupSessionHost *host;
SoupMessageQueueItem *item;
SoupMessage *msg;
g_mutex_lock (priv->host_lock);
-
host = g_hash_table_lookup (priv->conns, conn);
- if (!host) {
- g_mutex_unlock (priv->host_lock);
+ g_mutex_unlock (priv->host_lock);
+ if (!host)
return;
- }
- if (status == SOUP_STATUS_OK) {
- soup_connection_reserve (conn);
- host->connections = g_slist_prepend (host->connections, conn);
- g_mutex_unlock (priv->host_lock);
- return;
- }
-
- /* The connection failed. */
- g_mutex_unlock (priv->host_lock);
connection_disconnected (conn, session);
if (host->connections) {
@@ -1043,56 +1013,83 @@ connect_result (SoupConnection *conn, guint status, gpointer user_data)
return;
}
- /* There are two possibilities: either status is
- * SOUP_STATUS_TRY_AGAIN, in which case the session implementation
- * will create a new connection (and all we need to do here
- * is downgrade the message from CONNECTING to QUEUED); or
- * status is something else, probably CANT_CONNECT or
- * CANT_RESOLVE or the like, in which case we need to cancel
- * any messages waiting for this host, since they're out
- * of luck.
+ /* Assume that there's something wrong with the host, and
+ * cancel any other messages waiting for a connection to it,
+ * since they're out of luck.
*/
g_object_ref (session);
for (item = soup_message_queue_first (priv->queue); item; item = soup_message_queue_next (priv->queue, item)) {
msg = item->msg;
- if (get_host_for_message (session, msg) == host) {
- if (status == SOUP_STATUS_TRY_AGAIN) {
- if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_CONNECTING)
- soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_QUEUED);
- } else {
- soup_session_cancel_message (session, msg,
- status);
- }
- }
+ if (get_host_for_message (session, msg) == host)
+ soup_session_cancel_message (session, msg, status);
}
g_object_unref (session);
}
+static void
+tunnel_connected (SoupSession *session, SoupMessage *msg, gpointer user_data)
+{
+ if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupMessageQueueItem *item =
+ soup_message_queue_lookup (priv->queue, msg);
+
+ /* Clear the connection's proxy_uri, since it is now
+ * (effectively) directly connected.
+ */
+ g_object_set (item->conn,
+ SOUP_CONNECTION_PROXY_URI, NULL,
+ NULL);
+ }
+}
+
+SoupMessageQueueItem *
+soup_session_make_connect_message (SoupSession *session,
+ SoupAddress *server_addr)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ SoupURI *uri;
+ SoupMessage *msg;
+
+ uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
+ soup_uri_set_host (uri, soup_address_get_name (server_addr));
+ soup_uri_set_port (uri, soup_address_get_port (server_addr));
+ soup_uri_set_path (uri, "");
+ msg = soup_message_new_from_uri (SOUP_METHOD_CONNECT, uri);
+ soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
+ soup_uri_free (uri);
+
+ /* Call the base implementation of soup_session_queue_message
+ * directly, to add msg to the SoupMessageQueue and cause all
+ * the right signals to be emitted.
+ */
+ queue_message (session, msg, tunnel_connected, NULL);
+ return soup_message_queue_lookup (priv->queue, msg);
+}
+
/**
* soup_session_get_connection:
* @session: a #SoupSession
- * @msg: a #SoupMessage
+ * @item: a #SoupMessageQueueItem
* @try_pruning: on return, whether or not to try pruning a connection
- * @is_new: on return, %TRUE if the returned connection is new and not
- * yet connected
*
- * Tries to find or create a connection for @msg; this is an internal
+ * Tries to find or create a connection for @item; this is an internal
* method for #SoupSession subclasses.
*
* If there is an idle connection to the relevant host available, then
- * that connection will be returned (with *@is_new set to %FALSE). The
- * connection will be marked "reserved", so the caller must call
- * soup_connection_release() if it ends up not using the connection
- * right away.
+ * that connection will be returned. The connection will be set to
+ * %SOUP_CONNECTION_IN_USE, so the caller must call
+ * soup_connection_set_state() to set it to %SOUP_CONNECTION_IDLE if
+ * it ends up not using the connection right away.
*
* If there is no idle connection available, but it is possible to
- * create a new connection, then one will be created and returned,
- * with *@is_new set to %TRUE. The caller MUST then call
+ * create a new connection, then one will be created and returned
+ * (with state %SOUP_CONNECTION_NEW). The caller MUST then call
* soup_connection_connect_sync() or soup_connection_connect_async()
- * to connect it. If the connection attempt succeeds, the connection
- * will be marked "reserved" and added to @session's connection pool
- * once it connects. If the connection attempt fails, the connection
- * will be unreffed.
+ * to connect it. If the connection attempt fails, the caller must
+ * call soup_session_connection_failed() to tell the session to free
+ * the connection.
*
* If no connection is available and a new connection cannot be made,
* soup_session_get_connection() will return %NULL. If @session has
@@ -1107,32 +1104,35 @@ connect_result (SoupConnection *conn, guint status, gpointer user_data)
* Return value: a #SoupConnection, or %NULL
**/
SoupConnection *
-soup_session_get_connection (SoupSession *session, SoupMessage *msg,
- SoupAddress *proxy_addr,
- gboolean *try_pruning, gboolean *is_new)
+soup_session_get_connection (SoupSession *session,
+ SoupMessageQueueItem *item,
+ gboolean *try_pruning)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupConnection *conn;
SoupSessionHost *host;
+ SoupAddress *remote_addr, *tunnel_addr;
SoupSSLCredentials *ssl_creds;
GSList *conns;
+ gboolean has_pending = FALSE;
SoupURI *uri;
g_mutex_lock (priv->host_lock);
- host = get_host_for_message (session, msg);
+ host = get_host_for_message (session, item->msg);
for (conns = host->connections; conns; conns = conns->next) {
- if (!soup_connection_is_in_use (conns->data)) {
- soup_connection_reserve (conns->data);
+ if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) {
+ soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE);
g_mutex_unlock (priv->host_lock);
- *is_new = FALSE;
return conns->data;
- }
+ } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING)
+ has_pending = TRUE;
}
- if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_CONNECTING) {
- /* We already started a connection for this
- * message, so don't start another one.
+ if (has_pending) {
+ /* We've already started one connection to this
+ * address, so don't start another one until it's
+ * done.
*/
g_mutex_unlock (priv->host_lock);
return NULL;
@@ -1149,48 +1149,45 @@ soup_session_get_connection (SoupSession *session, SoupMessage *msg,
return NULL;
}
- uri = soup_message_get_uri (msg);
+ if (item->proxy_addr) {
+ remote_addr = item->proxy_addr;
+ tunnel_addr = NULL;
+ } else {
+ remote_addr = host->addr;
+ tunnel_addr = NULL;
+ }
+
+ uri = soup_message_get_uri (item->msg);
if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
if (!priv->ssl_creds)
priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file);
ssl_creds = priv->ssl_creds;
+
+ if (item->proxy_addr)
+ tunnel_addr = host->addr;
} else
ssl_creds = NULL;
conn = soup_connection_new (
- SOUP_CONNECTION_SERVER_ADDRESS, host->addr,
- SOUP_CONNECTION_PROXY_ADDRESS, proxy_addr,
+ SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr,
+ SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr,
+ SOUP_CONNECTION_PROXY_URI, item->proxy_uri,
SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds,
SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
NULL);
- g_signal_connect (conn, "connect_result",
- G_CALLBACK (connect_result),
- session);
g_signal_connect (conn, "disconnected",
G_CALLBACK (connection_disconnected),
session);
- g_signal_connect (conn, "request_started",
- G_CALLBACK (connection_started_request),
- session);
g_hash_table_insert (priv->conns, conn, host);
- /* We increment the connection counts so it counts against the
- * totals, but we don't add it to the host's connection list
- * yet, since it's not ready for use.
- */
priv->num_conns++;
host->num_conns++;
-
- /* Mark the request as connecting, so we don't try to open
- * another new connection for it while waiting for this one.
- */
- soup_message_set_io_status (msg, SOUP_MESSAGE_IO_STATUS_CONNECTING);
+ host->connections = g_slist_prepend (host->connections, conn);
g_mutex_unlock (priv->host_lock);
- *is_new = TRUE;
return conn;
}
@@ -1209,6 +1206,11 @@ message_finished (SoupMessage *msg, gpointer user_data)
SoupSession *session = item->session;
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ if (item->conn) {
+ g_object_unref (item->conn);
+ item->conn = NULL;
+ }
+
if (!SOUP_MESSAGE_IS_STARTING (msg)) {
soup_message_queue_remove (priv->queue, item);
g_signal_handlers_disconnect_by_func (msg, message_finished, item);
@@ -1422,6 +1424,10 @@ soup_session_cancel_message (SoupSession *session, SoupMessage *msg,
g_return_if_fail (SOUP_IS_SESSION (session));
g_return_if_fail (SOUP_IS_MESSAGE (msg));
+ /* If the message is already ending, don't do anything */
+ if (soup_message_get_io_status (msg) == SOUP_MESSAGE_IO_STATUS_FINISHED)
+ return;
+
SOUP_SESSION_GET_CLASS (session)->cancel_message (session, msg, status_code);
}
@@ -1494,9 +1500,8 @@ soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
soup_session_feature_attach (feature, session);
- if (SOUP_IS_PROXY_RESOLVER (feature))
- priv->proxy_resolver = SOUP_PROXY_RESOLVER (feature);
-
+ if (SOUP_IS_PROXY_URI_RESOLVER (feature))
+ priv->proxy_resolver = SOUP_PROXY_URI_RESOLVER (feature);
if (SOUP_IS_CACHE (feature))
priv->cache = SOUP_CACHE (feature);
}
@@ -1647,7 +1652,7 @@ soup_session_get_feature (SoupSession *session, GType feature_type)
return NULL;
}
-SoupProxyResolver *
+SoupProxyURIResolver *
soup_session_get_proxy_resolver (SoupSession *session)
{
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index d3f77f93..0c06f0fe 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -75,6 +75,7 @@ typedef struct {
GSource *watch_src;
GSource *read_src, *write_src;
GSource *read_timeout, *write_timeout;
+ GSource *connect_timeout;
GByteArray *read_buf;
GMutex *iolock, *addrlock;
@@ -156,6 +157,8 @@ finalize (GObject *object)
if (priv->watch_src)
g_source_destroy (priv->watch_src);
+ if (priv->connect_timeout)
+ g_source_destroy (priv->connect_timeout);
if (priv->async_context)
g_main_context_unref (priv->async_context);
@@ -604,6 +607,10 @@ connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
/* Remove the watch now in case we don't return immediately */
g_source_destroy (priv->watch_src);
priv->watch_src = NULL;
+ if (priv->connect_timeout) {
+ g_source_destroy (priv->connect_timeout);
+ priv->connect_timeout = NULL;
+ }
if ((condition & ~(G_IO_IN | G_IO_OUT)) ||
(getsockopt (priv->sockfd, SOL_SOCKET, SO_ERROR,
@@ -614,6 +621,22 @@ connect_watch (GIOChannel* iochannel, GIOCondition condition, gpointer data)
return idle_connect_result (sacd);
}
+static gboolean
+connect_timeout (gpointer data)
+{
+ SoupSocketAsyncConnectData *sacd = data;
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+
+ /* Remove the watch now in case we don't return immediately */
+ g_source_destroy (priv->watch_src);
+ priv->watch_src = NULL;
+ g_source_destroy (priv->connect_timeout);
+ priv->connect_timeout = NULL;
+
+ disconnect_internal (priv);
+ return idle_connect_result (sacd);
+}
+
static void
got_address (SoupAddress *addr, guint status, gpointer user_data)
{
@@ -731,6 +754,12 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
G_IO_PRI | G_IO_ERR |
G_IO_HUP | G_IO_NVAL,
connect_watch, sacd);
+ if (priv->timeout) {
+ priv->connect_timeout =
+ soup_add_timeout (priv->async_context,
+ priv->timeout * 1000,
+ connect_timeout, sacd);
+ }
if (cancellable) {
sacd->cancel_id =
g_signal_connect (cancellable, "cancelled",
diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c
index 2743576c..72a900db 100644
--- a/libsoup/soup-uri.c
+++ b/libsoup/soup-uri.c
@@ -100,16 +100,17 @@ const char *_SOUP_URI_SCHEME_HTTP, *_SOUP_URI_SCHEME_HTTPS;
static inline const char *
soup_uri_get_scheme (const char *scheme, int len)
{
- if (len == 4 && !strncmp (scheme, "http", 4)) {
+ if (len == 4 && !g_ascii_strncasecmp (scheme, "http", len)) {
return SOUP_URI_SCHEME_HTTP;
- } else if (len == 5 && !strncmp (scheme, "https", 5)) {
+ } else if (len == 5 && !g_ascii_strncasecmp (scheme, "https", len)) {
return SOUP_URI_SCHEME_HTTPS;
} else {
char *lower_scheme;
lower_scheme = g_ascii_strdown (scheme, len);
- scheme = g_intern_string (lower_scheme);
- g_free (lower_scheme);
+ scheme = g_intern_static_string (lower_scheme);
+ if (scheme != (const char *)lower_scheme)
+ g_free (lower_scheme);
return scheme;
}
}
@@ -921,6 +922,79 @@ soup_uri_set_fragment (SoupURI *uri, const char *fragment)
uri->fragment = g_strdup (fragment);
}
+/**
+ * soup_uri_copy_host:
+ * @uri: a #SoupUri
+ *
+ * Makes a copy of @uri, considering only the protocol, host, and port
+ *
+ * Return value: the new #SoupUri
+ *
+ * Since: 2.26.3
+ **/
+SoupURI *
+soup_uri_copy_host (SoupURI *uri)
+{
+ SoupURI *dup;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ dup = soup_uri_new (NULL);
+ dup->scheme = uri->scheme;
+ dup->host = g_strdup (uri->host);
+ dup->port = uri->port;
+ if (dup->scheme == SOUP_URI_SCHEME_HTTP ||
+ dup->scheme == SOUP_URI_SCHEME_HTTPS)
+ dup->path = g_strdup ("");
+
+ return dup;
+}
+
+/**
+ * soup_uri_host_hash:
+ * @key: a #SoupURI
+ *
+ * Hashes @key, considering only the scheme, host, and port.
+ *
+ * Return value: a hash
+ *
+ * Since: 2.26.3
+ **/
+guint
+soup_uri_host_hash (gconstpointer key)
+{
+ const SoupURI *uri = key;
+
+ return GPOINTER_TO_UINT (uri->scheme) + uri->port +
+ soup_str_case_hash (uri->host);
+}
+
+/**
+ * soup_uri_host_equal:
+ * @v1: a #SoupURI
+ * @v2: a #SoupURI
+ *
+ * Compares @v1 and @v2, considering only the scheme, host, and port.
+ *
+ * Return value: whether or not the URIs are equal in scheme, host,
+ * and port.
+ *
+ * Since: 2.26.3
+ **/
+gboolean
+soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
+{
+ const SoupURI *one = v1;
+ const SoupURI *two = v2;
+
+ if (one->scheme != two->scheme)
+ return FALSE;
+ if (one->port != two->port)
+ return FALSE;
+
+ return g_ascii_strcasecmp (one->host, two->host) == 0;
+}
+
GType
soup_uri_get_type (void)
diff --git a/libsoup/soup-uri.h b/libsoup/soup-uri.h
index 3a5c8dd0..c05f3f23 100644
--- a/libsoup/soup-uri.h
+++ b/libsoup/soup-uri.h
@@ -9,6 +9,7 @@
#define SOUP_URI_H 1
#include <libsoup/soup-types.h>
+#include <libsoup/soup-misc.h>
G_BEGIN_DECLS
@@ -30,8 +31,8 @@ struct _SoupURI {
GType soup_uri_get_type (void);
#define SOUP_TYPE_URI (soup_uri_get_type ())
-#define SOUP_URI_SCHEME_HTTP (_SOUP_URI_SCHEME_HTTP ? _SOUP_URI_SCHEME_HTTP : (_SOUP_URI_SCHEME_HTTP = g_intern_static_string ("http")))
-#define SOUP_URI_SCHEME_HTTPS (_SOUP_URI_SCHEME_HTTPS ? _SOUP_URI_SCHEME_HTTPS : (_SOUP_URI_SCHEME_HTTPS = g_intern_static_string ("https")))
+#define SOUP_URI_SCHEME_HTTP _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTP, "http")
+#define SOUP_URI_SCHEME_HTTPS _SOUP_ATOMIC_INTERN_STRING (_SOUP_URI_SCHEME_HTTPS, "https")
extern const char *_SOUP_URI_SCHEME_HTTP, *_SOUP_URI_SCHEME_HTTPS;
SoupURI *soup_uri_new_with_base (SoupURI *base,
@@ -74,10 +75,15 @@ void soup_uri_set_query_from_form (SoupURI *uri,
GHashTable *form);
void soup_uri_set_query_from_fields (SoupURI *uri,
const char *first_field,
- ...);
+ ...) G_GNUC_NULL_TERMINATED;
void soup_uri_set_fragment (SoupURI *uri,
const char *fragment);
+SoupURI *soup_uri_copy_host (SoupURI *uri);
+guint soup_uri_host_hash (gconstpointer key);
+gboolean soup_uri_host_equal (gconstpointer v1,
+ gconstpointer v2);
+
#define SOUP_URI_VALID_FOR_HTTP(uri) ((uri) && ((uri)->scheme == SOUP_URI_SCHEME_HTTP || (uri)->scheme == SOUP_URI_SCHEME_HTTPS) && (uri)->host)
G_END_DECLS
diff --git a/libsoup/soup-value-utils.h b/libsoup/soup-value-utils.h
index a7f48b62..1d265263 100644
--- a/libsoup/soup-value-utils.h
+++ b/libsoup/soup-value-utils.h
@@ -50,7 +50,7 @@ gboolean soup_value_hash_lookup (GHashTable *hash,
...);
gboolean soup_value_hash_lookup_vals (GHashTable *hash,
const char *first_key,
- ...);
+ ...) G_GNUC_NULL_TERMINATED;
GValueArray *soup_value_array_from_args (va_list args);
gboolean soup_value_array_to_args (GValueArray *array,
diff --git a/libsoup/soup-xmlrpc.h b/libsoup/soup-xmlrpc.h
index de0f1f31..380a31e4 100644
--- a/libsoup/soup-xmlrpc.h
+++ b/libsoup/soup-xmlrpc.h
@@ -46,7 +46,7 @@ void soup_xmlrpc_set_response (SoupMessage *msg,
void soup_xmlrpc_set_fault (SoupMessage *msg,
int fault_code,
const char *fault_format,
- ...);
+ ...) G_GNUC_PRINTF (3, 4);
/* Errors */
diff --git a/libsoup/soup.h b/libsoup/soup.h
index 4ac09911..bfbea4e9 100644
--- a/libsoup/soup.h
+++ b/libsoup/soup.h
@@ -16,6 +16,7 @@ extern "C" {
#include <libsoup/soup-auth-domain-basic.h>
#include <libsoup/soup-auth-domain-digest.h>
#include <libsoup/soup-cache.h>
+#include <libsoup/soup-content-sniffer.h>
#include <libsoup/soup-cookie.h>
#include <libsoup/soup-cookie-jar.h>
#include <libsoup/soup-cookie-jar-text.h>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 0d46df56..20716c24 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -26,6 +26,7 @@ noinst_PROGRAMS = \
redirect-test \
simple-httpd \
simple-proxy \
+ sniffing-test \
streaming-test \
timeout-test \
uri-parsing \
@@ -58,6 +59,7 @@ redirect_test_SOURCES = redirect-test.c $(TEST_SRCS)
server_auth_test_SOURCES = server-auth-test.c $(TEST_SRCS)
simple_httpd_SOURCES = simple-httpd.c
simple_proxy_SOURCES = simple-proxy.c
+sniffing_test_SOURCES = sniffing-test.c $(TEST_SRCS)
ssl_test_SOURCES = ssl-test.c $(TEST_SRCS)
streaming_test_SOURCES = streaming-test.c $(TEST_SRCS)
timeout_test_SOURCES = timeout-test.c $(TEST_SRCS)
@@ -87,6 +89,7 @@ TESTS = \
misc-test \
ntlm-test \
redirect-test \
+ sniffing-test \
streaming-test \
timeout-test \
uri-parsing \
@@ -95,15 +98,23 @@ TESTS = \
$(SSL_TESTS) \
$(XMLRPC_TESTS)
-EXTRA_DIST = \
- libsoup.supp \
- test-cert.pem \
- test-key.pem \
- htdigest \
- htpasswd \
- httpd.conf.in \
- index.txt \
- xmlrpc-server.php
+SNIFFING_FILES = \
+ resources/atom.xml \
+ resources/home.gif \
+ resources/mbox \
+ resources/rss20.xml \
+ resources/test.html
+
+EXTRA_DIST = \
+ htdigest \
+ htpasswd \
+ httpd.conf.in \
+ index.txt \
+ libsoup.supp \
+ test-cert.pem \
+ test-key.pem \
+ xmlrpc-server.php \
+ $(SNIFFING_FILES)
if MISSING_REGRESSION_TEST_PACKAGES
check-local: check-TESTS
diff --git a/tests/auth-test.c b/tests/auth-test.c
index cc992b1c..b090e239 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -50,8 +50,8 @@ static SoupAuthTest tests[] = {
{ "Should fail with no auth, fail again with bad password, and give up",
"Basic/realm2/", "4", "04", SOUP_STATUS_UNAUTHORIZED },
- { "Known realm, auth provided, so should succeed immediately",
- "Basic/realm1/", "1", "1", SOUP_STATUS_OK },
+ { "Auth provided this time, so should succeed",
+ "Basic/realm1/", "1", "01", SOUP_STATUS_OK },
{ "Now should automatically reuse previous auth",
"Basic/realm1/", "", "1", SOUP_STATUS_OK },
@@ -62,8 +62,8 @@ static SoupAuthTest tests[] = {
{ "Subdir should retry last auth, but will fail this time",
"Basic/realm1/realm2/", "", "1", SOUP_STATUS_UNAUTHORIZED },
- { "Now should use provided auth on first try",
- "Basic/realm1/realm2/", "2", "2", SOUP_STATUS_OK },
+ { "Now should use provided auth",
+ "Basic/realm1/realm2/", "2", "02", SOUP_STATUS_OK },
{ "Reusing last auth. Should succeed on first try",
"Basic/realm1/realm2/", "", "2", SOUP_STATUS_OK },
@@ -84,8 +84,8 @@ static SoupAuthTest tests[] = {
{ "Should fail with no auth, fail again with bad password, and give up",
"Digest/realm2/", "4", "04", SOUP_STATUS_UNAUTHORIZED },
- { "Known realm, auth provided, so should succeed immediately",
- "Digest/realm1/", "1", "1", SOUP_STATUS_OK },
+ { "Known realm, auth provided, so should succeed",
+ "Digest/realm1/", "1", "01", SOUP_STATUS_OK },
{ "Now should automatically reuse previous auth",
"Digest/realm1/", "", "1", SOUP_STATUS_OK },
@@ -93,6 +93,9 @@ static SoupAuthTest tests[] = {
{ "Subdir should also automatically reuse auth",
"Digest/realm1/subdir/", "", "1", SOUP_STATUS_OK },
+ { "Password provided, should succeed",
+ "Digest/realm2/", "2", "02", SOUP_STATUS_OK },
+
{ "Should already know correct domain and use provided auth on first try",
"Digest/realm1/realm2/", "2", "2", SOUP_STATUS_OK },
@@ -102,9 +105,6 @@ static SoupAuthTest tests[] = {
{ "Should succeed on first try because of earlier domain directive",
"Digest/realm1/realm2/realm1/", "", "1", SOUP_STATUS_OK },
- { "Should succeed on first try. (Known realm with cached password)",
- "Digest/realm2/", "", "2", SOUP_STATUS_OK },
-
{ "Fail once, then use typoed password, then use right password",
"Digest/realm3/", "43", "043", SOUP_STATUS_OK },
@@ -398,15 +398,15 @@ async_finished (SoupSession *session, SoupMessage *msg, gpointer user_data)
}
static void
-async_authenticate_522601 (SoupSession *session, SoupMessage *msg,
- SoupAuth *auth, gboolean retrying, gpointer data)
+async_authenticate_assert_once (SoupSession *session, SoupMessage *msg,
+ SoupAuth *auth, gboolean retrying, gpointer data)
{
gboolean *been_here = data;
- debug_printf (2, " async_authenticate_522601\n");
+ debug_printf (2, " async_authenticate_assert_once\n");
if (*been_here) {
- debug_printf (1, " ERROR: async_authenticate_522601 called twice\n");
+ debug_printf (1, " ERROR: async_authenticate_assert_once called twice\n");
errors++;
}
*been_here = TRUE;
@@ -531,8 +531,52 @@ do_async_auth_test (const char *base_uri)
soup_session_unpause_message (session, msg1);
auth_id = g_signal_connect (session, "authenticate",
- G_CALLBACK (async_authenticate_522601),
+ G_CALLBACK (async_authenticate_assert_once),
+ &been_there);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+
+ soup_test_session_abort_unref (session);
+
+ g_object_unref (msg1);
+
+
+ /* Test that giving no password doesn't cause multiple
+ * authenticate signals the second time.
+ */
+ debug_printf (1, "\nTesting async auth with no password (#583462):\n");
+
+ /* For this test, our first message will not finish twice */
+ finished = 1;
+ been_there = FALSE;
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ /* Send a message that doesn't actually authenticate
+ */
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (1));
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate), NULL);
+ g_object_ref (msg1);
+ soup_session_queue_message (session, msg1, async_finished, &finished);
+ g_main_loop_run (loop);
+ g_signal_handler_disconnect (session, auth_id);
+ soup_session_unpause_message (session, msg1);
+ g_main_loop_run (loop);
+ g_object_unref(msg1);
+
+ /* Now send a second message */
+ finished = 1;
+ msg1 = soup_message_new ("GET", uri);
+ g_object_set_data (G_OBJECT (msg1), "id", GINT_TO_POINTER (2));
+ g_object_ref (msg1);
+ auth_id = g_signal_connect (session, "authenticate",
+ G_CALLBACK (async_authenticate_assert_once),
&been_there);
+ soup_session_queue_message (session, msg1, async_finished, &finished);
+ g_main_loop_run (loop);
+ soup_session_unpause_message (session, msg1);
+
g_main_loop_run (loop);
g_signal_handler_disconnect (session, auth_id);
diff --git a/tests/get.c b/tests/get.c
index 9efed354..b0e5c576 100644
--- a/tests/get.c
+++ b/tests/get.c
@@ -22,141 +22,24 @@
#include <libsoup/soup.h>
#endif
-#ifdef G_OS_WIN32
-#include <io.h>
-#define mkdir(path, mode) _mkdir (path)
-#endif
-
static SoupSession *session;
static GMainLoop *loop;
-static gboolean recurse = FALSE, debug = FALSE;
+static gboolean debug = FALSE;
static const char *method;
-static char *base;
-static SoupURI *base_uri;
-static GHashTable *fetched_urls;
-
-static GPtrArray *
-find_hrefs (SoupURI *doc_base, const char *body, int length)
-{
- GPtrArray *hrefs = g_ptr_array_new ();
- char *buf = g_strndup (body, length);
- char *start = buf, *end;
- char *href, *frag;
- SoupURI *uri;
-
- while ((start = strstr (start, "href"))) {
- start += 4;
- while (isspace ((unsigned char) *start))
- start++;
- if (*start++ != '=')
- continue;
- while (isspace ((unsigned char) *start))
- start++;
- if (*start++ != '"')
- continue;
-
- end = strchr (start, '"');
- if (!end)
- break;
-
- href = g_strndup (start, end - start);
- start = end;
- frag = strchr (href, '#');
- if (frag)
- *frag = '\0';
-
- uri = soup_uri_new_with_base (doc_base, href);
- g_free (href);
-
- if (!uri)
- continue;
- if (doc_base->scheme != uri->scheme ||
- doc_base->port != uri->port ||
- g_ascii_strcasecmp (doc_base->host, uri->host) != 0) {
- soup_uri_free (uri);
- continue;
- }
-
- if (strncmp (doc_base->path, uri->path, strlen (doc_base->path)) != 0) {
- soup_uri_free (uri);
- continue;
- }
-
- g_ptr_array_add (hrefs, soup_uri_to_string (uri, FALSE));
- soup_uri_free (uri);
- }
- g_free (buf);
-
- return hrefs;
-}
-
-static void
-mkdirs (const char *path)
-{
- char *slash;
-
- for (slash = strchr (path, '/'); slash; slash = strchr (slash + 1, '/')) {
- *slash = '\0';
- if (*path && mkdir (path, 0755) == -1 && errno != EEXIST) {
- fprintf (stderr, "Could not create '%s'\n", path);
- g_main_loop_quit (loop);
- return;
- }
- *slash = '/';
- }
-}
static void
get_url (const char *url)
{
- char *url_to_get, *slash, *name;
+ const char *name;
SoupMessage *msg;
- int fd, i;
- SoupURI *uri;
- GPtrArray *hrefs;
const char *header;
- if (strncmp (url, base, strlen (base)) != 0)
- return;
- if (strchr (url, '?') && strcmp (url, base) != 0)
- return;
-
- slash = strrchr (url, '/');
- if (slash && !slash[1])
- url_to_get = g_strdup_printf ("%sindex.html", url);
- else
- url_to_get = g_strdup (url);
-
- if (g_hash_table_lookup (fetched_urls, url_to_get))
- return;
- g_hash_table_insert (fetched_urls, url_to_get, url_to_get);
-
- if (recurse) {
- /* See if we're already downloading it, and create the
- * file if not.
- */
-
- name = url_to_get + strlen (base);
- if (*name == '/')
- name++;
- if (access (name, F_OK) == 0)
- return;
-
- mkdirs (name);
- fd = open (name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- close (fd);
- }
-
- msg = soup_message_new (method, url_to_get);
+ msg = soup_message_new (method, url);
soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
soup_session_send_message (session, msg);
name = soup_message_get_uri (msg)->path;
- if (strncmp (base_uri->path, name, strlen (base_uri->path)) != 0) {
- fprintf (stderr, " Error: not under %s\n", base_uri->path);
- return;
- }
if (debug) {
SoupMessageHeadersIter iter;
@@ -176,13 +59,7 @@ get_url (const char *url)
} else
printf ("%s: %d %s\n", name, msg->status_code, msg->reason_phrase);
- name += strlen (base_uri->path);
- if (*name == '/')
- name++;
-
if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
- if (recurse)
- unlink (name);
header = soup_message_headers_get_one (msg->response_headers,
"Location");
if (header) {
@@ -190,47 +67,24 @@ get_url (const char *url)
printf (" -> %s\n", header);
get_url (header);
}
- return;
+ } else if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+ fwrite (msg->response_body->data, 1,
+ msg->response_body->length, stdout);
}
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
- return;
-
- if (recurse)
- fd = open (name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
- else
- fd = STDOUT_FILENO;
- write (fd, msg->response_body->data, msg->response_body->length);
- if (!recurse)
- return;
- close (fd);
-
- header = soup_message_headers_get_content_type (msg->response_headers, NULL);
- if (header && g_ascii_strcasecmp (header, "text/html") != 0)
- return;
-
- uri = soup_uri_new (url);
- hrefs = find_hrefs (uri, msg->response_body->data, msg->response_body->length);
- soup_uri_free (uri);
- for (i = 0; i < hrefs->len; i++) {
- get_url (hrefs->pdata[i]);
- g_free (hrefs->pdata[i]);
- }
- g_ptr_array_free (hrefs, TRUE);
}
static void
usage (void)
{
- fprintf (stderr, "Usage: get [-c CAfile] [-p proxy URL] [-r] [-h] [-d] URL\n");
+ fprintf (stderr, "Usage: get [-c CAfile] [-p proxy URL] [-h] [-d] URL\n");
exit (1);
}
int
main (int argc, char **argv)
{
- const char *cafile = NULL;
- SoupURI *proxy = NULL;
+ const char *cafile = NULL, *url;
+ SoupURI *proxy = NULL, *parsed;
gboolean synchronous = FALSE;
int opt;
@@ -239,7 +93,7 @@ main (int argc, char **argv)
method = SOUP_METHOD_GET;
- while ((opt = getopt (argc, argv, "c:dhp:rs")) != -1) {
+ while ((opt = getopt (argc, argv, "c:dhp:s")) != -1) {
switch (opt) {
case 'c':
cafile = optarg;
@@ -263,10 +117,6 @@ main (int argc, char **argv)
}
break;
- case 'r':
- recurse = TRUE;
- break;
-
case 's':
synchronous = TRUE;
break;
@@ -281,14 +131,13 @@ main (int argc, char **argv)
if (argc != 1)
usage ();
- base = argv[0];
- base_uri = soup_uri_new (base);
- if (!base_uri) {
- fprintf (stderr, "Could not parse '%s' as a URL\n", base);
+ url = argv[0];
+ parsed = soup_uri_new (url);
+ if (!parsed) {
+ fprintf (stderr, "Could not parse '%s' as a URL\n", url);
exit (1);
}
-
- fetched_urls = g_hash_table_new (g_str_hash, g_str_equal);
+ soup_uri_free (parsed);
if (synchronous) {
session = soup_session_sync_new_with_options (
@@ -318,28 +167,13 @@ main (int argc, char **argv)
NULL);
}
- if (recurse) {
- char *outdir;
-
- outdir = g_strdup_printf ("%lu", (unsigned long)getpid ());
- if (mkdir (outdir, 0755) != 0) {
- fprintf (stderr, "Could not make output directory\n");
- exit (1);
- }
- printf ("Output directory is '%s'\n", outdir);
- chdir (outdir);
- g_free (outdir);
- }
-
if (!synchronous)
loop = g_main_loop_new (NULL, TRUE);
- get_url (base);
+ get_url (url);
if (!synchronous)
g_main_loop_unref (loop);
- soup_uri_free (base_uri);
-
return 0;
}
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
index ebdd2cc1..8f57c1f0 100644
--- a/tests/proxy-test.c
+++ b/tests/proxy-test.c
@@ -21,7 +21,7 @@ static SoupProxyTest tests[] = {
{ "GET -> 404", "/not-found", SOUP_STATUS_NOT_FOUND },
{ "GET -> 401 -> 200", "/Basic/realm1/", SOUP_STATUS_OK },
{ "GET -> 401 -> 401", "/Basic/realm2/", SOUP_STATUS_UNAUTHORIZED },
- { "GET -> 403", "http://www.example.com/", SOUP_STATUS_FORBIDDEN },
+ { "GET -> 403", "http://no-such-hostname.xx/", SOUP_STATUS_FORBIDDEN },
};
static int ntests = sizeof (tests) / sizeof (tests[0]);
diff --git a/tests/redirect-test.c b/tests/redirect-test.c
index 9d5d9b86..9e21bd1d 100644
--- a/tests/redirect-test.c
+++ b/tests/redirect-test.c
@@ -82,9 +82,10 @@ static struct {
{ "HEAD", "/302", 302 },
{ "HEAD", "/", 200 } } },
- /* POST should only automatically redirect on 302 and 303 */
+ /* POST should only automatically redirect on 301, 302 and 303 */
{ { { "POST", "/301", 301 },
+ { "GET", "/", 200 },
{ NULL } } },
{ { { "POST", "/302", 302 },
{ "GET", "/", 200 },
diff --git a/tests/resources/atom.xml b/tests/resources/atom.xml
new file mode 100644
index 00000000..962ecf47
--- /dev/null
+++ b/tests/resources/atom.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xmlns:planet="http://planet.libsouprocks.net/" xmlns:indexing="urn:atom-extension:indexing" indexing:index="no"><access:restriction xmlns:access="http://www.bloglines.com/about/specs/fac-1.0" relationship="deny"/>
+ <title>A small ATOM feed</title>
+ <updated>2009-07-02T10:27:44Z</updated>
+ <generator>kov</generator>
+ <author>
+ <name>Anonymous Coward</name>
+ </author>
+ <id>http://libsoup.rocks/atom.xml</id>
+ <link href="http://libsoup.rocks/atom.xml" rel="self" type="application/atom+xml"/>
+ <link href="http://libsoup.rocks/" rel="alternate"/>
+
+ <entry xml:lang="en">
+ <id>http://libsoup.rocks/so/much/</id>
+ <link href="http://libsoup.rocks/so/much/" rel="alternate" type="text/html"/>
+ <title>One post too many</title>
+ <summary>woo [...]</summary>
+ <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><p>woohoo</p></div>
+ </content>
+ <updated>2009-07-02T10:38:28Z</updated>
+ <category term="Category1"/>
+ <category term="Personal"/>
+ <author>
+ <name>kov</name>
+ </author>
+ <source>
+ <id>http://libsoup.rocks/blog</id>
+ <link href="http://libsoup.rocks/blog/feed" rel="self" type="application/atom+xml"/>
+ <link href="http://libsoup.rocks/blog" rel="alternate" type="text/html"/>
+ <subtitle>Just stuff to test libsoup</subtitle>
+ <title>Random stuff to test libsoup</title>
+ <updated>2009-07-02T00:38:29Z</updated>
+ </source>
+ </entry>
+</feed>
diff --git a/tests/resources/home.gif b/tests/resources/home.gif
new file mode 100644
index 00000000..55e1d599
--- /dev/null
+++ b/tests/resources/home.gif
Binary files differ
diff --git a/tests/resources/mbox b/tests/resources/mbox
new file mode 100644
index 00000000..2e967278
--- /dev/null
+++ b/tests/resources/mbox
@@ -0,0 +1,64 @@
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
+From email@here Wed Jun 17 21:20:48 2009
+Return-path: <email@here>
+Envelope-to: email@here
+Delivery-date: Wed, 17 Jun 2009 21:20:48 -0300
+Received: from email by here.domain with local (Exim 4.69)
+ (envelope-from <email@here>)
+ id 1MH5N2-0008Lq-7c
+ for email@here; Wed, 17 Jun 2009 21:20:48 -0300
+To: email@here
+Subject: This is just so that I have a mailbox
+Message-Id: <E1MH5N2-0008Lq-7c@here.domain>
+From: A Nice User <email@here>
+Date: Wed, 17 Jun 2009 21:20:48 -0300
+
+This is a dumb email.
+
diff --git a/tests/resources/rss20.xml b/tests/resources/rss20.xml
new file mode 100644
index 00000000..d64bddac
--- /dev/null
+++ b/tests/resources/rss20.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<rss version="2.0">
+
+<channel>
+ <title>A small RSS</title>
+ <link>http://libsoup.rocks/</link>
+ <language>en</language>
+ <description>A small RSS to test libsoup</description>
+
+<item>
+ <title>One post too many</title>
+ <guid isPermaLink="true">http://libsoup.rocks/so/much/</guid>
+ <link>http://libsoup.rocks/so/much/</link>
+ <description>&lt;p&gt;woohoo&lt;/p&gt;</description>
+ <pubDate>Wed, 02 Jul 2009 10:26:28 +0000</pubDate>
+</item>
+<item>
+ <title>GCDS will rock</title>
+ <guid isPermaLink="true">http://libsoup.rocks/so/much/again/</guid>
+ <link>http://libsoup.rocks/so/much/again/</link>
+ <description>&lt;p&gt;I mean, really.&lt;/p&gt;</description>
+ <pubDate>Wed, 02 Jul 2009 10:26:28 +0000</pubDate>
+</item>
+
+</channel>
+</rss>
diff --git a/tests/resources/test.html b/tests/resources/test.html
new file mode 100644
index 00000000..5a6cc0c2
--- /dev/null
+++ b/tests/resources/test.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title></title>
+</head>
+<body>
+<h1>GNOME!</h1>
+</body>
+</html>
diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c
new file mode 100644
index 00000000..a5c95872
--- /dev/null
+++ b/tests/sniffing-test.c
@@ -0,0 +1,481 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+SoupSession *session;
+SoupURI *base_uri;
+SoupMessageBody *chunk_data;
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+ const char *path, GHashTable *query,
+ SoupClientContext *context, gpointer data)
+{
+ GError *error = NULL;
+ char *query_key;
+ char *contents;
+ gsize length, offset;
+ gboolean empty_response = FALSE;
+
+ if (msg->method != SOUP_METHOD_GET) {
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_OK);
+
+ if (query) {
+ query_key = g_hash_table_lookup (query, "chunked");
+ if (query_key && g_str_equal (query_key, "yes")) {
+ soup_message_headers_set_encoding (msg->response_headers,
+ SOUP_ENCODING_CHUNKED);
+ }
+
+ query_key = g_hash_table_lookup (query, "empty_response");
+ if (query_key && g_str_equal (query_key, "yes"))
+ empty_response = TRUE;
+ }
+
+ if (!strcmp (path, "/mbox")) {
+ if (empty_response) {
+ contents = g_strdup ("");
+ length = 0;
+ } else {
+ g_file_get_contents (SRCDIR "/resources/mbox",
+ &contents, &length,
+ &error);
+ }
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/plain");
+ }
+
+ if (g_str_has_prefix (path, "/text_or_binary/")) {
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/plain");
+ }
+
+ if (g_str_has_prefix (path, "/unknown/")) {
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "UNKNOWN/unknown");
+ }
+
+ if (g_str_has_prefix (path, "/type/")) {
+ char **components = g_strsplit (path, "/", 4);
+ char *ptr;
+
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ /* Hack to allow passing type in the URI */
+ ptr = g_strrstr (components[2], "_");
+ *ptr = '/';
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", components[2]);
+ g_strfreev (components);
+ }
+
+ if (g_str_has_prefix (path, "/multiple_headers/")) {
+ char *base_name = g_path_get_basename (path);
+ char *file_name = g_strdup_printf (SRCDIR "/resources/%s", base_name);
+
+ g_file_get_contents (file_name,
+ &contents, &length,
+ &error);
+
+ g_free (base_name);
+ g_free (file_name);
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/xml");
+ soup_message_headers_append (msg->response_headers,
+ "Content-Type", "text/plain");
+ }
+
+ for (offset = 0; offset < length; offset += 500) {
+ soup_message_body_append (msg->response_body,
+ SOUP_MEMORY_COPY,
+ contents + offset,
+ MIN(500, length - offset));
+ }
+ soup_message_body_complete (msg->response_body);
+}
+
+static gboolean
+unpause_msg (gpointer data)
+{
+ SoupMessage *msg = (SoupMessage*)data;
+ debug_printf (2, " unpause\n");
+ soup_session_unpause_message (session, msg);
+ return FALSE;
+}
+
+
+static void
+content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, gpointer data)
+{
+ gboolean should_pause = GPOINTER_TO_INT (data);
+
+ debug_printf (2, " content-sniffed -> %s\n", content_type);
+
+ if (g_object_get_data (G_OBJECT (msg), "got-chunk")) {
+ debug_printf (1, " got-chunk got emitted before content-sniffed\n");
+ errors++;
+ }
+
+ g_object_set_data (G_OBJECT (msg), "content-sniffed", GINT_TO_POINTER (TRUE));
+
+ if (should_pause) {
+ debug_printf (2, " pause\n");
+ soup_session_pause_message (session, msg);
+ g_idle_add (unpause_msg, msg);
+ }
+}
+
+static void
+got_headers (SoupMessage *msg, gpointer data)
+{
+ gboolean should_pause = GPOINTER_TO_INT (data);
+
+ debug_printf (2, " got-headers\n");
+
+ if (g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
+ debug_printf (1, " content-sniffed got emitted before got-headers\n");
+ errors++;
+ }
+
+ g_object_set_data (G_OBJECT (msg), "got-headers", GINT_TO_POINTER (TRUE));
+
+ if (should_pause) {
+ debug_printf (2, " pause\n");
+ soup_session_pause_message (session, msg);
+ g_idle_add (unpause_msg, msg);
+ }
+}
+
+static void
+got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer data)
+{
+ gboolean should_accumulate = GPOINTER_TO_INT (data);
+
+ debug_printf (2, " got-chunk\n");
+
+ g_object_set_data (G_OBJECT (msg), "got-chunk", GINT_TO_POINTER (TRUE));
+
+ if (!should_accumulate) {
+ if (!chunk_data)
+ chunk_data = soup_message_body_new ();
+ soup_message_body_append_buffer (chunk_data, chunk);
+ }
+}
+
+static void
+finished (SoupSession *session, SoupMessage *msg, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop*)data;
+ g_main_loop_quit (loop);
+}
+
+static void
+do_signals_test (gboolean should_content_sniff,
+ gboolean should_pause,
+ gboolean should_accumulate,
+ gboolean chunked_encoding,
+ gboolean empty_response)
+{
+ SoupURI *uri = soup_uri_new_with_base (base_uri, "/mbox");
+ SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
+ GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+ char *contents;
+ gsize length;
+ GError *error = NULL;
+ SoupBuffer *body = NULL;
+
+ debug_printf (1, "do_signals_test(%ssniff, %spause, %saccumulate, %schunked, %sempty)\n",
+ should_content_sniff ? "" : "!",
+ should_pause ? "" : "!",
+ should_accumulate ? "" : "!",
+ chunked_encoding ? "" : "!",
+ empty_response ? "" : "!");
+
+ if (chunked_encoding)
+ soup_uri_set_query (uri, "chunked=yes");
+
+ if (empty_response) {
+ if (uri->query) {
+ char *tmp = uri->query;
+ uri->query = g_strdup_printf("%s&empty_response=yes", tmp);
+ g_free (tmp);
+ } else
+ soup_uri_set_query (uri, "empty_response=yes");
+ }
+
+ soup_message_set_uri (msg, uri);
+
+ soup_message_body_set_accumulate (msg->response_body, should_accumulate);
+
+ g_object_connect (msg,
+ "signal::got-headers", got_headers, GINT_TO_POINTER (should_pause),
+ "signal::got-chunk", got_chunk, GINT_TO_POINTER (should_accumulate),
+ "signal::content_sniffed", content_sniffed, GINT_TO_POINTER (should_pause),
+ NULL);
+
+ g_object_ref (msg);
+ soup_session_queue_message (session, msg, finished, loop);
+
+ g_main_loop_run (loop);
+
+ if (!should_content_sniff &&
+ g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
+ debug_printf (1, " content-sniffed got emitted without a sniffer\n");
+ errors++;
+ } else if (should_content_sniff &&
+ !g_object_get_data (G_OBJECT (msg), "content-sniffed")) {
+ debug_printf (1, " content-sniffed did not get emitted\n");
+ errors++;
+ }
+
+ if (empty_response) {
+ contents = g_strdup ("");
+ length = 0;
+ } else {
+ g_file_get_contents (SRCDIR "/resources/mbox",
+ &contents, &length,
+ &error);
+ }
+
+ if (error) {
+ g_error ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (!should_accumulate && chunk_data) {
+ body = soup_message_body_flatten (chunk_data);
+ soup_message_body_free (chunk_data);
+ chunk_data = NULL;
+ } else if (msg->response_body)
+ body = soup_message_body_flatten (msg->response_body);
+
+ if (body && body->length != length) {
+ debug_printf (1, " lengths do not match\n");
+ errors++;
+ }
+
+ if (body && memcmp (body->data, contents, length)) {
+ debug_printf (1, " downloaded data does not match\n");
+ errors++;
+ }
+
+ if (body)
+ soup_buffer_free (body);
+
+ soup_uri_free (uri);
+ g_object_unref (msg);
+ g_main_loop_unref (loop);
+}
+
+static void
+sniffing_content_sniffed (SoupMessage *msg, char *content_type, GHashTable *params, gpointer data)
+{
+ char *expected_type = (char*)data;
+
+ if (strcmp (content_type, expected_type)) {
+ debug_printf (1, " sniffing failed! expected %s, got %s\n",
+ expected_type, content_type);
+ errors++;
+ }
+}
+
+static void
+test_sniffing (const char *path, const char *expected_type)
+{
+ SoupURI *uri = soup_uri_new_with_base (base_uri, path);
+ SoupMessage *msg = soup_message_new_from_uri ("GET", uri);
+ GMainLoop *loop = g_main_loop_new (NULL, TRUE);
+
+ debug_printf (1, "test_sniffing(\"%s\", \"%s\")\n", path, expected_type);
+
+ g_object_connect (msg,
+ "signal::content_sniffed", sniffing_content_sniffed, expected_type,
+ NULL);
+
+ g_object_ref (msg);
+
+ soup_session_queue_message (session, msg, finished, loop);
+
+ g_main_loop_run (loop);
+
+ soup_uri_free (uri);
+ g_object_unref (msg);
+ g_main_loop_unref (loop);
+}
+
+int
+main (int argc, char **argv)
+{
+ SoupServer *server;
+ SoupContentSniffer *sniffer;
+
+ test_init (argc, argv, NULL);
+
+ server = soup_test_server_new (TRUE);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ base_uri = soup_uri_new ("http://127.0.0.1/");
+ soup_uri_set_port (base_uri, soup_server_get_port (server));
+
+ session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+
+ /* No sniffer, no content_sniffed should be emitted */
+ do_signals_test (FALSE, FALSE, FALSE, FALSE, FALSE);
+ do_signals_test (FALSE, FALSE, FALSE, TRUE, FALSE);
+ do_signals_test (FALSE, FALSE, TRUE, FALSE, FALSE);
+ do_signals_test (FALSE, FALSE, TRUE, TRUE, FALSE);
+
+ do_signals_test (FALSE, TRUE, TRUE, FALSE, FALSE);
+ do_signals_test (FALSE, TRUE, TRUE, TRUE, FALSE);
+ do_signals_test (FALSE, TRUE, FALSE, FALSE, FALSE);
+ do_signals_test (FALSE, TRUE, FALSE, TRUE, FALSE);
+
+ /* Tests that the signals are correctly emitted for empty
+ * responses; see
+ * http://bugzilla.gnome.org/show_bug.cgi?id=587907 */
+
+ do_signals_test (FALSE, TRUE, TRUE, FALSE, TRUE);
+ do_signals_test (FALSE, TRUE, TRUE, TRUE, TRUE);
+
+ sniffer = soup_content_sniffer_new ();
+ soup_session_add_feature (session, (SoupSessionFeature*)sniffer);
+
+ /* Now, with a sniffer, content_sniffed must be emitted after
+ * got-headers, and before got-chunk.
+ */
+ do_signals_test (TRUE, FALSE, FALSE, FALSE, FALSE);
+ do_signals_test (TRUE, FALSE, FALSE, TRUE, FALSE);
+ do_signals_test (TRUE, FALSE, TRUE, FALSE, FALSE);
+ do_signals_test (TRUE, FALSE, TRUE, TRUE, FALSE);
+
+ do_signals_test (TRUE, TRUE, TRUE, FALSE, FALSE);
+ do_signals_test (TRUE, TRUE, TRUE, TRUE, FALSE);
+ do_signals_test (TRUE, TRUE, FALSE, FALSE, FALSE);
+ do_signals_test (TRUE, TRUE, FALSE, TRUE, FALSE);
+
+ /* Empty response tests */
+ do_signals_test (TRUE, TRUE, TRUE, FALSE, TRUE);
+ do_signals_test (TRUE, TRUE, TRUE, TRUE, TRUE);
+
+ /* Test the text_or_binary sniffing path */
+
+ /* GIF is a 'safe' type */
+ test_sniffing ("/text_or_binary/home.gif", "image/gif");
+
+ /* With our current code, no sniffing is done using GIO, so
+ * the mbox will be identified as text/plain; should we change
+ * this?
+ */
+ test_sniffing ("/text_or_binary/mbox", "text/plain");
+
+ /* HTML is considered unsafe for this algorithm, since it is
+ * scriptable, so going from text/plain to text/html is
+ * considered 'privilege escalation'
+ */
+ test_sniffing ("/text_or_binary/test.html", "text/plain");
+
+ /* Test the unknown sniffing path */
+
+ test_sniffing ("/unknown/test.html", "text/html");
+ test_sniffing ("/unknown/home.gif", "image/gif");
+ test_sniffing ("/unknown/mbox", "application/mbox");
+
+ /* Test the XML sniffing path */
+
+ test_sniffing ("/type/text_xml/home.gif", "text/xml");
+ test_sniffing ("/type/anice_type+xml/home.gif", "anice/type+xml");
+ test_sniffing ("/type/application_xml/home.gif", "application/xml");
+
+ /* Test the image sniffing path */
+
+ test_sniffing ("/type/image_png/home.gif", "image/gif");
+
+ /* Test the feed or html path */
+
+ test_sniffing ("/type/text_html/test.html", "text/html");
+ test_sniffing ("/type/text_html/rss20.xml", "application/rss+xml");
+ test_sniffing ("/type/text_html/atom.xml", "application/atom+xml");
+
+ /* The spec tells us to only use the last Content-Type header */
+
+ test_sniffing ("/multiple_headers/home.gif", "image/gif");
+
+ soup_uri_free (base_uri);
+
+ test_cleanup ();
+ return errors != 0;
+}