soup-session: fix some http-aliases/https-aliases problems
authorDan Winship <danw@gnome.org>
Wed, 30 Nov 2011 12:34:43 +0000 (13:34 +0100)
committerDan Winship <danw@gnome.org>
Thu, 1 Dec 2011 10:10:35 +0000 (11:10 +0100)
and add a test to misc-test

libsoup/soup-session.c
libsoup/soup-uri.c
tests/misc-test.c

index 94b50a2..3babab4 100644 (file)
@@ -73,6 +73,8 @@ typedef struct {
        GSource     *keep_alive_src;
        SoupSession *session;
 } SoupSessionHost;
+static guint soup_host_uri_hash (gconstpointer key);
+gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
 
 typedef struct {
        GTlsDatabase *tlsdb;
@@ -88,7 +90,7 @@ typedef struct {
        GSList *features;
        GHashTable *features_cache;
 
-       GHashTable *hosts; /* char* -> SoupSessionHost */
+       GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
        GHashTable *conns; /* SoupConnection -> SoupSessionHost */
        guint num_conns;
        guint max_conns, max_conns_per_host;
@@ -186,9 +188,12 @@ soup_session_init (SoupSession *session)
        priv->queue = soup_message_queue_new (session);
 
        g_mutex_init (&priv->host_lock);
-       priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
-                                            soup_uri_host_equal,
-                                            NULL, (GDestroyNotify)free_host);
+       priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
+                                                 soup_host_uri_equal,
+                                                 NULL, (GDestroyNotify)free_host);
+       priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
+                                                  soup_host_uri_equal,
+                                                  NULL, (GDestroyNotify)free_host);
        priv->conns = g_hash_table_new (NULL, NULL);
 
        priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
@@ -241,7 +246,8 @@ finalize (GObject *object)
        soup_message_queue_destroy (priv->queue);
 
        g_mutex_clear (&priv->host_lock);
-       g_hash_table_destroy (priv->hosts);
+       g_hash_table_destroy (priv->http_hosts);
+       g_hash_table_destroy (priv->https_hosts);
        g_hash_table_destroy (priv->conns);
 
        g_free (priv->user_agent);
@@ -1048,12 +1054,18 @@ load_ssl_ca_file (SoupSessionPrivate *priv)
 static void
 set_aliases (char ***variable, char **value)
 {
-       int len = g_strv_length (value), i;
+       int len, i;
 
        if (*variable)
                g_free (*variable);
 
-       *variable = g_new (char *, len);
+       if (!value) {
+               *variable = NULL;
+               return;
+       }
+
+       len = g_strv_length (value);
+       *variable = g_new (char *, len + 1);
        for (i = 0; i < len; i++)
                (*variable)[i] = (char *)g_intern_string (value[i]);
        (*variable)[i] = NULL;
@@ -1358,6 +1370,32 @@ soup_session_get_async_context (SoupSession *session)
 
 /* Hosts */
 
+static guint
+soup_host_uri_hash (gconstpointer key)
+{
+       const SoupURI *uri = key;
+
+       g_return_val_if_fail (uri != NULL && uri->host != NULL, 0);
+
+       return uri->port + soup_str_case_hash (uri->host);
+}
+
+gboolean
+soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
+{
+       const SoupURI *one = v1;
+       const SoupURI *two = v2;
+
+       g_return_val_if_fail (one != NULL && two != NULL, one == two);
+       g_return_val_if_fail (one->host != NULL && two->host != NULL, one->host == two->host);
+
+       if (one->port != two->port)
+               return FALSE;
+
+       return g_ascii_strcasecmp (one->host, two->host) == 0;
+}
+
+
 static SoupSessionHost *
 soup_session_host_new (SoupSession *session, SoupURI *uri)
 {
@@ -1365,6 +1403,16 @@ soup_session_host_new (SoupSession *session, SoupURI *uri)
 
        host = g_slice_new0 (SoupSessionHost);
        host->uri = soup_uri_copy_host (uri);
+       if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
+           host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
+               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+
+               if (uri_is_https (priv, host->uri))
+                       host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
+               else
+                       host->uri->scheme = SOUP_URI_SCHEME_HTTP;
+       }
+
        host->addr = soup_address_new (host->uri->host, host->uri->port);
        host->keep_alive_src = NULL;
        host->session = session;
@@ -1379,12 +1427,19 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
        SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
        SoupSessionHost *host;
 
-       host = g_hash_table_lookup (priv->hosts, uri);
+       if (uri_is_https (priv, uri))
+               host = g_hash_table_lookup (priv->https_hosts, uri);
+       else
+               host = g_hash_table_lookup (priv->http_hosts, uri);
        if (host)
                return host;
 
        host = soup_session_host_new (session, uri);
-       g_hash_table_insert (priv->hosts, host->uri, host);
+
+       if (uri_is_https (priv, uri))
+               g_hash_table_insert (priv->https_hosts, host->uri, host);
+       else
+               g_hash_table_insert (priv->http_hosts, host->uri, host);
 
        return host;
 }
