Index: content/browser/loader/resource_dispatcher_host_unittest.cc |
diff --git a/content/browser/loader/resource_dispatcher_host_unittest.cc b/content/browser/loader/resource_dispatcher_host_unittest.cc |
index 126fbad1e2936ad7797250eeecff561ac341f249..025c888848af48305ecacb909c0d5b8c8e1ab049 100644 |
--- a/content/browser/loader/resource_dispatcher_host_unittest.cc |
+++ b/content/browser/loader/resource_dispatcher_host_unittest.cc |
@@ -2,14 +2,19 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <queue> |
+#include <string> |
#include <vector> |
#include "base/basictypes.h" |
#include "base/bind.h" |
+#include "base/callback.h" |
#include "base/command_line.h" |
#include "base/files/file_path.h" |
#include "base/files/file_util.h" |
#include "base/location.h" |
+#include "base/macros.h" |
+#include "base/memory/scoped_ptr.h" |
#include "base/memory/scoped_vector.h" |
#include "base/memory/shared_memory.h" |
#include "base/pickle.h" |
@@ -47,9 +52,12 @@ |
#include "content/public/test/test_renderer_host.h" |
#include "content/test/test_content_browser_client.h" |
#include "net/base/elements_upload_data_stream.h" |
+#include "net/base/load_flags.h" |
#include "net/base/net_errors.h" |
#include "net/base/request_priority.h" |
#include "net/base/upload_bytes_element_reader.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/http/http_response_info.h" |
#include "net/http/http_util.h" |
#include "net/url_request/url_request.h" |
#include "net/url_request/url_request_context.h" |
@@ -830,8 +838,10 @@ class ResourceDispatcherHostTest : public testing::Test, |
typedef ResourceDispatcherHostImpl::LoadInfo LoadInfo; |
typedef ResourceDispatcherHostImpl::LoadInfoMap LoadInfoMap; |
- ResourceDispatcherHostTest() |
+ ResourceDispatcherHostTest( |
+ scoped_ptr<net::TestNetworkDelegate> network_delegate) |
: thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), |
+ network_delegate_(network_delegate.Pass()), |
old_factory_(NULL), |
send_data_received_acks_(false) { |
browser_context_.reset(new TestBrowserContext()); |
@@ -843,9 +853,15 @@ class ResourceDispatcherHostTest : public testing::Test, |
browser_context_->GetResourceContext()->GetRequestContext(); |
job_factory_.reset(new TestURLRequestJobFactory(this)); |
request_context->set_job_factory(job_factory_.get()); |
- request_context->set_network_delegate(&network_delegate_); |
+ request_context->set_network_delegate(network_delegate_.get()); |
+ // TODO(ricea): Remove this when it becomes the default. |
+ host_.async_revalidation_enabled_ = true; |
} |
+ ResourceDispatcherHostTest() |
+ : ResourceDispatcherHostTest( |
+ make_scoped_ptr(new net::TestNetworkDelegate)) {} |
+ |
// IPC::Sender implementation |
bool Send(IPC::Message* msg) override { |
accum_.AddMessage(*msg); |
@@ -961,7 +977,9 @@ class ResourceDispatcherHostTest : public testing::Test, |
void CompleteStartRequest(int request_id); |
void CompleteStartRequest(ResourceMessageFilter* filter, int request_id); |
- net::TestNetworkDelegate* network_delegate() { return &network_delegate_; } |
+ net::TestNetworkDelegate* network_delegate() { |
+ return network_delegate_.get(); |
+ } |
void EnsureSchemeIsAllowed(const std::string& scheme) { |
ChildProcessSecurityPolicyImpl* policy = |
@@ -1021,9 +1039,22 @@ class ResourceDispatcherHostTest : public testing::Test, |
wait_for_request_complete_loop_.reset(); |
} |
+ typedef base::Callback<net::URLRequestJob*(net::URLRequest*, |
+ net::NetworkDelegate*)> |
+ URLRequestJobCreateCallback; |
+ |
+ void SetCustomURLRequestJobCreateCallback( |
+ const URLRequestJobCreateCallback& callback) { |
+ custom_url_request_job_create_callback_.reset( |
+ new URLRequestJobCreateCallback(callback)); |
+ } |
+ |
scoped_ptr<LoadInfoTestRequestInfo> loader_test_request_info_; |
scoped_ptr<base::RunLoop> wait_for_request_create_loop_; |
+ scoped_ptr<URLRequestJobCreateCallback> |
+ custom_url_request_job_create_callback_; |
+ |
content::TestBrowserThreadBundle thread_bundle_; |
scoped_ptr<TestBrowserContext> browser_context_; |
scoped_ptr<TestURLRequestJobFactory> job_factory_; |
@@ -1031,7 +1062,7 @@ class ResourceDispatcherHostTest : public testing::Test, |
scoped_ptr<TestWebContentsObserver> web_contents_observer_; |
scoped_refptr<ForwardingFilter> filter_; |
scoped_refptr<TestFilterSpecifyingChild> web_contents_filter_; |
- net::TestNetworkDelegate network_delegate_; |
+ scoped_ptr<net::TestNetworkDelegate> network_delegate_; |
ResourceDispatcherHostImpl host_; |
ResourceIPCAccumulator accum_; |
std::string response_headers_; |
@@ -3397,6 +3428,10 @@ net::URLRequestJob* TestURLRequestJobFactory::MaybeCreateJobWithProtocolHandler( |
return new URLRequestLoadInfoJob(request, network_delegate, |
info->load_state, info->upload_progress); |
} |
+ if (test_fixture_->custom_url_request_job_create_callback_) { |
+ return test_fixture_->custom_url_request_job_create_callback_->Run( |
+ request, network_delegate); |
+ } |
if (test_fixture_->response_headers_.empty()) { |
if (delay_start_) { |
return new URLRequestTestDelayedStartJob(request, network_delegate); |
@@ -3443,4 +3478,294 @@ net::URLRequestJob* TestURLRequestJobFactory::MaybeInterceptResponse( |
return nullptr; |
} |
+TEST_F(ResourceDispatcherHostTest, SupportsAsyncRevalidation) { |
+ // Scheme has to be HTTP or HTTPS to support async revalidation. |
+ HandleScheme("http"); |
+ // Prevent the job from completing synchronously. |
+ job_factory_->SetDelayedCompleteJobGeneration(true); |
+ SetResponse(net::URLRequestTestJob::test_headers(), "delay complete"); |
+ MakeTestRequest(0, 1, GURL("http://example.com/baz")); |
+ |
+ net::URLRequest* url_request( |
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1))); |
+ ASSERT_TRUE(url_request); |
+ |
+ EXPECT_TRUE(url_request->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION); |
+} |
+ |
+TEST_F(ResourceDispatcherHostTest, AsyncRevalidationNotSupportedForPOST) { |
+ // Scheme has to be HTTP or HTTPS to support async revalidation. |
+ HandleScheme("http"); |
+ // Prevent the job from completing synchronously. |
+ job_factory_->SetDelayedCompleteJobGeneration(true); |
+ SetResponse(net::URLRequestTestJob::test_headers(), "delay complete"); |
+ // Create POST request. |
+ ResourceHostMsg_Request request = CreateResourceRequest( |
+ "POST", RESOURCE_TYPE_SUB_RESOURCE, GURL("http://example.com/baz.php")); |
+ ResourceHostMsg_RequestResource msg(0, 1, request); |
+ host_.OnMessageReceived(msg, filter_.get()); |
+ KickOffRequest(); |
+ |
+ net::URLRequest* url_request( |
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1))); |
+ ASSERT_TRUE(url_request); |
+ |
+ EXPECT_FALSE(url_request->load_flags() & |
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION); |
+} |
+ |
+TEST_F(ResourceDispatcherHostTest, AsyncRevalidationNotSupportedForRedirect) { |
+ static const char kRedirectHeaders[] = |
+ "HTTP/1.1 302 MOVED\n" |
+ "Location: http://example.com/var\n" |
+ "\n"; |
+ // Scheme has to be HTTP or HTTPS to support async revalidation. |
+ HandleScheme("http"); |
+ // Prevent the job from completing synchronously. |
+ job_factory_->SetDelayedCompleteJobGeneration(true); |
+ SetResponse(kRedirectHeaders, ""); |
+ |
+ MakeTestRequest(0, 1, GURL("http://example.com/baz")); |
+ |
+ net::URLRequest* url_request( |
+ host_.GetURLRequest(GlobalRequestID(filter_->child_id(), 1))); |
+ ASSERT_TRUE(url_request); |
+ |
+ EXPECT_FALSE(url_request->load_flags() & |
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION); |
+} |
+ |
+// A URLRequestJob implementation which sets the |async_revalidation_required| |
+// flag on the HttpResponseInfo object to true if the request has the |
+// LOAD_SUPPORT_ASYNC_REVALIDATION flag. |
+class AsyncRevalidationRequiredURLRequestTestJob |
+ : public net::URLRequestTestJob { |
+ public: |
+ // The Create() method is useful for wrapping the construction of the object |
+ // in a Callback. |
+ static net::URLRequestJob* Create(net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate) { |
+ return new AsyncRevalidationRequiredURLRequestTestJob(request, |
+ network_delegate); |
+ } |
+ |
+ void GetResponseInfo(net::HttpResponseInfo* info) override { |
+ if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION) |
+ info->async_revalidation_required = true; |
+ } |
+ |
+ private: |
+ AsyncRevalidationRequiredURLRequestTestJob( |
+ net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate) |
+ : URLRequestTestJob(request, |
+ network_delegate, |
+ net::URLRequestTestJob::test_headers(), |
+ std::string(), |
+ false) {} |
+ |
+ ~AsyncRevalidationRequiredURLRequestTestJob() override {} |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AsyncRevalidationRequiredURLRequestTestJob); |
+}; |
+ |
+// A URLRequestJob implementation which serves a redirect and sets the |
+// |async_revalidation_required| flag on the HttpResponseInfo object to true if |
+// the request has the LOAD_SUPPORT_ASYNC_REVALIDATION flag. |
+class RedirectAndRevalidateURLRequestTestJob : public net::URLRequestTestJob { |
+ public: |
+ // The Create() method is useful for wrapping the construction of the object |
+ // in a Callback. |
+ static net::URLRequestJob* Create(net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate) { |
+ return new RedirectAndRevalidateURLRequestTestJob(request, |
+ network_delegate); |
+ } |
+ |
+ void GetResponseInfo(net::HttpResponseInfo* info) override { |
+ URLRequestTestJob::GetResponseInfo(info); |
+ if (request()->load_flags() & net::LOAD_SUPPORT_ASYNC_REVALIDATION) |
+ info->async_revalidation_required = true; |
+ } |
+ |
+ private: |
+ RedirectAndRevalidateURLRequestTestJob(net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate) |
+ : URLRequestTestJob( |
+ request, |
+ network_delegate, |
+ std::string(CreateRedirectHeaders()), |
+ std::string(), |
+ false) {} |
+ |
+ ~RedirectAndRevalidateURLRequestTestJob() override {} |
+ |
+ static std::string CreateRedirectHeaders() { |
+ static const char kRedirectHeaders[] = |
+ "HTTP/1.1 302 MOVED\0" |
+ "Location: http://example.com/var\0" |
+ "\0"; |
+ return std::string(kRedirectHeaders, arraysize(kRedirectHeaders)); |
+ } |
+ |
+ DISALLOW_COPY_AND_ASSIGN(RedirectAndRevalidateURLRequestTestJob); |
+}; |
+ |
+// A NetworkDelegate that records the URLRequests as they are created. |
+class URLRequestRecordingNetworkDelegate : public net::TestNetworkDelegate { |
+ public: |
+ URLRequestRecordingNetworkDelegate() : requests_() {} |
+ |
+ net::URLRequest* NextRequest() { |
+ if (requests_.empty()) |
+ return nullptr; |
+ net::URLRequest* request = requests_.front(); |
+ requests_.pop(); |
+ return request; |
+ } |
+ |
+ bool IsEmpty() { |
+ return requests_.empty(); |
+ } |
+ |
+ int OnBeforeURLRequest(net::URLRequest* request, |
+ const net::CompletionCallback& callback, |
+ GURL* new_url) override { |
+ requests_.push(request); |
+ return TestNetworkDelegate::OnBeforeURLRequest(request, callback, new_url); |
+ } |
+ |
+ private: |
+ std::queue<net::URLRequest*> requests_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(URLRequestRecordingNetworkDelegate); |
+}; |
+ |
+class ResourceDispatcherHostAsyncRevalidationTest |
+ : public ResourceDispatcherHostTest { |
+ public: |
+ ResourceDispatcherHostAsyncRevalidationTest() |
+ : ResourceDispatcherHostTest( |
+ make_scoped_ptr(new URLRequestRecordingNetworkDelegate)) { |
+ // Scheme has to be HTTP or HTTPS to support async revalidation. |
+ HandleScheme("http"); |
+ // Use the AsyncRevalidationRequiredURLRequestTestJob. |
+ SetCustomURLRequestJobCreateCallback( |
+ base::Bind(&AsyncRevalidationRequiredURLRequestTestJob::Create)); |
+ } |
+ |
+ URLRequestRecordingNetworkDelegate* recording_network_delegate() { |
+ return static_cast<URLRequestRecordingNetworkDelegate*>(network_delegate()); |
+ } |
+ |
+ net::URLRequest* NextRequest() { |
+ return recording_network_delegate()->NextRequest(); |
+ } |
+ |
+ bool IsEmpty() { |
+ return recording_network_delegate()->IsEmpty(); |
+ } |
+}; |
+ |
+// Verify that an async revalidation is actually created when needed. |
+TEST_F(ResourceDispatcherHostAsyncRevalidationTest, Issued) { |
+ // Create the original request. |
+ MakeTestRequest(0, 1, GURL("http://example.com/baz")); |
+ |
+ net::URLRequest* initial_request = NextRequest(); |
+ ASSERT_TRUE(initial_request); |
+ EXPECT_TRUE(initial_request->load_flags() & |
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION); |
+ |
+ net::URLRequest* async_request = NextRequest(); |
+ ASSERT_TRUE(async_request); |
+} |
+ |
+// Verify the the URL of the async revalidation matches the original request. |
+TEST_F(ResourceDispatcherHostAsyncRevalidationTest, URLMatches) { |
+ // Create the original request. |
+ MakeTestRequest(0, 1, GURL("http://example.com/special-baz")); |
+ |
+ // Discard the original request. |
+ NextRequest(); |
+ |
+ net::URLRequest* async_request = NextRequest(); |
+ ASSERT_TRUE(async_request); |
+ EXPECT_EQ(GURL("http://example.com/special-baz"), async_request->url()); |
+} |
+ |
+TEST_F(ResourceDispatcherHostAsyncRevalidationTest, |
+ AsyncRevalidationsDoNotSupportAsyncRevalidation) { |
+ // Create the original request. |
+ MakeTestRequest(0, 1, GURL("http://example.com/baz")); |
+ |
+ // Discard the original request. |
+ NextRequest(); |
+ |
+ // Get the async revalidation request. |
+ net::URLRequest* async_request = NextRequest(); |
+ ASSERT_TRUE(async_request); |
+ EXPECT_FALSE(async_request->load_flags() & |
+ net::LOAD_SUPPORT_ASYNC_REVALIDATION); |
+} |
+ |
+TEST_F(ResourceDispatcherHostAsyncRevalidationTest, |
+ AsyncRevalidationsNotDuplicated) { |
+ // Create the original request. |
+ MakeTestRequest(0, 1, GURL("http://example.com/baz")); |
+ |
+ // Discard the original request. |
+ NextRequest(); |
+ |
+ // Get the async revalidation request. |
+ net::URLRequest* async_request = NextRequest(); |
+ EXPECT_TRUE(async_request); |
+ |
+ // Start a second request to the same URL. |
+ MakeTestRequest(0, 2, GURL("http://example.com/baz")); |
+ |
+ // Discard the second request. |
+ NextRequest(); |
+ |
+ // There should not be another async revalidation request. |
+ EXPECT_TRUE(IsEmpty()); |
+} |
+ |
+// Async revalidation to different URLs should not be treated as duplicates. |
+TEST_F(ResourceDispatcherHostAsyncRevalidationTest, |
+ AsyncRevalidationsToSeparateURLsAreSeparate) { |
+ // Create two requests to two URLs. |
+ MakeTestRequest(0, 1, GURL("http://example.com/baz")); |
+ MakeTestRequest(0, 2, GURL("http://example.com/far")); |
+ |
+ net::URLRequest* initial_request = NextRequest(); |
+ ASSERT_TRUE(initial_request); |
+ net::URLRequest* initial_async_revalidation = NextRequest(); |
+ ASSERT_TRUE(initial_async_revalidation); |
+ net::URLRequest* second_request = NextRequest(); |
+ ASSERT_TRUE(second_request); |
+ net::URLRequest* second_async_revalidation = NextRequest(); |
+ ASSERT_TRUE(second_async_revalidation); |
+ |
+ EXPECT_EQ("http://example.com/baz", initial_request->url().spec()); |
+ EXPECT_EQ("http://example.com/baz", initial_async_revalidation->url().spec()); |
+ EXPECT_EQ("http://example.com/far", second_request->url().spec()); |
+ EXPECT_EQ("http://example.com/far", second_async_revalidation->url().spec()); |
+} |
+ |
+// A stale-while-revalidate applicable redirect response should not result in an |
+// async revalidation. |
+TEST_F(ResourceDispatcherHostAsyncRevalidationTest, RedirectNotApplicable) { |
+ // Use the appropriate URLRequestJob for the test. |
+ SetCustomURLRequestJobCreateCallback( |
+ base::Bind(&RedirectAndRevalidateURLRequestTestJob::Create)); |
+ MakeTestRequest(0, 1, GURL("http://example.com/redirect")); |
+ |
+ net::URLRequest* initial_request = NextRequest(); |
+ EXPECT_TRUE(initial_request); |
+ |
+ // There should not be an async revalidation request. |
+ EXPECT_TRUE(IsEmpty()); |
+} |
+ |
} // namespace content |