Title: [139239] trunk
Revision
139239
Author
[email protected]
Date
2013-01-09 14:03:48 -0800 (Wed, 09 Jan 2013)

Log Message

[Soup] Handle redirection inside WebKit
https://bugs.webkit.org/show_bug.cgi?id=61122
https://bugs.webkit.org/show_bug.cgi?id=88961

Reviewed by Martin Robinson.

Source/WebCore:

Rather than using libsoup's built-in redirection handling (which
doesn't do everything exactly the way WebKit wants, and can't
handle redirects to non-http URIs anyway), process redirections
ourselves.

No new tests; unskips a few existing tests.

* platform/network/ResourceHandleInternal.h:
(WebCore::ResourceHandleInternal::ResourceHandleInternal):
(ResourceHandleInternal):
* platform/network/soup/ResourceError.h:
(ResourceError):
* platform/network/soup/ResourceErrorSoup.cpp:
(WebCore::ResourceError::transportError):
(WebCore):
(WebCore::ResourceError::httpError):
* platform/network/soup/ResourceHandleSoup.cpp:
(WebCore):
(WebCore::gotHeadersCallback):
(WebCore::restartedCallback):
(WebCore::shouldRedirect):
(WebCore::doRedirect):
(WebCore::redirectCloseCallback):
(WebCore::redirectSkipCallback):
(WebCore::cleanupSoupRequestOperation):
(WebCore::sendRequestCallback):
(WebCore::createSoupMessageForHandleAndRequest):
(WebCore::createSoupRequestAndMessageForHandle):
(WebCore::ResourceHandle::start):

LayoutTests:

Unskip some tests.

* platform/efl/TestExpectations:
* platform/gtk/TestExpectations:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (139238 => 139239)


--- trunk/LayoutTests/ChangeLog	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/LayoutTests/ChangeLog	2013-01-09 22:03:48 UTC (rev 139239)
@@ -1,3 +1,16 @@
+2013-01-09  Dan Winship  <[email protected]>
+
+        [Soup] Handle redirection inside WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=61122
+        https://bugs.webkit.org/show_bug.cgi?id=88961
+
+        Reviewed by Martin Robinson.
+
+        Unskip some tests.
+
+        * platform/efl/TestExpectations:
+        * platform/gtk/TestExpectations:
+
 2013-01-09  Florin Malita  <[email protected]>
 
         [Skia] Implement GraphicsContext::addRoundedRectClip() using SkCanvas::clipRRect()

Modified: trunk/LayoutTests/platform/efl/TestExpectations (139238 => 139239)


--- trunk/LayoutTests/platform/efl/TestExpectations	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/LayoutTests/platform/efl/TestExpectations	2013-01-09 22:03:48 UTC (rev 139239)
@@ -96,10 +96,6 @@
 http/tests/media/video-play-stall.html
 http/tests/security/feed-urls-from-remote.html
 
-# Test times out (via GTK+)
-# https://bugs.webkit.org/show_bug.cgi?id=61122
-http/tests/navigation/post-307-response.html
-
 # ------------------------------
 # Tests which need investigation
 # ------------------------------
@@ -1376,9 +1372,6 @@
 # Failing after r119268 on GTK and EFL
 webkit.org/b/88138 http/tests/media/video-buffered.html [ Failure ]
 
-# Failing after r120145 on both GTK and EFL
-webkit.org/b/88961 http/tests/misc/redirect-to-about-blank.html [ Failure ]
-
 # EFL port does not support hyphenation
 webkit.org/b/89830 fast/text/hyphen-min-preferred-width.html [ ImageOnlyFailure ]
 webkit.org/b/89830 fast/text/hyphenate-locale.html [ Skip ]

Modified: trunk/LayoutTests/platform/gtk/TestExpectations (139238 => 139239)


--- trunk/LayoutTests/platform/gtk/TestExpectations	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/LayoutTests/platform/gtk/TestExpectations	2013-01-09 22:03:48 UTC (rev 139239)
@@ -906,7 +906,6 @@
 
 # Needs to make sure the redirect-chain scenario in https://bugs.webkit.org/show_bug.cgi?id=31410 works
 webkit.org/b/35300 http/tests/loading/307-after-303-after-post.html [ Failure ]
