[libsoup] SoupSession: add some API for handling redirections



commit 43043425599e98cae85877aa53baf0115e342f5c
Author: Dan Winship <danw gnome org>
Date:   Wed Mar 12 21:39:14 2008 -0400

    SoupSession: add some API for handling redirections

 docs/reference/libsoup-2.4-sections.txt |    4 +
 libsoup/soup-message.c                  |   35 ++++++
 libsoup/soup-message.h                  |    4 +
 libsoup/soup-session.c                  |  199 +++++++++++++++++++++----------
 libsoup/soup-session.h                  |    5 +
 tests/chunk-test.c                      |    4 +-
 tests/forms-test.c                      |    4 +-
 tests/misc-test.c                       |    8 +-
 tests/redirect-test.c                   |   10 +--
 tests/simple-httpd.c                    |   11 +-
 10 files changed, 190 insertions(+), 94 deletions(-)
---
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index 7d462c8..c647eb0 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -18,6 +18,7 @@ soup_message_get_address
 <SUBSECTION>
 soup_message_set_status
 soup_message_set_status_full
+soup_message_set_redirect
 soup_message_is_keepalive
 soup_message_get_https_status
 <SUBSECTION>
@@ -391,6 +392,9 @@ soup_session_cancel_message
 soup_session_prepare_for_uri
 soup_session_abort
 <SUBSECTION>
+soup_session_would_redirect
+soup_session_redirect_message
+<SUBSECTION>
 soup_session_pause_message
 soup_session_unpause_message
 <SUBSECTION>
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 82f39de..dea3748 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -1934,3 +1934,38 @@ soup_message_get_https_status (SoupMessage           *msg,
 		*errors = priv->tls_errors;
 	return priv->tls_certificate != NULL;
 }
+
+/**
+ * soup_message_set_redirect:
+ * @msg: a #SoupMessage
+ * @status_code: a 3xx status code
+ * @redirect_uri: the URI to redirect @msg to
+ *
+ * Sets @msg's status_code to @status_code and adds a Location header
+ * pointing to @redirect_uri. Use this from a #SoupServer when you
+ * want to redirect the client to another URI.
+ *
+ * @redirect_uri can be a relative URI, in which case it is
+ * interpreted relative to @msg's current URI. In particular, if
+ * @redirect_uri is just a path, it will replace the path
+ * <emphasis>and query</emphasis> of @msg's URI.
+ *
+ * Since: 2.38
+ */
+void
+soup_message_set_redirect (SoupMessage *msg, guint status_code,
+			   const char *redirect_uri)
+{
+	SoupURI *location;
+	char *location_str;
+
+	location = soup_uri_new_with_base (soup_message_get_uri (msg), redirect_uri);
+	g_return_if_fail (location != NULL);
+
+	soup_message_set_status (msg, status_code);
+	location_str = soup_uri_to_string (location, FALSE);
+	soup_message_headers_replace (msg->response_headers, "Location",
+				      location_str);
+	g_free (location_str);
+	soup_uri_free (location);
+}
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 8505f00..f1a8c6d 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -155,6 +155,10 @@ void           soup_message_set_status_full     (SoupMessage       *msg,
 						 guint              status_code, 
 						 const char        *reason_phrase);
 
+void           soup_message_set_redirect        (SoupMessage       *msg,
+						 guint              status_code,
+						 const char        *redirect_uri);
+
 /* I/O */
 typedef SoupBuffer * (*SoupChunkAllocator)      (SoupMessage       *msg,
 						 gsize              max_len,
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 0015dba..7f5ea0e 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -1392,32 +1392,126 @@ auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
 				     method == SOUP_METHOD_OPTIONS || \
 				     method == SOUP_METHOD_PROPFIND)
 
