[libsoup] Support "OPTIONS *" in SoupServer



commit 280dc8568d9ebe9fb16fb13b2272169e968b3e49
Author: Dan Winship <danw gnome org>
Date:   Tue Aug 4 19:34:30 2009 -0400

    Support "OPTIONS *" in SoupServer
    
    Update soup-message-server-io.c to allow "*" in a Request-Line, and
    update SoupServer and SoupAuthDomain methods and docs to deal with it.
    For backward-compatibility, requests to "*" are NOT passed to the
    default handler; you must explicitly register a handler for "*".
    
    http://bugzilla.gnome.org/show_bug.cgi?id=590751

 libsoup/soup-auth-domain.c       |   19 ++++++--
 libsoup/soup-message-server-io.c |   21 ++++++--
 libsoup/soup-server.c            |   24 +++++++++-
 tests/misc-test.c                |   95 +++++++++++++++++++++++++++++++++++++-
 4 files changed, 146 insertions(+), 13 deletions(-)
---
diff --git a/libsoup/soup-auth-domain.c b/libsoup/soup-auth-domain.c
index d3949d1..62fa772 100644
--- a/libsoup/soup-auth-domain.c
+++ b/libsoup/soup-auth-domain.c
@@ -29,7 +29,9 @@
  * In order for an auth domain to have any effect, you must add one or
  * more paths to it (via soup_auth_domain_add_path() or the
  * %SOUP_AUTH_DOMAIN_ADD_PATH property). To require authentication for
- * all requests, add the path "/".
+ * all ordinary requests, add the path "/". (Note that this does not
+ * include the special "*" URI (eg, "OPTIONS *"), which must be added
+ * as a separate path if you want to cover it.)
  *
  * If you need greater control over which requests should and
  * shouldn't be authenticated, add paths covering everything you
@@ -312,6 +314,10 @@ soup_auth_domain_add_path (SoupAuthDomain *domain, const char *path)
 {
 	SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
 
+	/* "" should not match "*" */
+	if (!*path)
+		path = "/";
+
 	soup_path_map_add (priv->paths, path, GINT_TO_POINTER (TRUE));
 }
 
@@ -340,6 +346,10 @@ soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path)
 {
 	SoupAuthDomainPrivate *priv = SOUP_AUTH_DOMAIN_GET_PRIVATE (domain);
 
+	/* "" should not match "*" */
+	if (!*path)
+		path = "/";
+
 	soup_path_map_add (priv->paths, path, GINT_TO_POINTER (FALSE));
 }
 
@@ -529,8 +539,8 @@ soup_auth_domain_check_password (SoupAuthDomain *domain,
  *
  * Checks if @domain requires @msg to be authenticated (according to
  * its paths and filter function). This does not actually look at
- * whether @msg *is* authenticated, merely whether or not is needs to
- * be.
+ * whether @msg <emphasis>is</emphasis> authenticated, merely whether
+ * or not it needs to be.
  *
  * This is used by #SoupServer internally and is probably of no use to
  * anyone else.
@@ -560,7 +570,8 @@ soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg)
  *
  * Checks if @msg contains appropriate authorization for @domain to
  * accept it. Mirroring soup_auth_domain_covers(), this does not check
- * whether or not @domain *cares* if @msg is authorized.
+ * whether or not @domain <emphasis>cares</emphasis> if @msg is
+ * authorized.
  *
  * This is used by #SoupServer internally and is probably of no use to
  * anyone else.
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 2e769f4..56d2a6b 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -56,14 +56,23 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
 
 	/* Generate correct context for request */
 	req_host = soup_message_headers_get_one (msg->request_headers, "Host");
+	if (strchr (req_host, '/')) {
+		g_free (req_path);
+		return SOUP_STATUS_BAD_REQUEST;
+	}
 