-webkit.org/b/35300 http/tests/navigation/post-307-response.html [ Skip ]
 
 # Paged continuousMouseScrollBy() events are not supported in GTK.
 Bug(GTK) fast/events/platform-wheelevent-paging-x-in-non-scrolling-div.html [ Failure ]
@@ -1230,9 +1229,6 @@
 # This test is affecting other tests and seems to be behaving incorrectly itself
 webkit.org/b/86771 fast/dom/gc-10.html [ Skip ]
 
-# Failing after r120145 on both GTK and EFL
-webkit.org/b/88961 http/tests/misc/redirect-to-about-blank.html [ Failure ]
-
 webkit.org/b/86971 svg/hittest/svg-shapes-non-scale-stroke.html [ Failure ]
 
 webkit.org/b/87088 fast/js/names.html [ Failure ]

Modified: trunk/Source/WebCore/ChangeLog (139238 => 139239)


--- trunk/Source/WebCore/ChangeLog	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/Source/WebCore/ChangeLog	2013-01-09 22:03:48 UTC (rev 139239)
@@ -1,3 +1,41 @@
+2013-01-09  Dan Winship  <[email protected]>
+
+        [Soup] Handle redirection inside WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=61122
+        https://bugs.webkit.org/show_bug.cgi?id=88961
+
+        Reviewed by Martin Robinson.
+
+        Rather than using libsoup's built-in redirection handling (which
+        doesn't do everything exactly the way WebKit wants, and can't
+        handle redirects to non-http URIs anyway), process redirections
+        ourselves.
+
+        No new tests; unskips a few existing tests.
+
+        * platform/network/ResourceHandleInternal.h:
+        (WebCore::ResourceHandleInternal::ResourceHandleInternal):
+        (ResourceHandleInternal):
+        * platform/network/soup/ResourceError.h:
+        (ResourceError):
+        * platform/network/soup/ResourceErrorSoup.cpp:
+        (WebCore::ResourceError::transportError):
+        (WebCore):
+        (WebCore::ResourceError::httpError):
+        * platform/network/soup/ResourceHandleSoup.cpp:
+        (WebCore):
+        (WebCore::gotHeadersCallback):
+        (WebCore::restartedCallback):
+        (WebCore::shouldRedirect):
+        (WebCore::doRedirect):
+        (WebCore::redirectCloseCallback):
+        (WebCore::redirectSkipCallback):
+        (WebCore::cleanupSoupRequestOperation):
+        (WebCore::sendRequestCallback):
+        (WebCore::createSoupMessageForHandleAndRequest):
+        (WebCore::createSoupRequestAndMessageForHandle):
+        (WebCore::ResourceHandle::start):
+
 2013-01-09  Florin Malita  <[email protected]>
 
         [Skia] Implement GraphicsContext::addRoundedRectClip() using SkCanvas::clipRRect()

Modified: trunk/Source/WebCore/platform/network/ResourceHandleInternal.h (139238 => 139239)


--- trunk/Source/WebCore/platform/network/ResourceHandleInternal.h	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/Source/WebCore/platform/network/ResourceHandleInternal.h	2013-01-09 22:03:48 UTC (rev 139239)
@@ -115,6 +115,7 @@
             , m_buffer(0)
             , m_bodySize(0)
             , m_bodyDataSent(0)
+            , m_redirectCount(0)
 #endif
 #if PLATFORM(QT)
             , m_job(0)
@@ -203,6 +204,7 @@
         unsigned long m_bodyDataSent;
         RefPtr<NetworkingContext> m_context;
         SoupSession* soupSession();
+        int m_redirectCount;
 #endif
 #if PLATFORM(GTK)
         struct {

Modified: trunk/Source/WebCore/platform/network/soup/ResourceError.h (139238 => 139239)


--- trunk/Source/WebCore/platform/network/soup/ResourceError.h	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/Source/WebCore/platform/network/soup/ResourceError.h	2013-01-09 22:03:48 UTC (rev 139239)
@@ -50,6 +50,7 @@
     }
 
     static ResourceError httpError(SoupMessage*, GError*, SoupRequest*);
