| 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 43d5d308f5c5a813e6d509ceabc4ac0e1bb7e935..9d90cf82801c9e52f49c4ba04aa3276360713dfa 100644
 | 
| --- a/net/url_request/url_request_unittest.cc
 | 
| +++ b/net/url_request/url_request_unittest.cc
 | 
| @@ -368,7 +368,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,
 | 
| @@ -391,7 +392,7 @@ 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.
 | 
|    int block_on_;  // Bit mask: in which stages to block.
 | 
|  
 | 
|    // |auth_credentials_| will be copied to |*target_auth_credential_| on
 | 
| @@ -483,10 +484,13 @@ int BlockingNetworkDelegate::OnHeadersReceived(
 | 
|      URLRequest* request,
 | 
|      const CompletionCallback& callback,
 | 
|      const HttpResponseHeaders* original_response_headers,
 | 
| -    scoped_refptr<HttpResponseHeaders>* override_response_headers) {
 | 
| -  TestNetworkDelegate::OnHeadersReceived(
 | 
| -      request, callback, original_response_headers,
 | 
| -      override_response_headers);
 | 
| +    scoped_refptr<HttpResponseHeaders>* override_response_headers,
 | 
| +    GURL* allowed_unsafe_redirect_url) {
 | 
| +  TestNetworkDelegate::OnHeadersReceived(request,
 | 
| +                                         callback,
 | 
| +                                         original_response_headers,
 | 
| +                                         override_response_headers,
 | 
| +                                         allowed_unsafe_redirect_url);
 | 
|  
 | 
|    return MaybeBlockStage(ON_HEADERS_RECEIVED, callback);
 | 
|  }
 | 
| @@ -2417,8 +2421,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_;
 | 
| @@ -2430,7 +2434,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());
 | 
|  
 | 
| @@ -2441,7 +2446,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
 | 
| @@ -3008,6 +3014,39 @@ 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_on_headers_received_url(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(net::OK, 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 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
 | 
| @@ -3920,10 +3959,13 @@ class AsyncLoggingNetworkDelegate : public TestNetworkDelegate {
 | 
|        URLRequest* request,
 | 
|        const CompletionCallback& callback,
 | 
|        const HttpResponseHeaders* original_response_headers,
 | 
| -      scoped_refptr<HttpResponseHeaders>* override_response_headers) OVERRIDE {
 | 
| -    TestNetworkDelegate::OnHeadersReceived(request, callback,
 | 
| +      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);
 | 
|    }
 | 
|  
 | 
| @@ -5235,6 +5277,142 @@ TEST_F(URLRequestTestHTTP, NoCacheOnNetworkDelegateRedirect) {
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +// Tests that redirection to an unsafe URL is allowed when it has been marked as
 | 
| +// safe.
 | 
| +TEST_F(URLRequestTestHTTP, UnsafeRedirectToWhitelistedUnsafeURL) {
 | 
| +  ASSERT_TRUE(test_server_.Start());
 | 
| +
 | 
| +  GURL unsafe_url("data:text/html,this-is-considered-an-unsafe-url");
 | 
| +  default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
 | 
| +  default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
 | 
| +
 | 
| +  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(net::OK, r.status().error());
 | 
| +    EXPECT_EQ(unsafe_url, r.url());
 | 
| +    EXPECT_EQ("this-is-considered-an-unsafe-url", d.data_received());
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// 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");
 | 
| +  default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
 | 
| +  default_network_delegate_.set_allowed_unsafe_redirect_url(
 | 
| +      different_unsafe_url);
 | 
| +
 | 
| +  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());
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// 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");
 | 
| +
 | 
| +  default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
 | 
| +  default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
 | 
| +
 | 
| +  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(net::OK, r.status().error());
 | 
| +    EXPECT_EQ(original_url, r.original_url());
 | 
| +    EXPECT_EQ(expected_url, r.url());
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +// Redirects from an URL with fragment to an unsafe URL with fragment should
 | 
| +// be allowed, and the reference fragment of the target URL should be preserved.
 | 
| +TEST_F(URLRequestTestHTTP, UnsafeRedirectWithDifferentReferenceFragment) {
 | 
| +  ASSERT_TRUE(test_server_.Start());
 | 
| +
 | 
| +  GURL original_url(test_server_.GetURL("original#fragment1"));
 | 
| +  GURL unsafe_url("data:,url-marked-safe-and-used-in-redirect#fragment2");
 | 
| +  GURL expected_url("data:,url-marked-safe-and-used-in-redirect#fragment2");
 | 
| +
 | 
| +  default_network_delegate_.set_redirect_on_headers_received_url(unsafe_url);
 | 
| +  default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
 | 
| +
 | 
| +  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(net::OK, 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"));
 | 
| +
 | 
| +  default_network_delegate_.set_redirect_on_headers_received_url(redirect_url);
 | 
| +  default_network_delegate_.set_allowed_unsafe_redirect_url(unsafe_url);
 | 
| +
 | 
| +  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(net::OK, r.status().error());
 | 
| +    EXPECT_EQ(original_url, r.original_url());
 | 
| +    EXPECT_EQ(expected_redirect_url, r.url());
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  TEST_F(URLRequestTestHTTP, NoUserPassInReferrer) {
 | 
|    ASSERT_TRUE(test_server_.Start());
 | 
|  
 | 
| 
 |