[libsoup] SoupSession: add some API for handling redirections
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup] SoupSession: add some API for handling redirections
- Date: Sat, 5 Nov 2011 13:28:38 +0000 (UTC)
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]