+    static ResourceError transportError(SoupRequest*, int statusCode, const String& reasonPhrase);
     static ResourceError genericIOError(GError*, SoupRequest*);
     static ResourceError tlsError(SoupRequest*, unsigned tlsErrors, GTlsCertificate*);
     static ResourceError timeoutError(const String& failingURL);

Modified: trunk/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp (139238 => 139239)


--- trunk/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp	2013-01-09 22:03:48 UTC (rev 139239)
@@ -48,12 +48,19 @@
     return failingURI(soup_request_get_uri(request));
 }
 
+ResourceError ResourceError::transportError(SoupRequest* request, int statusCode, const String& reasonPhrase)
+{
+    return ResourceError(g_quark_to_string(SOUP_HTTP_ERROR), statusCode,
+        failingURI(request), reasonPhrase);
+}
+
 ResourceError ResourceError::httpError(SoupMessage* message, GError* error, SoupRequest* request)
 {
-    if (!message || !SOUP_STATUS_IS_TRANSPORT_ERROR(message->status_code))
+    if (message && SOUP_STATUS_IS_TRANSPORT_ERROR(message->status_code))
+        return transportError(request, message->status_code,
+            String::fromUTF8(message->reason_phrase));
+    else
         return genericIOError(error, request);
-    return ResourceError(g_quark_to_string(SOUP_HTTP_ERROR), message->status_code,
-        failingURI(request), String::fromUTF8(message->reason_phrase));
 }
 
 ResourceError ResourceError::authenticationError(SoupMessage* message)

Modified: trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp (139238 => 139239)


--- trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp	2013-01-09 22:01:09 UTC (rev 139238)
+++ trunk/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp	2013-01-09 22:03:48 UTC (rev 139239)
@@ -222,7 +222,8 @@
     HashSet<String> m_certificates;
 };
 
-static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying);
+static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&, bool isHTTPFamilyRequest);
+static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying = false);
 static void sendRequestCallback(GObject*, GAsyncResult*, gpointer);
 static void readCallback(GObject*, GAsyncResult*, gpointer);
 static void closeCallback(GObject*, GAsyncResult*, gpointer);
@@ -330,8 +331,7 @@
 #endif
 
     // The original response will be needed later to feed to willSendRequest in
-    // restartedCallback() in case we are redirected. For this reason, so we store it
-    // here.
+    // doRedirect() in case we are redirected. For this reason, we store it here.
     ResourceResponse response;
     response.updateFromSoupMessage(message);
     d->m_response = response;
@@ -374,7 +374,7 @@
 }
 
 // Called each time the message is going to be sent again except the first time.
-// It's used mostly to let webkit know about redirects.
+// This happens when libsoup handles HTTP authentication.
 static void restartedCallback(SoupMessage* message, gpointer data)
 {
     ResourceHandle* handle = static_cast<ResourceHandle*>(data);
@@ -384,64 +384,144 @@
     if (d->m_cancelled)
         return;
 
+#if ENABLE(WEB_TIMING)
     ResourceResponse& redirectResponse = d->m_response;
-#if ENABLE(WEB_TIMING)
     redirectResponse.setResourceLoadTiming(ResourceLoadTiming::create());
     redirectResponse.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime();
 #endif
+}
 
