Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: net/url_request/url_request_unittest.cc

Issue 154473002: Support redirectUrl at onHeadersReceived in WebRequest / DWR API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Pass allowed_unsafe_redirect_url via delegate parameter instead of HttpResponseHeaders + fragment t… Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/url_request/url_request_unittest.cc
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 63e7c757244ddf6c31f28adcd369cd86c2a0ef48..1bd981e37e6713b0258f3a752ce35746b7f36769 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -338,6 +338,9 @@ class BlockingNetworkDelegate : public TestNetworkDelegate {
void set_redirect_url(const GURL& url) {
redirect_url_ = url;
}
+ void set_redirect_url_on_headers_received(const GURL& url) {
+ redirect_url_on_headers_received_ = url;
+ }
void set_block_on(int block_on) {
block_on_ = block_on;
@@ -367,7 +370,8 @@ class BlockingNetworkDelegate : public TestNetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE;
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE;
virtual NetworkDelegate::AuthRequiredResponse OnAuthRequired(
URLRequest* request,
@@ -390,7 +394,8 @@ class BlockingNetworkDelegate : public TestNetworkDelegate {
int retval_; // To be returned in non-auth stages.
AuthRequiredResponse auth_retval_;
- GURL redirect_url_; // Used if non-empty.
+ GURL redirect_url_; // Used if non-empty during OnBeforeURLRequest.
+ GURL redirect_url_on_headers_received_; // Used if non-empty.
int block_on_; // Bit mask: in which stages to block.
// |auth_credentials_| will be copied to |*target_auth_credential_| on
@@ -482,10 +487,26 @@ int BlockingNetworkDelegate::OnHeadersReceived(
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
TestNetworkDelegate::OnHeadersReceived(
request, callback, original_response_headers,
- override_response_headers);
+ override_response_headers, allowed_unsafe_redirect_url);
+
+ if (!redirect_url_on_headers_received_.is_empty()) {
+ if (override_response_headers->get() == NULL) {
+ *override_response_headers = new net::HttpResponseHeaders(
+ original_response_headers->raw_headers());
+ }
+ (*override_response_headers)->ReplaceStatusLine(
+ "HTTP/1.1 307 Temporary Redirect");
+ (*override_response_headers)->RemoveHeader("location");
+ (*override_response_headers)->AddHeader(
+ "Location: " + redirect_url_on_headers_received_.spec());
+ *allowed_unsafe_redirect_url = redirect_url_on_headers_received_;
+ // Redirect just once.
+ redirect_url_on_headers_received_ = GURL();
+ }
return MaybeBlockStage(ON_HEADERS_RECEIVED, callback);
}
@@ -2416,8 +2437,8 @@ class FixedDateNetworkDelegate : public TestNetworkDelegate {
net::URLRequest* request,
const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
- scoped_refptr<net::HttpResponseHeaders>* override_response_headers)
- OVERRIDE;
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE;
private:
std::string fixed_date_;
@@ -2429,7 +2450,8 @@ int FixedDateNetworkDelegate::OnHeadersReceived(
net::URLRequest* request,
const net::CompletionCallback& callback,
const net::HttpResponseHeaders* original_response_headers,
- scoped_refptr<net::HttpResponseHeaders>* override_response_headers) {
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
net::HttpResponseHeaders* new_response_headers =
new net::HttpResponseHeaders(original_response_headers->raw_headers());
@@ -2440,7 +2462,8 @@ int FixedDateNetworkDelegate::OnHeadersReceived(
return TestNetworkDelegate::OnHeadersReceived(request,
callback,
original_response_headers,
- override_response_headers);
+ override_response_headers,
+ allowed_unsafe_redirect_url);
}
// Test that cookie expiration times are adjusted for server/client clock
@@ -2696,6 +2719,196 @@ class URLRequestTestHTTP : public URLRequestTest {
LocalHttpTestServer test_server_;
};
+namespace {
+
+// RedirectOnHeadersReceivedNetworkDelegate redirects to |redirect_url|,
+// optionally with an URL that has been marked safe for redirection.
+class RedirectOnHeadersReceivedNetworkDelegate : public TestNetworkDelegate {
+ public:
+ explicit RedirectOnHeadersReceivedNetworkDelegate(const GURL& redirect_url)
+ : redirect_url_(redirect_url) {}
+ virtual ~RedirectOnHeadersReceivedNetworkDelegate() {}
+
+ // net::NetworkDelegate implementation
+ virtual int OnHeadersReceived(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ const net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE;
+
+ void set_url_marked_safe_for_redirect(const GURL& safe_url) {
+ safe_url_ = safe_url;
+ }
+
+ private:
+ GURL redirect_url_;
+ GURL safe_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(RedirectOnHeadersReceivedNetworkDelegate);
+};
+
+int RedirectOnHeadersReceivedNetworkDelegate::OnHeadersReceived(
+ net::URLRequest* request,
+ const net::CompletionCallback& callback,
+ const net::HttpResponseHeaders* original_response_headers,
+ scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) {
+
+ if (!redirect_url_.is_empty()) {
+ net::HttpResponseHeaders* new_response_headers =
+ new net::HttpResponseHeaders(original_response_headers->raw_headers());
+
+ new_response_headers->ReplaceStatusLine("HTTP/1.1 302 Found");
+ new_response_headers->RemoveHeader("Location");
+ new_response_headers->AddHeader("Location: " + redirect_url_.spec());
+
+ *override_response_headers = new_response_headers;
+ *allowed_unsafe_redirect_url = safe_url_;
+ redirect_url_ = GURL();
+ }
+ return TestNetworkDelegate::OnHeadersReceived(request,
+ callback,
+ original_response_headers,
+ override_response_headers,
+ allowed_unsafe_redirect_url);
+}
+
+} // namespace
+
+// Tests that the allowed redirect URL is preserved when the Location header
+// has been overwritten.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectToWhitelistedUnsafeURL) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL unsafe_url("data:text/html,this-is-considered-an-unsafe-url");
+ RedirectOnHeadersReceivedNetworkDelegate network_delegate(unsafe_url);
+ network_delegate.set_url_marked_safe_for_redirect(unsafe_url);
+ default_context_.set_network_delegate(&network_delegate);
+ TestDelegate d;
+ {
+ URLRequest r(test_server_.GetURL("whatever"),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(0, r.status().error());
+ EXPECT_EQ(unsafe_url, r.url());
+ }
+}
+
+// Tests that a redirect to a different unsafe URL is blocked, even after adding
+// some other URL to the whitelist.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectToDifferentUnsafeURL) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL unsafe_url("data:text/html,something");
+ GURL different_unsafe_url("data:text/html,something-else");
+ RedirectOnHeadersReceivedNetworkDelegate network_delegate(unsafe_url);
+ network_delegate.set_url_marked_safe_for_redirect(different_unsafe_url);
+ default_context_.set_network_delegate(&network_delegate);
+ TestDelegate d;
+ {
+ URLRequest r(test_server_.GetURL("whatever"),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::FAILED, r.status().status());
+ EXPECT_EQ(ERR_UNSAFE_REDIRECT, r.status().error());
+ }
+}
+
+// Tests that a redirect to a safe URL is allowed, regardless of whether an
+// unsafe URL was whitelisted.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectToSafeURL) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL safe_url(test_server_.GetURL("echoheader?"));
+ GURL unrelated_unsafe_url("data:text/html,does-not-match-safe-url");
+ RedirectOnHeadersReceivedNetworkDelegate network_delegate(safe_url);
+ network_delegate.set_url_marked_safe_for_redirect(unrelated_unsafe_url);
+ default_context_.set_network_delegate(&network_delegate);
+ TestDelegate d;
+ {
+ URLRequest r(test_server_.GetURL("whatever"),
+ DEFAULT_PRIORITY,
+ &d,
+ &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(0, r.status().error());
+ EXPECT_EQ(safe_url, r.url());
+ EXPECT_EQ("", d.data_received());
+ }
+}
mmenke 2014/03/21 18:15:04 This test is indeed probably overkill.
robwu 2014/03/25 00:04:00 Indeed, removed; This case is already covered by t
+
+// Redirects from an URL with fragment to an unsafe URL without fragment should
+// be allowed.
+TEST_F(URLRequestTestHTTP, UnsafeRedirectWithReferenceFragment) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("original#fragment"));
+ GURL unsafe_url("data:,url-marked-safe-and-used-in-redirect");
+ GURL expected_url("data:,url-marked-safe-and-used-in-redirect#fragment");
mmenke 2014/03/21 18:15:04 Suggest a linebreak after the GURLs in all of thes
robwu 2014/03/25 00:04:00 Done.
+ RedirectOnHeadersReceivedNetworkDelegate network_delegate(unsafe_url);
+ network_delegate.set_url_marked_safe_for_redirect(unsafe_url);
+ default_context_.set_network_delegate(&network_delegate);
+ TestDelegate d;
+ {
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(0, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(expected_url, r.url());
+ }
+}
+
+// When a delegate has specified a safe redirect URL, but it does not match the
+// redirect target, then do not prevent the reference fragment from being added.
+TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragment) {
+ ASSERT_TRUE(test_server_.Start());
+
+ GURL original_url(test_server_.GetURL("original#expected-fragment"));
+ GURL unsafe_url("data:text/html,this-url-does-not-match-redirect-url");
+ GURL redirect_url(test_server_.GetURL("target"));
+ GURL expected_redirect_url(test_server_.GetURL("target#expected-fragment"));
+ RedirectOnHeadersReceivedNetworkDelegate network_delegate(redirect_url);
+ network_delegate.set_url_marked_safe_for_redirect(unsafe_url);
+ default_context_.set_network_delegate(&network_delegate);
+ TestDelegate d;
+ {
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &default_context_);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(0, r.status().error());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(expected_redirect_url, r.url());
+ }
+}
mmenke 2014/03/21 18:15:04 I don't think we need this test. But I do think w
robwu 2014/03/25 00:04:00 Currently, the test is indeed redundant (it merely
+
// In this unit test, we're using the HTTPTestServer as a proxy server and
// issuing a CONNECT request with the magic host name "www.redirect.com".
// The HTTPTestServer will return a 302 response, which we should not
@@ -3007,6 +3220,83 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestPost) {
EXPECT_EQ(1, network_delegate.destroyed_requests());
}
+// Tests that the network delegate can block and redirect a request to a new
+// URL during OnHeadersReceived.
+TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestOnHeadersReceived) {
+ ASSERT_TRUE(test_server_.Start());
+
+ TestDelegate d;
+ BlockingNetworkDelegate network_delegate(
+ BlockingNetworkDelegate::AUTO_CALLBACK);
+ network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED);
+ GURL redirect_url(test_server_.GetURL("simple.html"));
+ network_delegate.set_redirect_url_on_headers_received(redirect_url);
+
+ TestURLRequestContextWithProxy context(
+ test_server_.host_port_pair().ToString(), &network_delegate);
+
+ {
+ GURL original_url(test_server_.GetURL("empty.html"));
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &context);
+
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(0, r.status().error());
+ EXPECT_EQ(redirect_url, r.url());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(2, network_delegate.created_requests());
+ EXPECT_EQ(0, network_delegate.destroyed_requests());
+ }
+ EXPECT_EQ(1, network_delegate.destroyed_requests());
+}
+
+// Tests that redirects caused by the network delegate during OnHeadersReceived
+// preserve POST data.
mmenke 2014/03/21 18:15:04 Both these test are probably overkill as well. Mo
robwu 2014/03/25 00:04:00 You're right. What I actually wanted to do was to
+TEST_F(URLRequestTestHTTP,
+ NetworkDelegateRedirectRequestOnHeadersReceivedPost) {
+ ASSERT_TRUE(test_server_.Start());
+
+ const char kData[] = "hello world";
+
+ TestDelegate d;
+ BlockingNetworkDelegate network_delegate(
+ BlockingNetworkDelegate::AUTO_CALLBACK);
+ network_delegate.set_block_on(BlockingNetworkDelegate::ON_HEADERS_RECEIVED);
+ GURL redirect_url(test_server_.GetURL("echo"));
+ network_delegate.set_redirect_url_on_headers_received(redirect_url);
+
+ TestURLRequestContext context(true);
+ context.set_network_delegate(&network_delegate);
+ context.Init();
+
+ {
+ GURL original_url(test_server_.GetURL("empty.html"));
+ URLRequest r(original_url, DEFAULT_PRIORITY, &d, &context);
+ r.set_method("POST");
+ r.set_upload(make_scoped_ptr(CreateSimpleUploadData(kData)));
+ HttpRequestHeaders headers;
+ headers.SetHeader(HttpRequestHeaders::kContentLength,
+ base::UintToString(arraysize(kData) - 1));
+ r.SetExtraRequestHeaders(headers);
+ r.Start();
+ base::RunLoop().Run();
+
+ EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status());
+ EXPECT_EQ(0, r.status().error());
+ EXPECT_EQ(redirect_url, r.url());
+ EXPECT_EQ(original_url, r.original_url());
+ EXPECT_EQ(2U, r.url_chain().size());
+ EXPECT_EQ(2, network_delegate.created_requests());
+ EXPECT_EQ(0, network_delegate.destroyed_requests());
+ EXPECT_EQ("POST", r.method());
+ EXPECT_EQ(kData, d.data_received());
+ }
+ EXPECT_EQ(1, network_delegate.destroyed_requests());
+}
+
// Tests that the network delegate can synchronously complete OnAuthRequired
// by taking no action. This indicates that the NetworkDelegate does not want to
// handle the challenge, and is passing the buck along to the
@@ -3919,10 +4209,12 @@ class AsyncLoggingNetworkDelegate : public TestNetworkDelegate {
URLRequest* request,
const CompletionCallback& callback,
const HttpResponseHeaders* original_response_headers,
- scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE {
+ scoped_refptr<HttpResponseHeaders>* override_response_headers,
+ GURL* allowed_unsafe_redirect_url) OVERRIDE {
TestNetworkDelegate::OnHeadersReceived(request, callback,
original_response_headers,
- override_response_headers);
+ override_response_headers,
+ allowed_unsafe_redirect_url);
return RunCallbackAsynchronously(request, callback);
}
« net/url_request/url_request_http_job.cc ('K') | « net/url_request/url_request_test_util.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698