@@ -1684,7 +1739,10 @@ free_unused_host (gpointer user_data)
        /* This will free the host in addition to removing it from the
         * hash table
         */
-       g_hash_table_remove (priv->hosts, host->uri);
+       if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
+               g_hash_table_remove (priv->https_hosts, host->uri);
+       else
+               g_hash_table_remove (priv->http_hosts, host->uri);
        g_mutex_unlock (&priv->host_lock);
 
        return FALSE;
index 9170feb..cb7c5ce 100644 (file)
@@ -742,10 +742,6 @@ soup_uri_normalize (const char *part, const char *unescape_extra)
 gboolean
 soup_uri_uses_default_port (SoupURI *uri)
 {
-       g_return_val_if_fail (uri->scheme == SOUP_URI_SCHEME_HTTP ||
-                             uri->scheme == SOUP_URI_SCHEME_HTTPS ||
-                             uri->scheme == SOUP_URI_SCHEME_FTP, FALSE);
-
        return uri->port == soup_scheme_default_port (uri->scheme);
 }
 
index b1bc2e7..332e938 100644 (file)
@@ -18,8 +18,8 @@
 
 #include "test-utils.h"
 
-SoupServer *server;
-SoupURI *base_uri;
+SoupServer *server, *ssl_server;
+SoupURI *base_uri, *ssl_base_uri;
 GMutex server_mutex;
 
 static gboolean