-	if (*req_path != '/') {
-		/* Check for absolute URI */
+	if (!strcmp (req_path, "*") && req_host) {
+		/* Eg, "OPTIONS * HTTP/1.1" */
+		url = g_strdup_printf ("%s://%s",
+				       soup_socket_is_ssl (sock) ? "https" : "http",
+				       req_host);
+		uri = soup_uri_new (url);
+		if (uri)
+			soup_uri_set_path (uri, "*");
+		g_free (url);
+	} else if (*req_path != '/') {
+		/* Must be an absolute URI */
 		uri = soup_uri_new (req_path);
-		if (!uri) {
-			g_free (req_path);
-			return SOUP_STATUS_BAD_REQUEST;
-		}
 	} else if (req_host) {
 		url = g_strdup_printf ("%s://%s%s",
 				       soup_socket_is_ssl (sock) ? "https" : "http",
diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c
index e34fffb..b7ccef2 100644
--- a/libsoup/soup-server.c
+++ b/libsoup/soup-server.c
@@ -40,10 +40,15 @@
  * same handler, just pass "/" (or %NULL) for the path.) Any request
  * that does not match any handler will automatically be returned to
  * the client with a 404 (Not Found) status.
+ *
+ * If you want to handle the special "*" URI (eg, "OPTIONS *"), you
+ * must explicitly register a handler for "*"; the default handler
+ * will not be used for that case.
  * 
  * To add authentication to some or all paths, create an appropriate
  * #SoupAuthDomain (qv), and add it to the server via
- * soup_server_add_auth_domain.
+ * soup_server_add_auth_domain(). (As with handlers, you must
+ * explicitly add "*" to an auth domain if you want it to be covered.)
  * 
  * Additional processing options are available via #SoupServer's
  * signals; Connect to #SoupServer::request-started to be notified
@@ -739,6 +744,8 @@ soup_server_get_handler (SoupServer *server, const char *path)
 		hand = soup_path_map_lookup (priv->handlers, path);
 		if (hand)
 			return hand;
+		if (!strcmp (path, "*"))
+			return NULL;
 	}
 	return priv->default_handler;
 }
@@ -1199,6 +1206,12 @@ soup_client_context_get_auth_user (SoupClientContext *client)
  * Adds a handler to @server for requests under @path. See the
  * documentation for #SoupServerCallback for information about
  * how callbacks should behave.