-static void
-redirect_handler (SoupMessage *msg, gpointer user_data)
+#define SOUP_SESSION_WOULD_REDIRECT_AS_GET(session, msg) \
+	((msg)->status_code == SOUP_STATUS_SEE_OTHER || \
+	 ((msg)->status_code == SOUP_STATUS_FOUND && \
+	  !SOUP_METHOD_IS_SAFE ((msg)->method)) || \
+	 ((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY && \
+	  (msg)->method == SOUP_METHOD_POST))
+
+#define SOUP_SESSION_WOULD_REDIRECT_AS_SAFE(session, msg) \
+	(((msg)->status_code == SOUP_STATUS_MOVED_PERMANENTLY || \
+	  (msg)->status_code == SOUP_STATUS_TEMPORARY_REDIRECT || \
+	  (msg)->status_code == SOUP_STATUS_FOUND) && \
+	 SOUP_METHOD_IS_SAFE ((msg)->method))
+
+static inline SoupURI *
+redirection_uri (SoupMessage *msg)
 {
-	SoupMessageQueueItem *item = user_data;
-	SoupSession *session = item->session;
-	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
 	const char *new_loc;
 	SoupURI *new_uri;
 
 	new_loc = soup_message_headers_get_one (msg->response_headers,
 						"Location");
-	g_return_if_fail (new_loc != NULL);
+	if (!new_loc)
+		return NULL;
+	new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
+	if (!new_uri || !new_uri->host) {
+		if (new_uri)
+			soup_uri_free (new_uri);
+		return NULL;
+	}
+
+	return new_uri;
+}
+
+/**
+ * soup_session_would_redirect:
+ * @session: a #SoupSession
+ * @msg: a #SoupMessage that has response headers
+ *
+ * Checks if @msg contains a response that would cause @session to
+ * redirect it to a new URL (ignoring @msg's %SOUP_MESSAGE_NO_REDIRECT
+ * flag, and the number of times it has already been redirected).
+ *
+ * Return value: whether @msg would be redirected
+ *
+ * Since: 2.38
+ */
+gboolean
+soup_session_would_redirect (SoupSession *session, SoupMessage *msg)
+{
+	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+	SoupURI *new_uri;
+
+	/* It must have an appropriate status code and method */
+	if (!SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg) &&
+	    !SOUP_SESSION_WOULD_REDIRECT_AS_SAFE (session, msg))
+		return FALSE;
+
+	/* and a Location header that parses to an http URI */
+	if (!soup_message_headers_get_one (msg->response_headers, "Location"))
+		return FALSE;
+	new_uri = redirection_uri (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_free (new_uri);
+		return FALSE;
+	}
+
+	soup_uri_free (new_uri);
+	return TRUE;
+}
+
+/**
+ * soup_session_redirect_message:
+ * @session: the session
+ * @msg: a #SoupMessage that has received a 3xx response
+ *
+ * Updates @msg's URI according to its status code and "Location"
+ * header, and requeues it on @session. Use this when you have set
+ * %SOUP_MESSAGE_NO_REDIRECT on a message, but have decided to allow a
+ * particular redirection to occur, or if you want to allow a
+ * redirection that #SoupSession will not perform automatically (eg,
+ * redirecting a non-safe method such as DELETE).
+ *
+ * If @msg's status code indicates that it should be retried as a GET
+ * request, then @msg will be modified accordingly.
+ *
+ * If @msg has already been redirected too many times, this will
+ * cause it to fail with %SOUP_STATUS_TOO_MANY_REDIRECTS.
+ *
+ * Return value: %TRUE if a redirection was applied, %FALSE if not
+ * (eg, because there was no Location header, or it could not be
+ * parsed).
+ *
+ * Since: 2.38
+ */
+gboolean
+soup_session_redirect_message (SoupSession *session, SoupMessage *msg)
+{
+	SoupMessageQueueItem *item;
+	SoupURI *new_uri;
+
+	new_uri = redirection_uri (msg);
+	if (!new_uri)
+		return FALSE;
 
+	item = soup_message_queue_lookup (soup_session_get_queue (session), msg);
+	if (!item)
+		return FALSE;
 	if (item->redirection_count >= SOUP_SESSION_MAX_REDIRECTION_COUNT) {
 		soup_session_cancel_message (session, msg, SOUP_STATUS_TOO_MANY_REDIRECTS);
-		return;
+		soup_message_queue_item_unref (item);
+		return FALSE;
 	}
 	item->redirection_count++;
+	soup_message_queue_item_unref (item);
 
-	if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
-	    (msg->status_code == SOUP_STATUS_FOUND &&
-	     !SOUP_METHOD_IS_SAFE (msg->method)) ||
-	    (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY &&
-	     msg->method == SOUP_METHOD_POST)) {
+	if (SOUP_SESSION_WOULD_REDIRECT_AS_GET (session, msg)) {
 		if (msg->method != SOUP_METHOD_HEAD) {
-			/* Redirect using a GET */
 			g_object_set (msg,
 				      SOUP_MESSAGE_METHOD, SOUP_METHOD_GET,
 				      NULL);
@@ -1426,66 +1520,39 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
 					  SOUP_MEMORY_STATIC, NULL, 0);
 		soup_message_headers_set_encoding (msg->request_headers,
 						   SOUP_ENCODING_NONE);
-	} else if (msg->status_code == SOUP_STATUS_MOVED_PERMANENTLY ||
-		   msg->status_code == SOUP_STATUS_TEMPORARY_REDIRECT ||
-		   msg->status_code == SOUP_STATUS_FOUND) {
-		/* Don't redirect non-safe methods */
-		if (!SOUP_METHOD_IS_SAFE (msg->method))
-			return;
-	} else {
-		/* Three possibilities:
-		 *
-		 *   1) This was a non-3xx response that happened to
-		 *      have a "Location" header
-		 *   2) It's a non-redirecty 3xx response (300, 304,
-		 *      305, 306)
-		 *   3) It's some newly-defined 3xx response (308+)
-		 *
-		 * We ignore all of these cases. In the first two,
-		 * redirecting would be explicitly wrong, and in the
-		 * last case, we have no clue if the 3xx response is
-		 * supposed to be redirecty or non-redirecty. Plus,
-		 * 2616 says unrecognized status codes should be
-		 * treated as the equivalent to the x00 code, and we
-		 * don't redirect on 300, so therefore we shouldn't
-		 * redirect on 308+ either.
-		 */
-		return;
 	}
 
-	/* Location is supposed to be an absolute URI, but some sites
-	 * are lame, so we use soup_uri_new_with_base().
-	 */
-	new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
-	if (!new_uri || !new_uri->host) {
-		if (new_uri)
-			soup_uri_free (new_uri);
-		soup_message_set_status_full (msg,
-					      SOUP_STATUS_MALFORMED,
-					      "Invalid Redirect URL");
-		return;
-	}
+	soup_message_set_uri (msg, new_uri);
+	soup_uri_free (new_uri);
 
-	/* If the URI is not "http" or "https" or a recognized alias,
-	 * then we let the redirect response be returned to the caller.
-	 */
-	if (!uri_is_http (priv, new_uri) && !uri_is_https (priv, new_uri)) {
-		soup_uri_free (new_uri);
-		return;
-	}
+	soup_session_requeue_message (session, msg);
+	return TRUE;
+}
 