@@ -117,6 +117,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                 SoupClientContext *context, gpointer data)
 {
        SoupURI *uri = soup_message_get_uri (msg);
+       const char *server_protocol = data;
 
        /* The way this gets used in the tests, we don't actually
         * need to hold it through the whole function, so it's simpler
@@ -145,6 +146,34 @@ server_callback (SoupServer *server, SoupMessage *msg,
                return;
        }
 
+       if (!strcmp (path, "/alias-redirect")) {
+               SoupURI *redirect_uri;
+               char *redirect_string;
+               const char *redirect_protocol;
+
+               redirect_protocol = soup_message_headers_get_one (msg->request_headers, "X-Redirect-Protocol");
+
+               redirect_uri = soup_uri_copy (uri);
+               soup_uri_set_scheme (redirect_uri, "foo");
+               if (!g_strcmp0 (redirect_protocol, "https"))
+                       soup_uri_set_port (redirect_uri, ssl_base_uri->port);
+               else
+                       soup_uri_set_port (redirect_uri, base_uri->port);
+               soup_uri_set_path (redirect_uri, "/alias-redirected");
+               redirect_string = soup_uri_to_string (redirect_uri, FALSE);
+
+               soup_message_set_redirect (msg, SOUP_STATUS_FOUND, redirect_string);
+               g_free (redirect_string);
+               soup_uri_free (redirect_uri);
+               return;
+       } else if (!strcmp (path, "/alias-redirected")) {
+               soup_message_set_status (msg, SOUP_STATUS_OK);
+               soup_message_headers_append (msg->response_headers,
+                                            "X-Redirected-Protocol",
+                                            server_protocol);
+               return;
+       }
+
        if (g_str_has_prefix (path, "/content-length/")) {
                gboolean too_long = strcmp (path, "/content-length/long") == 0;
                gboolean no_close = strcmp (path, "/content-length/noclose") == 0;
@@ -1011,6 +1040,69 @@ do_cancel_while_reading_test (void)
        soup_test_session_abort_unref (session);
 }
 
+static void
+do_aliases_test_for_session (SoupSession *session,
+                            const char *redirect_protocol)
+{
+       SoupMessage *msg;
+       SoupURI *uri;
+       const char *redirected_protocol;
+
+       uri = soup_uri_new_with_base (base_uri, "/alias-redirect");
+       msg = soup_message_new_from_uri ("GET", uri);
+       if (redirect_protocol)
+               soup_message_headers_append (msg->request_headers, "X-Redirect-Protocol", redirect_protocol);
+       soup_uri_free (uri);
+       soup_session_send_message (session, msg);
+
+       redirected_protocol = soup_message_headers_get_one (msg->response_headers, "X-Redirected-Protocol");
+
+       if (g_strcmp0 (redirect_protocol, redirected_protocol)) {
+               debug_printf (1, "    redirect went to %s, should have gone to %s!\n",
+                             redirected_protocol ? redirected_protocol : "(none)",
+                             redirect_protocol ? redirect_protocol : "(none)");
+               errors++;
+       } else if (redirect_protocol && !SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+               debug_printf (1, "    msg failed? (%d %s)\n",
+                             msg->status_code, msg->reason_phrase);
+               errors++;
+       } else if (!redirect_protocol && SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+               debug_printf (1, "    msg succeeded? (%d %s)\n",
+                             msg->status_code, msg->reason_phrase);
+               errors++;
+       }
+
+       g_object_unref (msg);
+}
+
+static void
+do_aliases_test (void)
+{
+       SoupSession *session;
+       char *aliases[] = { "foo", NULL };
+
+       debug_printf (1, "\nhttp-aliases / https-aliases\n");
+
+       debug_printf (1, "  Default behavior\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+       do_aliases_test_for_session (session, "http");
+       soup_test_session_abort_unref (session);
+
+       debug_printf (1, "  foo-means-https\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+                                        SOUP_SESSION_HTTPS_ALIASES, aliases,
+                                        NULL);
+       do_aliases_test_for_session (session, "https");
+       soup_test_session_abort_unref (session);
+
+       debug_printf (1, "  foo-means-nothing\n");
+       session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
+                                        SOUP_SESSION_HTTP_ALIASES, NULL,
+                                        NULL);
+       do_aliases_test_for_session (session, NULL);
+       soup_test_session_abort_unref (session);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -1019,7 +1111,7 @@ main (int argc, char **argv)
        test_init (argc, argv, NULL);
 
        server = soup_test_server_new (TRUE);
-       soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+       soup_server_add_handler (server, NULL, server_callback, "http", NULL);
        base_uri = soup_uri_new ("http://127.0.0.1/");
        soup_uri_set_port (base_uri, soup_server_get_port (server));
 
@@ -1031,6 +1123,11 @@ main (int argc, char **argv)
        soup_server_add_auth_domain (server, auth_domain);
        g_object_unref (auth_domain);
 
+       ssl_server = soup_test_server_new_ssl (TRUE);
+       soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL);
+       ssl_base_uri = soup_uri_new ("https://127.0.0.1/");
+       soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server));
+
        do_host_test ();
        do_callback_unref_test ();
        do_msg_reuse_test ();
@@ -1041,9 +1138,12 @@ main (int argc, char **argv)
        do_persistent_connection_timeout_test ();
        do_max_conns_test ();
        do_cancel_while_reading_test ();
+       do_aliases_test ();
 
        soup_uri_free (base_uri);
+       soup_uri_free (ssl_base_uri);
        soup_test_server_quit_unref (server);
+       soup_test_server_quit_unref (ssl_server);
 
        test_cleanup ();
        return errors != 0;