-    // WebCore only expects us to call willSendRequest when we are redirecting. soup
-    // fires this signal also when it's handling authentication challenges, so in that
-    // case we should not willSendRequest.
-    if (isAuthenticationFailureStatusCode(redirectResponse.httpStatusCode()))
+static bool shouldRedirect(ResourceHandle* handle)
+{
+    ResourceHandleInternal* d = handle->getInternal();
+    SoupMessage* message = d->m_soupMessage.get();
+
+    // Some 3xx status codes aren't actually redirects.
+    if (message->status_code == 300 || message->status_code == 304 || message->status_code == 305 || message->status_code == 306)
+        return false;
+
+    if (!soup_message_headers_get_one(message->response_headers, "Location"))
+        return false;
+
+    return true;
+}
+
+static bool shouldRedirectAsGET(SoupMessage* message, KURL& newURL, bool crossOrigin)
+{
+    if (message->method == SOUP_METHOD_GET)
+        return false;
+
+    if (!newURL.protocolIsInHTTPFamily())
+        return true;
+
+    switch (message->status_code) {
+    case SOUP_STATUS_SEE_OTHER:
+        return true;
+    case SOUP_STATUS_FOUND:
+    case SOUP_STATUS_MOVED_PERMANENTLY:
+        if (message->method == SOUP_METHOD_POST)
+            return true;
+        break;
+    }
+
+    if (crossOrigin && message->method == SOUP_METHOD_DELETE)
+        return true;
+
+    return false;
+}
+
+static void doRedirect(ResourceHandle* handle)
+{
+    ResourceHandleInternal* d = handle->getInternal();
+    static const int maxRedirects = 20;
+
+    if (d->m_redirectCount++ > maxRedirects) {
+        d->client()->didFail(handle, ResourceError::transportError(d->m_soupRequest.get(), SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects"));
+        cleanupSoupRequestOperation(handle);
         return;
+    }
 
     ResourceRequest request = handle->firstRequest();
-    request.setURL(KURL(handle->firstRequest().url(), soupURIToKURL(soup_message_get_uri(message))));
-    request.setHTTPMethod(message->method);
+    SoupMessage* message = d->m_soupMessage.get();
+    const char* location = soup_message_headers_get_one(message->response_headers, "Location");
+    KURL newURL = KURL(soupURIToKURL(soup_message_get_uri(message)), location);
+    bool crossOrigin = !protocolHostAndPortAreEqual(request.url(), newURL);
+    request.setURL(newURL);
 
+    if (shouldRedirectAsGET(message, newURL, crossOrigin)) {
+        request.setHTTPMethod("GET");
+        request.setHTTPBody(0);
+        request.clearHTTPContentType();
+    }
+
     // Should not set Referer after a redirect from a secure resource to non-secure one.
-    if (!request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https")) {
+    if (!newURL.protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
         request.clearHTTPReferrer();
-        soup_message_headers_remove(message->request_headers, "Referer");
-    }
 
-    const KURL& url = ""
-    d->m_user = url.user();
-    d->m_pass = url.pass();
+    d->m_user = newURL.user();
+    d->m_pass = newURL.pass();
     request.removeCredentials();
 
-    if (!protocolHostAndPortAreEqual(request.url(), redirectResponse.url())) {
+    if (crossOrigin) {
         // If the network layer carries over authentication headers from the original request
         // in a cross-origin redirect, we want to clear those headers here. 
         request.clearHTTPAuthorization();
-        soup_message_headers_remove(message->request_headers, "Authorization");
 
         // TODO: We are losing any username and password specified in the redirect URL, as this is the 
         // same behavior as the CFNet port. We should investigate if this is really what we want.
     } else
         applyAuthenticationToRequest(handle, request, true);
 
-    // Per-request authentication is handled via the URI-embedded username/password.
-    GOwnPtr<SoupURI> newSoupURI(request.soupURI());
-    soup_message_set_uri(message, newSoupURI.get());
+    cleanupSoupRequestOperation(handle);
+    if (!createSoupRequestAndMessageForHandle(handle, request, true)) {
+        d->client()->cannotShowURL(handle);
+        return;
+    }
 
     // If we sent credentials with this request's URL, we don't want the response to carry them to
     // the WebKit layer. They were only placed in the URL for the benefit of libsoup.
     request.removeCredentials();
 
-    if (d->client())
-        d->client()->willSendRequest(handle, request, redirectResponse);
+    d->client()->willSendRequest(handle, request, d->m_response);
+    handle->sendPendingRequest();
+}
 
-    if (d->m_cancelled)
+static void redirectCloseCallback(GObject*, GAsyncResult* res, gpointer data)
+{
+    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
+    ResourceHandleInternal* d = handle->getInternal();
+
+    g_input_stream_close_finish(d->m_inputStream.get(), res, 0);
+    doRedirect(handle.get());
+}
+
+static void redirectSkipCallback(GObject*, GAsyncResult* asyncResult, gpointer data)
+{
+    RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data);
+
+    ResourceHandleInternal* d = handle->getInternal();
+    ResourceHandleClient* client = handle->client();
+
+    if (d->m_cancelled || !client) {
+        cleanupSoupRequestOperation(handle.get());
         return;
+    }
 
-    // Update the first party in case the base URL changed with the redirect
-    String firstPartyString = request.firstPartyForCookies().string();
-    if (!firstPartyString.isEmpty()) {
-        GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data()));
-        soup_message_set_first_party(d->m_soupMessage.get(), firstParty.get());
+    GOwnPtr<GError> error;
+    gssize bytesSkipped = g_input_stream_skip_finish(d->m_inputStream.get(), asyncResult, &error.outPtr());
+    if (error) {
+        client->didFail(handle.get(), ResourceError::genericIOError(error.get(), d->m_soupRequest.get()));
+        cleanupSoupRequestOperation(handle.get());
+        return;
     }
+
+    if (bytesSkipped > 0) {
+        g_input_stream_skip_async(d->m_inputStream.get(), G_MAXSSIZE, G_PRIORITY_DEFAULT,
+            d->m_cancellable.get(), redirectSkipCallback, handle.get());
+        return;
+    }
+
+    g_input_stream_close_async(d->m_inputStream.get(), G_PRIORITY_DEFAULT, 0, redirectCloseCallback, handle.get());
 }
 
 static void wroteBodyDataCallback(SoupMessage*, SoupBuffer* buffer, gpointer data)
@@ -463,7 +543,7 @@
     client->didSendData(handle.get(), internal->m_bodyDataSent, internal->m_bodySize);
 }
 
-static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying = false)
+static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying)
 {
     ResourceHandleInternal* d = handle->getInternal();
 
@@ -583,9 +663,14 @@
         return;
     }
 