+ *
+ * If @path is %NULL or "/", then this will be the default handler for
+ * all requests that don't have a more specific handler. Note though
+ * that if you want to handle requests to the special "*" URI, you
+ * must explicitly register a handler for "*"; the default handler
+ * will not be used for that case.
  **/
 void
 soup_server_add_handler (SoupServer            *server,
@@ -1214,6 +1227,13 @@ soup_server_add_handler (SoupServer            *server,
 	g_return_if_fail (callback != NULL);
 	priv = SOUP_SERVER_GET_PRIVATE (server);
 
+	/* "" was never documented as meaning the same this as "/",
+	 * but it effectively was. We have to special case it now or
+	 * otherwise it would match "*" too.
+	 */
+	if (path && (!*path || !strcmp (path, "/")))
+		path = NULL;
+
 	hand = g_slice_new0 (SoupServerHandler);
 	hand->path       = g_strdup (path);
 	hand->callback   = callback;
@@ -1250,7 +1270,7 @@ soup_server_remove_handler (SoupServer *server, const char *path)
 	g_return_if_fail (SOUP_IS_SERVER (server));
 	priv = SOUP_SERVER_GET_PRIVATE (server);
 
-	if (!path) {
+	if (!path || !*path || !strcmp (path, "/")) {
 		if (priv->default_handler) {
 			unregister_handler (priv->default_handler);
 			free_handler (priv->default_handler);
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 70415d3..e694477 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -18,6 +18,7 @@
 
 #include "test-utils.h"
 
+SoupServer *server;
 SoupURI *base_uri;
 
 static gboolean
@@ -34,6 +35,16 @@ server_callback (SoupServer *server, SoupMessage *msg,
 {
 	SoupURI *uri = soup_message_get_uri (msg);
 
+	soup_message_headers_append (msg->response_headers,
+				     "X-Handled-By", "server_callback");
+
+	if (!strcmp (path, "*")) {
+		debug_printf (1, "    default server_callback got request for '*'!\n");
+		errors++;
+		soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+		return;
+	}
+
 	if (msg->method != SOUP_METHOD_GET) {
 		soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
 		return;
@@ -62,6 +73,29 @@ server_callback (SoupServer *server, SoupMessage *msg,
 	}
 }
 
+static void
+server_star_callback (SoupServer *server, SoupMessage *msg,
+		      const char *path, GHashTable *query,
+		      SoupClientContext *context, gpointer data)
+{
+	soup_message_headers_append (msg->response_headers,
+				     "X-Handled-By", "star_callback");
+
+	if (strcmp (path, "*") != 0) {
+		debug_printf (1, "    server_star_callback got request for '%s'!\n", path);
+		errors++;
+		soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+		return;
+	}
+
+	if (msg->method != SOUP_METHOD_OPTIONS) {
+		soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+		return;
+	}
+
+	soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
 /* Host header handling: client must be able to override the default
  * value, server must be able to recognize different Host values.
  * #539803.
@@ -283,10 +317,68 @@ do_msg_reuse_test (void)
 	g_free (signal_ids);
 }
 
+static void
+do_star_test (void)
+{
+	SoupSession *session;
+	SoupMessage *msg;
+	SoupURI *star_uri;
+	const char *handled_by;
+
+	debug_printf (1, "\nOPTIONS *\n");
+
+	session = soup_test_session_new (SOUP_TYPE_SESSION_SYNC, NULL);
+	star_uri = soup_uri_copy (base_uri);
+	soup_uri_set_path (star_uri, "*");
+
+	debug_printf (1, "  Testing with no handler\n");
+	msg = soup_message_new_from_uri ("OPTIONS", star_uri);
+	soup_session_send_message (session, msg);
+
+	if (msg->status_code != SOUP_STATUS_NOT_FOUND) {
+		debug_printf (1, "    Unexpected response: %d %s\n",
+			      msg->status_code, msg->reason_phrase);
+		errors++;
+	}
+	handled_by = soup_message_headers_get_one (msg->response_headers,
+						   "X-Handled-By");
+	if (handled_by) {
+		/* Should have been rejected by SoupServer directly */
+		debug_printf (1, "    Message reached handler '%s'\n",
+			      handled_by);
+		errors++;
+	}
+	g_object_unref (msg);
+
+	soup_server_add_handler (server, "*", server_star_callback, NULL, NULL);
+
+	debug_printf (1, "  Testing with handler\n");
+	msg = soup_message_new_from_uri ("OPTIONS", star_uri);
+	soup_session_send_message (session, msg);
+
+	if (msg->status_code != SOUP_STATUS_OK) {
+		debug_printf (1, "    Unexpected response: %d %s\n",
+			      msg->status_code, msg->reason_phrase);
+		errors++;
+	}
+	handled_by = soup_message_headers_get_one (msg->response_headers,
+						   "X-Handled-By");
+	if (!handled_by) {
+		debug_printf (1, "    Message did not reach handler!\n");
+		errors++;
+	} else if (strcmp (handled_by, "star_callback") != 0) {
+		debug_printf (1, "    Message reached incorrect handler '%s'\n",
+			      handled_by);
+		errors++;
+	}
+	g_object_unref (msg);
+
+	soup_test_session_abort_unref (session);
+}
+
 int
 main (int argc, char **argv)
 {
-	SoupServer *server;
 	SoupAuthDomain *auth_domain;
 
 	test_init (argc, argv, NULL);
@@ -307,6 +399,7 @@ main (int argc, char **argv)
 	do_host_test ();
 	do_callback_unref_test ();
 	do_msg_reuse_test ();
+	do_star_test ();
 
 	soup_uri_free (base_uri);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]