summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2013-08-25 10:51:22 -0400
committerDan Winship <danw@gnome.org>2013-08-26 11:00:42 -0400
commit4a8b1c0544b168b25cbd3b016b7aedee263515ba (patch)
tree823f0e9fbc26d311aa3fb6e16f80684407ecb86f
parent3935e102d8195ec12349e688b87e200df4f33649 (diff)
downloadlibsoup-4a8b1c0544b168b25cbd3b016b7aedee263515ba.tar.gz
SoupServer: add :http-aliases and :https-aliases properties
Add :http-aliases and :https-aliases properties to SoupServer, to allow it to handle requests like: GET daap://host:port/path HTTP/1.1 https://bugzilla.gnome.org/show_bug.cgi?id=703694
-rw-r--r--libsoup/soup-message-server-io.c5
-rw-r--r--libsoup/soup-misc-private.h2
-rw-r--r--libsoup/soup-server.c117
-rw-r--r--libsoup/soup-server.h2
-rw-r--r--libsoup/soup-session.c54
-rw-r--r--libsoup/soup-uri.c43
-rw-r--r--tests/server-test.c130
7 files changed, 298 insertions, 55 deletions
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 814ae7e5..2647b811 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -98,10 +98,7 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
g_free (req_path);
- if (!SOUP_URI_VALID_FOR_HTTP (uri)) {
- /* certainly not "a valid host on the server" (RFC2616 5.2.3)
- * SOUP_URI_VALID_FOR_HTTP also guards against uri == NULL
- */
+ if (!uri || !uri->host) {
if (uri)
soup_uri_free (uri);
return SOUP_STATUS_BAD_REQUEST;
diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h
index 02768706..e9c83f61 100644
--- a/libsoup/soup-misc-private.h
+++ b/libsoup/soup-misc-private.h
@@ -13,6 +13,8 @@
char *uri_decoded_copy (const char *str, int length, int *decoded_length);
char *soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
gboolean force_port);
+gboolean soup_uri_is_http (SoupURI *uri, char **aliases);
+gboolean soup_uri_is_https (SoupURI *uri, char **aliases);
guint soup_socket_handshake_sync (SoupSocket *sock,
GCancellable *cancellable);
diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c
index ff51e5bd..abda60ba 100644
--- a/libsoup/soup-server.c
+++ b/libsoup/soup-server.c
@@ -14,6 +14,7 @@
#include "soup-server.h"
#include "soup.h"
#include "soup-message-private.h"
+#include "soup-misc-private.h"
#include "soup-path-map.h"
/**
@@ -105,6 +106,8 @@ typedef struct {
GSList *auth_domains;
GMainContext *async_context;
+
+ char **http_aliases, **https_aliases;
} SoupServerPrivate;
#define SOUP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER, SoupServerPrivate))
@@ -121,6 +124,8 @@ enum {
PROP_ASYNC_CONTEXT,
PROP_RAW_PATHS,
PROP_SERVER_HEADER,
+ PROP_HTTP_ALIASES,
+ PROP_HTTPS_ALIASES,
LAST_PROP
};
@@ -141,6 +146,10 @@ soup_server_init (SoupServer *server)
SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
priv->handlers = soup_path_map_new ((GDestroyNotify)free_handler);
+
+ priv->http_aliases = g_new (char *, 2);
+ priv->http_aliases[0] = (char *)g_intern_string ("*");
+ priv->http_aliases[1] = NULL;
}
static void
@@ -191,6 +200,9 @@ soup_server_finalize (GObject *object)
g_clear_pointer (&priv->loop, g_main_loop_unref);
g_clear_pointer (&priv->async_context, g_main_context_unref);
+ g_free (priv->http_aliases);
+ g_free (priv->https_aliases);
+
G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
}
@@ -250,6 +262,29 @@ soup_server_constructor (GType type,
return server;
}
+/* priv->http_aliases and priv->https_aliases are stored as arrays of
+ * *interned* strings, so we can't just use g_strdupv() to set them.
+ */
+static void
+set_aliases (char ***variable, char **value)
+{
+ int len, i;
+
+ if (*variable)
+ g_free (*variable);
+
+ 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;
+}
+
static void
soup_server_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
@@ -304,6 +339,12 @@ soup_server_set_property (GObject *object, guint prop_id,
} else
priv->server_header = g_strdup (header);
break;
+ case PROP_HTTP_ALIASES:
+ set_aliases (&priv->http_aliases, g_value_get_boxed (value));
+ break;
+ case PROP_HTTPS_ALIASES:
+ set_aliases (&priv->https_aliases, g_value_get_boxed (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -341,6 +382,12 @@ soup_server_get_property (GObject *object, guint prop_id,
case PROP_SERVER_HEADER:
g_value_set_string (value, priv->server_header);
break;
+ case PROP_HTTP_ALIASES:
+ g_value_set_boxed (value, priv->http_aliases);
+ break;
+ case PROP_HTTPS_ALIASES:
+ g_value_set_boxed (value, priv->https_aliases);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -628,6 +675,68 @@ soup_server_class_init (SoupServerClass *server_class)
"Server header",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ /**
+ * SoupServer:http-aliases:
+ *
+ * A %NULL-terminated array of URI schemes that should be
+ * considered to be aliases for "http". Eg, if this included
+ * <literal>"dav"</literal>, than a URI of
+ * <literal>dav://example.com/path</literal> would be treated
+ * identically to <literal>http://example.com/path</literal>.
+ * In particular, this is needed in cases where a client
+ * sends requests with absolute URIs, where those URIs do
+ * not use "http:".
+ *
+ * The default value is an array containing the single element
+ * <literal>"*"</literal>, a special value which means that
+ * any scheme except "https" is considered to be an alias for
+ * "http".
+ *
+ * See also #SoupServer:https-aliases.
+ *
+ * Since: 2.44
+ */
+ /**
+ * SOUP_SERVERI_HTTP_ALIASES:
+ *
+ * Alias for the #SoupServer:http-aliases property, qv.
+ *
+ * Since: 2.44
+ */
+ g_object_class_install_property (
+ object_class, PROP_HTTP_ALIASES,
+ g_param_spec_boxed (SOUP_SERVER_HTTP_ALIASES,
+ "http aliases",
+ "URI schemes that are considered aliases for 'http'",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE));
+ /**
+ * SoupServer:https-aliases:
+ *
+ * A comma-delimited list of URI schemes that should be
+ * considered to be aliases for "https". See
+ * #SoupServer:http-aliases for more information.
+ *
+ * The default value is %NULL, meaning that no URI schemes
+ * are considered aliases for "https".
+ *
+ * Since: 2.44
+ */
+ /**
+ * SOUP_SERVER_HTTPS_ALIASES:
+ *
+ * Alias for the #SoupServer:https-aliases property, qv.
+ *
+ * Since: 2.44
+ **/
+ g_object_class_install_property (
+ object_class, PROP_HTTPS_ALIASES,
+ g_param_spec_boxed (SOUP_SERVER_HTTPS_ALIASES,
+ "https aliases",
+ "URI schemes that are considered aliases for 'https'",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE));
}
/**
@@ -816,10 +925,16 @@ got_headers (SoupMessage *msg, SoupClientContext *client)
gboolean rejected = FALSE;
char *auth_user;
+ uri = soup_message_get_uri (msg);
+ if ((soup_server_is_https (server) && !soup_uri_is_https (uri, priv->https_aliases)) ||
+ (!soup_server_is_https (server) && !soup_uri_is_http (uri, priv->http_aliases))) {
+ soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+ return;
+ }
+
if (!priv->raw_paths) {
char *decoded_path;
- uri = soup_message_get_uri (msg);
decoded_path = soup_uri_decode (uri->path);
if (strstr (decoded_path, "/../") ||
diff --git a/libsoup/soup-server.h b/libsoup/soup-server.h
index e1c9bbfb..0d09322e 100644
--- a/libsoup/soup-server.h
+++ b/libsoup/soup-server.h
@@ -64,6 +64,8 @@ typedef void (*SoupServerCallback) (SoupServer *server,
#define SOUP_SERVER_ASYNC_CONTEXT "async-context"
#define SOUP_SERVER_RAW_PATHS "raw-paths"
#define SOUP_SERVER_SERVER_HEADER "server-header"
+#define SOUP_SERVER_HTTP_ALIASES "http-aliases"
+#define SOUP_SERVER_HTTPS_ALIASES "https-aliases"
SoupServer *soup_server_new (const char *optname1,
...) G_GNUC_NULL_TERMINATED;
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 10851b3e..85ef1b25 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -831,49 +831,6 @@ soup_session_new_with_options (const char *optname1,
return session;
}
-static gboolean
-uri_is_http (SoupSessionPrivate *priv, SoupURI *uri)
-{
- int i;
-
- if (uri->scheme == SOUP_URI_SCHEME_HTTP)
- return TRUE;
- else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
- return FALSE;
- else if (!priv->http_aliases)
- return FALSE;
-
- for (i = 0; priv->http_aliases[i]; i++) {
- if (uri->scheme == priv->http_aliases[i])
- return TRUE;
- }
-
- if (!priv->http_aliases[1] && !strcmp (priv->http_aliases[0], "*"))
- return TRUE;
- else
- return FALSE;
-}
-
-static gboolean
-uri_is_https (SoupSessionPrivate *priv, SoupURI *uri)
-{
- int i;
-
- if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
- return TRUE;
- else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
- return FALSE;
- else if (!priv->https_aliases)
- return FALSE;
-
- for (i = 0; priv->https_aliases[i]; i++) {
- if (uri->scheme == priv->https_aliases[i])
- return TRUE;
- }
-
- return FALSE;
-}
-
/**
* soup_session_get_async_context:
* @session: a #SoupSession
@@ -945,7 +902,7 @@ soup_session_host_new (SoupSession *session, SoupURI *uri)
host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
- if (uri_is_https (priv, host->uri))
+ if (soup_uri_is_https (host->uri, priv->https_aliases))
host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
else
host->uri->scheme = SOUP_URI_SCHEME_HTTP;
@@ -969,7 +926,7 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
SoupSessionHost *host;
- if (uri_is_https (priv, uri))
+ if (soup_uri_is_https (uri, priv->https_aliases))
host = g_hash_table_lookup (priv->https_hosts, uri);
else
host = g_hash_table_lookup (priv->http_hosts, uri);
@@ -978,7 +935,7 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
host = soup_session_host_new (session, uri);
- if (uri_is_https (priv, uri))
+ if (soup_uri_is_https (uri, priv->https_aliases))
g_hash_table_insert (priv->https_hosts, host->uri, host);
else
g_hash_table_insert (priv->http_hosts, host->uri, host);
@@ -1080,7 +1037,8 @@ soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
if (!new_uri)
return FALSE;
if (!new_uri->host || !*new_uri->host ||
- (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri))) {
+ (!soup_uri_is_http (new_uri, priv->http_aliases) &&
+ !soup_uri_is_https (new_uri, priv->https_aliases))) {
soup_uri_free (new_uri);
return FALSE;
}
@@ -1691,7 +1649,7 @@ get_connection_for_host (SoupSession *session,
SOUP_TYPE_CONNECTION,
SOUP_CONNECTION_REMOTE_URI, host->uri,
SOUP_CONNECTION_PROXY_RESOLVER, priv->proxy_resolver,
- SOUP_CONNECTION_SSL, uri_is_https (priv, soup_message_get_uri (item->msg)),
+ SOUP_CONNECTION_SSL, soup_uri_is_https (soup_message_get_uri (item->msg), priv->https_aliases),
SOUP_CONNECTION_SSL_CREDENTIALS, priv->tlsdb,
SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict && (priv->tlsdb != NULL || SOUP_IS_PLAIN_SESSION (session)),
SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c
index 6e3d220b..808d2ccc 100644
--- a/libsoup/soup-uri.c
+++ b/libsoup/soup-uri.c
@@ -1291,4 +1291,47 @@ soup_uri_host_equal (gconstpointer v1, gconstpointer v2)
return g_ascii_strcasecmp (one->host, two->host) == 0;
}
+gboolean
+soup_uri_is_http (SoupURI *uri, char **aliases)
+{
+ int i;
+
+ if (uri->scheme == SOUP_URI_SCHEME_HTTP)
+ return TRUE;
+ else if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
+ return FALSE;
+ else if (!aliases)
+ return FALSE;
+
+ for (i = 0; aliases[i]; i++) {
+ if (uri->scheme == aliases[i])
+ return TRUE;
+ }
+
+ if (!aliases[1] && !strcmp (aliases[0], "*"))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean
+soup_uri_is_https (SoupURI *uri, char **aliases)
+{
+ int i;
+
+ if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
+ return TRUE;
+ else if (uri->scheme == SOUP_URI_SCHEME_HTTP)
+ return FALSE;
+ else if (!aliases)
+ return FALSE;
+
+ for (i = 0; aliases[i]; i++) {
+ if (uri->scheme == aliases[i])
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
G_DEFINE_BOXED_TYPE (SoupURI, soup_uri, soup_uri_copy, soup_uri_free)
diff --git a/tests/server-test.c b/tests/server-test.c
index 3cad5c52..3ff7cadd 100644
--- a/tests/server-test.c
+++ b/tests/server-test.c
@@ -5,8 +5,8 @@
#include "test-utils.h"
-SoupServer *server;
-SoupURI *base_uri;
+SoupServer *server, *ssl_server;
+SoupURI *base_uri, *ssl_base_uri;
static void
server_callback (SoupServer *server, SoupMessage *msg,
@@ -120,6 +120,109 @@ do_star_test (void)
}
static void
+do_one_server_aliases_test (SoupURI *uri,
+ const char *alias,
+ gboolean succeed)
+{
+ GSocketClient *client;
+ GSocketConnectable *addr;
+ GSocketConnection *conn;
+ GInputStream *in;
+ GOutputStream *out;
+ GError *error = NULL;
+ GString *req;
+ static char buf[1024];
+
+ debug_printf (1, " %s via %s\n", alias, uri->scheme);
+
+ /* There's no way to make libsoup's client side send an absolute
+ * URI (to a non-proxy server), so we have to fake this.
+ */
+
+ client = g_socket_client_new ();
+ if (uri->scheme == SOUP_URI_SCHEME_HTTPS) {
+ g_socket_client_set_tls (client, TRUE);
+ g_socket_client_set_tls_validation_flags (client, 0);
+ }
+ addr = g_network_address_new (uri->host, uri->port);
+
+ conn = g_socket_client_connect (client, addr, NULL, &error);
+ g_object_unref (addr);
+ g_object_unref (client);
+ if (!conn) {
+ debug_printf (1, " error connecting to server: %s\n",
+ error->message);
+ g_error_free (error);
+ errors++;
+ return;
+ }
+
+ in = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+ out = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+
+ req = g_string_new (NULL);
+ g_string_append_printf (req, "GET %s://%s:%d HTTP/1.1\r\n",
+ alias, uri->host, uri->port);
+ g_string_append_printf (req, "Host: %s:%d\r\n",
+ uri->host, uri->port);
+ g_string_append (req, "Connection: close\r\n\r\n");
+
+ if (!g_output_stream_write_all (out, req->str, req->len, NULL, NULL, &error)) {
+ debug_printf (1, " error sending request: %s\n",
+ error->message);
+ g_error_free (error);
+ errors++;
+ g_object_unref (conn);
+ g_string_free (req, TRUE);
+ return;
+ }
+ g_string_free (req, TRUE);
+
+ if (!g_input_stream_read_all (in, buf, sizeof (buf), NULL, NULL, &error)) {
+ debug_printf (1, " error reading response: %s\n",
+ error->message);
+ g_error_free (error);
+ errors++;
+ g_object_unref (conn);
+ return;
+ }
+
+ if ((succeed && !g_str_has_prefix (buf, "HTTP/1.1 200 ")) ||
+ (!succeed && !g_str_has_prefix (buf, "HTTP/1.1 400 "))) {
+ debug_printf (1, " unexpected response: %.*s\n",
+ (int) strcspn (buf, "\r\n"), buf);
+ errors++;
+ }
+
+ g_io_stream_close (G_IO_STREAM (conn), NULL, NULL);
+ g_object_unref (conn);
+}
+
+static void
+do_server_aliases_test (void)
+{
+ char *http_good[] = { "http", "dav", NULL };
+ char *http_bad[] = { "https", "davs", "fred", NULL };
+ char *https_good[] = { "https", "davs", NULL };
+ char *https_bad[] = { "http", "dav", "fred", NULL };
+ int i;
+
+ debug_printf (1, "\nserver aliases test\n");
+
+ for (i = 0; http_good[i]; i++)
+ do_one_server_aliases_test (base_uri, http_good[i], TRUE);
+ for (i = 0; http_bad[i]; i++)
+ do_one_server_aliases_test (base_uri, http_bad[i], FALSE);
+
+ if (tls_available) {
+ for (i = 0; https_good[i]; i++)
+ do_one_server_aliases_test (ssl_base_uri, https_good[i], TRUE);
+ for (i = 0; https_bad[i]; i++)
+ do_one_server_aliases_test (ssl_base_uri, https_bad[i], FALSE);
+ }
+}
+
+static void
do_dot_dot_test (void)
{
SoupSession *session;
@@ -232,6 +335,9 @@ do_ipv6_test (void)
int
main (int argc, char **argv)
{
+ char *http_aliases[] = { "dav", NULL };
+ char *https_aliases[] = { "davs", NULL };
+
test_init (argc, argv, NULL);
server = soup_test_server_new (TRUE);
@@ -239,13 +345,33 @@ main (int argc, char **argv)
base_uri = soup_uri_new ("http://127.0.0.1/");
soup_uri_set_port (base_uri, soup_server_get_port (server));
+ g_object_set (G_OBJECT (server),
+ SOUP_SERVER_HTTP_ALIASES, http_aliases,
+ NULL);
+
+ if (tls_available) {
+ ssl_server = soup_test_server_new_ssl (TRUE);
+ soup_server_add_handler (ssl_server, NULL, server_callback, NULL, 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));
+ g_object_set (G_OBJECT (ssl_server),
+ SOUP_SERVER_HTTPS_ALIASES, https_aliases,
+ NULL);
+ }
+
do_star_test ();
+ do_server_aliases_test ();
do_dot_dot_test ();
do_ipv6_test ();
soup_uri_free (base_uri);
soup_test_server_quit_unref (server);
+ if (tls_available) {
+ soup_uri_free (ssl_base_uri);
+ soup_test_server_quit_unref (ssl_server);
+ }
+
test_cleanup ();
return errors != 0;
}