-	if (!new_uri->host) {
-		soup_uri_free (new_uri);
-		soup_message_set_status_full (msg,
-					      SOUP_STATUS_MALFORMED,
-					      "Invalid Redirect URL");
+static void
+redirect_handler (SoupMessage *msg, gpointer user_data)
+{
+	SoupMessageQueueItem *item = user_data;
+	SoupSession *session = item->session;
+
+	if (!soup_session_would_redirect (session, msg)) {
+		SoupURI *new_uri = redirection_uri (msg);
+		gboolean invalid = !new_uri || !new_uri->host;
+
+		if (new_uri)
+			soup_uri_free (new_uri);
+		if (invalid) {
+			/* Really we should just leave the status as-is,
+			 * but that would be an API break.
+			 */
+			soup_message_set_status_full (msg,
+						      SOUP_STATUS_MALFORMED,
+						      "Invalid Redirect URL");
+		}
 		return;
 	}
 
-	soup_message_set_uri (msg, new_uri);
-	soup_uri_free (new_uri);
-
-	soup_session_requeue_message (session, msg);
+	soup_session_redirect_message (session, msg);
 }
 
 void
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 6afbbd1..349cfdb 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -104,6 +104,11 @@ void            soup_session_abort            (SoupSession           *session);
 void            soup_session_prepare_for_uri  (SoupSession           *session,
 					       SoupURI               *uri);
 
+gboolean        soup_session_would_redirect   (SoupSession           *session,
+					       SoupMessage           *msg);
+gboolean        soup_session_redirect_message (SoupSession           *session,
+					       SoupMessage           *msg);
+
 void                soup_session_add_feature            (SoupSession        *session,
 							 SoupSessionFeature *feature);
 void                soup_session_add_feature_by_type    (SoupSession        *session,
diff --git a/tests/chunk-test.c b/tests/chunk-test.c
index 435f7fa..c6b4a42 100644
--- a/tests/chunk-test.c
+++ b/tests/chunk-test.c
@@ -434,9 +434,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
 	char *md5;
 
 	if (g_str_has_prefix (path, "/redirect")) {
-		soup_message_set_status (msg, SOUP_STATUS_FOUND);
-		soup_message_headers_replace (msg->response_headers,
-					      "Location", "/");
+		soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
 		return;
 	}
 
diff --git a/tests/forms-test.c b/tests/forms-test.c
index 4c2846e..fbbd97c 100644
--- a/tests/forms-test.c
+++ b/tests/forms-test.c
@@ -395,9 +395,7 @@ md5_post_callback (SoupServer *server, SoupMessage *msg,
 					NULL);
 	redirect_uri = soup_uri_to_string (uri, FALSE);
 
-	soup_message_set_status (msg, SOUP_STATUS_SEE_OTHER);
-	soup_message_headers_replace (msg->response_headers, "Location",
-				      redirect_uri);
+	soup_message_set_redirect (msg, SOUP_STATUS_SEE_OTHER, redirect_uri);
 
 	g_free (redirect_uri);
 	soup_uri_free (uri);
diff --git a/tests/misc-test.c b/tests/misc-test.c
index f5ecb57..907e338 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -141,13 +141,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
 	}
 
 	if (!strcmp (path, "/redirect")) {
-		soup_message_set_status (msg, SOUP_STATUS_FOUND);
-		soup_message_headers_append (msg->response_headers,
-					     /* Kids: don't try this at home!
-					      * RFC2616 says to use an
-					      * absolute URI!
-					      */
-					     "Location", "/");
+		soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
 		return;
 	}
 
diff --git a/tests/redirect-test.c b/tests/redirect-test.c
index c953f03..aa1fb54 100644
--- a/tests/redirect-test.c
+++ b/tests/redirect-test.c
@@ -430,14 +430,8 @@ server_callback (SoupServer *server, SoupMessage *msg,
 	if (*remainder == '/')
 		soup_message_set_http_version (msg, SOUP_HTTP_1_0);
 
-	soup_message_set_status (msg, status_code);
-	if (*remainder) {
-		soup_message_headers_replace (msg->response_headers,
-					      "Location", remainder);
-	} else {
-		soup_message_headers_replace (msg->response_headers,
-					      "Location", "/");
-	}
+	soup_message_set_redirect (msg, status_code,
+				   *remainder ? remainder : "/");
 }
 
 static void
diff --git a/tests/simple-httpd.c b/tests/simple-httpd.c
index c776d36..535de4b 100644
--- a/tests/simple-httpd.c
+++ b/tests/simple-httpd.c
@@ -112,15 +112,12 @@ do_get (SoupServer *server, SoupMessage *msg, const char *path)
 
 		slash = strrchr (path, '/');
 		if (!slash || slash[1]) {
-			char *uri, *redir_uri;
+			char *redir_uri;
 
-			uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
-			redir_uri = g_strdup_printf ("%s/", uri);
-			soup_message_headers_append (msg->response_headers,
-						     "Location", redir_uri);
-			soup_message_set_status (msg, SOUP_STATUS_MOVED_PERMANENTLY);
+			redir_uri = g_strdup_printf ("%s/", soup_message_get_uri (msg)->path);
+			soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY,
+						   redir_uri);
 			g_free (redir_uri);
-			g_free (uri);
 			return;
 		}
 



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