-    d->m_buffer = static_cast<char*>(g_slice_alloc(READ_BUFFER_SIZE));
+    if (soupMessage) {
+        if (SOUP_STATUS_IS_REDIRECTION(soupMessage->status_code) && shouldRedirect(handle.get())) {
+            d->m_inputStream = inputStream;
+            g_input_stream_skip_async(d->m_inputStream.get(), G_MAXSSIZE, G_PRIORITY_DEFAULT,
+                d->m_cancellable.get(), redirectSkipCallback, handle.get());
+            return;
+        }
 
-    if (soupMessage) {
         if (handle->shouldContentSniff() && soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED) {
             const char* sniffedType = soup_request_get_content_type(d->m_soupRequest.get());
             d->m_response.setSniffedContentType(sniffedType);
@@ -612,6 +697,8 @@
         return;
     }
 
+    d->m_buffer = static_cast<char*>(g_slice_alloc(READ_BUFFER_SIZE));
+
     if (soupMessage && d->m_response.isMultipart()) {
         d->m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(soupMessage, inputStream.get()));
         soup_multipart_input_stream_next_part_async(d->m_multipartInputStream.get(), G_PRIORITY_DEFAULT,
@@ -879,6 +966,8 @@
     g_signal_connect(d->m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), handle);
     g_signal_connect(d->m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle);
 
+    soup_message_set_flags(d->m_soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(d->m_soupMessage.get()) | SOUP_MESSAGE_NO_REDIRECT));
+
 #if ENABLE(WEB_TIMING)
     d->m_response.setResourceLoadTiming(ResourceLoadTiming::create());
     g_signal_connect(d->m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), handle);
@@ -888,13 +977,12 @@
     return true;
 }
 
-static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, bool isHTTPFamilyRequest)
+static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request, bool isHTTPFamilyRequest)
 {
     ResourceHandleInternal* d = handle->getInternal();
     SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(d->soupSession(), SOUP_TYPE_REQUESTER));
 
     GOwnPtr<GError> error;
-    ResourceRequest& request = handle->firstRequest();
 
     GOwnPtr<SoupURI> soupURI(request.soupURI());
     d->m_soupRequest = adoptGRef(soup_requester_request_uri(requester, soupURI.get(), &error.outPtr()));
@@ -937,7 +1025,7 @@
 
     applyAuthenticationToRequest(this, firstRequest(), false);
 
-    if (!createSoupRequestAndMessageForHandle(this, isHTTPFamilyRequest)) {
+    if (!createSoupRequestAndMessageForHandle(this, request, isHTTPFamilyRequest)) {
         this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately
         return true;
     }
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to