Index: content/browser/loader/resource_dispatcher_host_impl.cc |
diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc |
index ade6e5d1648e01c3304f968789c526f469e63511..0c045d8bde47a851a57433791b1667be59fae6ce 100644 |
--- a/content/browser/loader/resource_dispatcher_host_impl.cc |
+++ b/content/browser/loader/resource_dispatcher_host_impl.cc |
@@ -19,6 +19,7 @@ |
#include "base/memory/scoped_ptr.h" |
#include "base/memory/shared_memory.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/metrics/field_trial.h" |
#include "base/metrics/histogram_macros.h" |
#include "base/metrics/sparse_histogram.h" |
#include "base/profiler/scoped_tracker.h" |
@@ -41,6 +42,7 @@ |
#include "content/browser/loader/detachable_resource_handler.h" |
#include "content/browser/loader/navigation_resource_handler.h" |
#include "content/browser/loader/navigation_url_loader_impl_core.h" |
+#include "content/browser/loader/null_resource_handler.h" |
#include "content/browser/loader/power_save_block_resource_throttle.h" |
#include "content/browser/loader/redirect_to_file_resource_handler.h" |
#include "content/browser/loader/resource_message_filter.h" |
@@ -89,6 +91,8 @@ |
#include "net/cookies/cookie_monster.h" |
#include "net/http/http_response_headers.h" |
#include "net/http/http_response_info.h" |
+#include "net/http/http_transaction_factory.h" |
+#include "net/http/http_util.h" |
#include "net/ssl/ssl_cert_request_info.h" |
#include "net/url_request/url_request.h" |
#include "net/url_request/url_request_context.h" |
@@ -416,6 +420,31 @@ void LogResourceRequestTimeOnUI( |
} |
} |
+bool QualifiesForAsyncRevalidation(const ResourceHostMsg_Request& request) { |
+ if (request.load_flags & |
+ (net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | |
+ net::LOAD_VALIDATE_CACHE | net::LOAD_PREFERRING_CACHE | |
+ net::LOAD_ONLY_FROM_CACHE)) { |
+ return false; |
+ } |
+ if (request.method != "GET") |
+ return false; |
+ // A GET request should not have a body, but don't leave it to chance. |
+ if (request.request_body.get()) |
+ return false; |
+ if (!request.url.SchemeIsHTTPOrHTTPS()) |
+ return false; |
+ return true; |
+} |
+ |
+std::pair<net::HttpCache*, std::string> GenerateAsyncRevalidationKeyFor( |
+ net::HttpTransactionFactory* http_transaction_factory, |
+ const GURL& url) { |
+ net::HttpCache* http_cache = http_transaction_factory->GetCache(); |
+ std::string url_key = net::HttpUtil::SpecForRequest(url); |
+ return std::make_pair(http_cache, url_key); |
+} |
+ |
} // namespace |
// static |
@@ -429,14 +458,14 @@ ResourceDispatcherHostImpl::ResourceDispatcherHostImpl() |
is_shutdown_(false), |
num_in_flight_requests_(0), |
max_num_in_flight_requests_(base::SharedMemory::GetHandleLimit()), |
- max_num_in_flight_requests_per_process_( |
- static_cast<int>( |
- max_num_in_flight_requests_ * kMaxRequestsPerProcessRatio)), |
+ max_num_in_flight_requests_per_process_(static_cast<int>( |
+ max_num_in_flight_requests_ * kMaxRequestsPerProcessRatio)), |
max_outstanding_requests_cost_per_process_( |
kMaxOutstandingRequestsCostPerProcess), |
filter_(NULL), |
delegate_(NULL), |
- allow_cross_origin_auth_prompt_(false) { |
+ allow_cross_origin_auth_prompt_(false), |
+ async_revalidation_enabled_(false) { |
DCHECK_CURRENTLY_ON(BrowserThread::UI); |
DCHECK(!g_resource_dispatcher_host); |
g_resource_dispatcher_host = this; |
@@ -454,6 +483,12 @@ ResourceDispatcherHostImpl::ResourceDispatcherHostImpl() |
update_load_states_timer_.reset( |
new base::RepeatingTimer<ResourceDispatcherHostImpl>()); |
+ |
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
+ if (base::FieldTrialList::FindFullName("StaleWhileRevalidate") == "Enabled" || |
+ command_line->HasSwitch(switches::kEnableStaleWhileRevalidate)) { |
+ async_revalidation_enabled_ = true; |
+ } |
} |
ResourceDispatcherHostImpl::~ResourceDispatcherHostImpl() { |
@@ -819,22 +854,25 @@ void ResourceDispatcherHostImpl::DidReceiveRedirect(ResourceLoader* loader, |
void ResourceDispatcherHostImpl::DidReceiveResponse(ResourceLoader* loader) { |
ResourceRequestInfoImpl* info = loader->GetRequestInfo(); |
- |
- if (loader->request()->was_fetched_via_proxy() && |
- loader->request()->was_fetched_via_spdy() && |
- loader->request()->url().SchemeIs(url::kHttpScheme)) { |
+ net::URLRequest* request = loader->request(); |
+ if (request->was_fetched_via_proxy() && |
+ request->was_fetched_via_spdy() && |
+ request->url().SchemeIs(url::kHttpScheme)) { |
tyoshino (SeeGerritForStatus)
2015/06/16 13:04:42
you can split this refactoring into a small patch.
Adam Rice
2015/06/17 10:05:15
Done: https://codereview.chromium.org/1183723006/
|
scheduler_->OnReceivedSpdyProxiedHttpResponse( |
info->GetChildID(), info->GetRouteID()); |
} |
+ if (request->response_info().async_revalidation_required) |
+ BeginAsyncRevalidation(request); |
+ |
int render_process_id, render_frame_host; |
if (!info->GetAssociatedRenderFrame(&render_process_id, &render_frame_host)) |
return; |
// Notify the observers on the UI thread. |
scoped_ptr<ResourceRequestDetails> detail(new ResourceRequestDetails( |
- loader->request(), |
- GetCertID(loader->request(), info->GetChildID()))); |
+ request, |
+ GetCertID(request, info->GetChildID()))); |
BrowserThread::PostTask( |
BrowserThread::UI, FROM_HERE, |
base::Bind( |
@@ -1186,7 +1224,22 @@ void ResourceDispatcherHostImpl::BeginRequest( |
AbortRequestBeforeItStarts(filter_, sync_result, request_id); |
return; |
} |
+ bool is_async_revalidation = false; |
+ BeginRequestFromData(request_id, request_data, sync_result, process_type, |
+ child_id, route_id, is_async_revalidation, |
+ request_context, resource_context); |
+} |
+void ResourceDispatcherHostImpl::BeginRequestFromData( |
+ int request_id, |
+ const ResourceHostMsg_Request& request_data, |
+ IPC::Message* sync_result, |
+ int process_type, |
+ int child_id, |
+ int route_id, |
+ bool is_async_revalidation, |
+ net::URLRequestContext* request_context, |
+ ResourceContext* resource_context) { |
// Construct the request. |
scoped_ptr<net::URLRequest> new_request; |
new_request = request_context->CreateRequest( |
@@ -1259,6 +1312,13 @@ void ResourceDispatcherHostImpl::BeginRequest( |
load_flags |= net::LOAD_DO_NOT_USE_EMBEDDED_IDENTITY; |
} |
+ // Check if request is eligible for async revalidation. |
+ bool support_async_revalidation = |
+ (!is_async_revalidation && !is_sync_load && async_revalidation_enabled_ && |
+ QualifiesForAsyncRevalidation(request_data)); |
+ if (support_async_revalidation) |
+ load_flags |= net::LOAD_SUPPORT_ASYNC_REVALIDATION; |
+ |
// Sync loads should have maximum priority and should be the only |
// requets that have the ignore limits flag set. |
if (is_sync_load) { |
@@ -1292,7 +1352,9 @@ void ResourceDispatcherHostImpl::BeginRequest( |
request_data.referrer_policy, |
request_data.visiblity_state, |
resource_context, filter_->GetWeakPtr(), |
- !is_sync_load); |
+ !is_sync_load, |
+ is_async_revalidation, |
+ support_async_revalidation ? &request_data : NULL); |
// Request takes ownership. |
extra_info->AssociateWithRequest(new_request.get()); |
@@ -1327,11 +1389,9 @@ void ResourceDispatcherHostImpl::BeginRequest( |
request_data.appcache_host_id, request_data.resource_type, |
request_data.should_reset_appcache); |
- scoped_ptr<ResourceHandler> handler( |
- CreateResourceHandler( |
- new_request.get(), |
- request_data, sync_result, route_id, process_type, child_id, |
- resource_context)); |
+ scoped_ptr<ResourceHandler> handler(CreateResourceHandler( |
+ new_request.get(), request_data, sync_result, route_id, process_type, |
+ child_id, is_async_revalidation, resource_context)); |
if (handler) |
BeginRequestInternal(new_request.Pass(), handler.Pass()); |
@@ -1344,6 +1404,7 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( |
int route_id, |
int process_type, |
int child_id, |
+ bool is_async_revalidation, |
ResourceContext* resource_context) { |
// TODO(pkasting): Remove ScopedTracker below once crbug.com/456331 is fixed. |
tracked_objects::ScopedTracker tracking_profile( |
@@ -1360,6 +1421,10 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( |
} |
handler.reset(new SyncResourceHandler(request, sync_result, this)); |
+ } else if (is_async_revalidation) { |
+ handler.reset(new NullResourceHandler( |
+ request, this, base::TimeDelta::FromSeconds( |
+ kNullResourceHandlerDefaultReadTimeoutSecs))); |
} else { |
handler.reset(new AsyncResourceHandler(request, this)); |
@@ -1400,29 +1465,39 @@ scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateResourceHandler( |
handler.reset(new CrossSiteResourceHandler(handler.Pass(), request)); |
} |
- return AddStandardHandlers(request, request_data.resource_type, |
- resource_context, filter_->appcache_service(), |
- child_id, route_id, handler.Pass()); |
+ // The BufferedResourceHandler performs mime-type sniffing and redirecting of |
+ // downloads and plugin-handled content. The first we don't need for an |
+ // async_revalidation, and the second is dangerous. |
+ if (!is_async_revalidation) { |
+ handler = AddBufferedResourceHandler(request, handler.Pass()); |
+ } |
+ |
+ return AddThrottlesAndSchedule(request, request_data.resource_type, |
+ resource_context, filter_->appcache_service(), |
+ child_id, route_id, handler.Pass()); |
} |
-scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddStandardHandlers( |
+scoped_ptr<ResourceHandler> |
+ResourceDispatcherHostImpl::AddBufferedResourceHandler( |
net::URLRequest* request, |
- ResourceType resource_type, |
- ResourceContext* resource_context, |
- AppCacheService* appcache_service, |
- int child_id, |
- int route_id, |
scoped_ptr<ResourceHandler> handler) { |
- |
PluginService* plugin_service = nullptr; |
#if defined(ENABLE_PLUGINS) |
plugin_service = PluginService::GetInstance(); |
#endif |
// Insert a buffered event handler before the actual one. |
- handler.reset( |
- new BufferedResourceHandler( |
- handler.Pass(), this, plugin_service, request)); |
+ return make_scoped_ptr(new BufferedResourceHandler(handler.Pass(), this, |
+ plugin_service, request)); |
+} |
+scoped_ptr<ResourceHandler> ResourceDispatcherHostImpl::AddThrottlesAndSchedule( |
+ net::URLRequest* request, |
+ ResourceType resource_type, |
+ ResourceContext* resource_context, |
+ AppCacheService* appcache_service, |
+ int child_id, |
+ int route_id, |
+ scoped_ptr<ResourceHandler> handler) { |
ScopedVector<ResourceThrottle> throttles; |
if (delegate_) { |
delegate_->RequestBeginning(request, |
@@ -1553,7 +1628,9 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( |
blink::WebPageVisibilityStateVisible, |
context, |
base::WeakPtr<ResourceMessageFilter>(), // filter |
- true); // is_async |
+ true, // is_async |
+ false, // is_async_revalidation |
+ NULL); // original_request |
} |
void ResourceDispatcherHostImpl::OnRenderViewHostCreated(int child_id, |
@@ -1704,7 +1781,8 @@ void ResourceDispatcherHostImpl::CancelRequestsForRoute(int child_id, |
any_requests_transferring = true; |
if (info->detachable_handler()) { |
info->detachable_handler()->Detach(); |
- } else if (!info->IsDownload() && !info->is_stream() && |
+ } else if (!info->IsDownload() && |
+ !info->is_stream() && !info->IsAsyncRevalidation() && |
!IsTransferredNavigation(id) && |
(route_id == -1 || route_id == info->GetRouteID())) { |
matching_requests.push_back(id); |
@@ -1774,6 +1852,15 @@ void ResourceDispatcherHostImpl::RemovePendingRequest(int child_id, |
void ResourceDispatcherHostImpl::RemovePendingLoader( |
const LoaderMap::iterator& iter) { |
ResourceRequestInfoImpl* info = iter->second->GetRequestInfo(); |
+ if (info->IsAsyncRevalidation()) { |
+ net::URLRequest* request = iter->second->request(); |
+ auto async_revalidation_key = GenerateAsyncRevalidationKeyFor( |
+ request->context()->http_transaction_factory(), |
+ request->original_url()); |
+ bool erased = |
+ in_progress_async_revalidations_.erase(async_revalidation_key); |
+ DCHECK(erased); |
+ } |
// Remove the memory credit that we added when pushing the request onto |
// the pending list. |
@@ -1999,7 +2086,9 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( |
blink::WebPageVisibilityStateVisible, |
resource_context, |
base::WeakPtr<ResourceMessageFilter>(), // filter |
- true); |
+ true, // is_async |
+ false, // is_async_revalidation |
+ NULL); // original_message |
// Request takes ownership. |
extra_info->AssociateWithRequest(new_request.get()); |
@@ -2023,12 +2112,12 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( |
// TODO(davidben): Pass in the appropriate appcache_service. Also fix the |
// dependency on child_id/route_id. Those are used by the ResourceScheduler; |
// currently it's a no-op. |
- handler = AddStandardHandlers(new_request.get(), resource_type, |
- resource_context, |
- nullptr, // appcache_service |
- -1, // child_id |
- -1, // route_id |
- handler.Pass()); |
+ handler = AddThrottlesAndSchedule( |
+ new_request.get(), resource_type, resource_context, |
+ nullptr, // appcache_service |
+ -1, // child_id |
+ -1, // route_id |
+ AddBufferedResourceHandler(new_request.get(), handler.Pass())); |
BeginRequestInternal(new_request.Pass(), handler.Pass()); |
} |
@@ -2372,4 +2461,60 @@ int ResourceDispatcherHostImpl::BuildLoadFlagsForRequest( |
return load_flags; |
} |
+void ResourceDispatcherHostImpl::BeginAsyncRevalidation( |
+ net::URLRequest* for_request) { |
+ ResourceRequestInfoImpl* info = |
+ ResourceRequestInfoImpl::ForRequest(for_request); |
+ DCHECK(info); |
+ DCHECK(!info->IsAsyncRevalidation()); |
+ ResourceHostMsg_Request* original_request_data = info->original_request(); |
+ DCHECK(original_request_data); |
+ |
+ ResourceHostMsg_Request new_request_data = *original_request_data; |
+ new_request_data.do_not_prompt_for_login = true; |
+ new_request_data.allow_download = false; |
+ new_request_data.enable_load_timing = false; |
+ new_request_data.download_to_file = false; |
+ new_request_data.transferred_request_child_id = -1; |
+ new_request_data.transferred_request_request_id = -1; |
+ new_request_data.priority = net::MINIMUM_PRIORITY; |
+ |
+ int child_id = info->GetChildID(); |
+ int route_id = info->GetRouteID(); |
+ int request_id = --request_id_; |
+ |
+ ResourceContext* resource_context = NULL; |
+ net::URLRequestContext* request_context = NULL; |
+ // This |request_context| needs to be valid until |
+ // RemoveResourceContext(resource_context) is called. This is currently |
+ // correct, see |
+ // https://groups.google.com/a/chromium.org/d/msg/net-dev/lHh764ZFdr0/phJIWXokt7cJ |
+ info->filter()->GetContexts(new_request_data, &resource_context, |
+ &request_context); |
+ if (is_shutdown_ || (delegate_ && |
+ !delegate_->ShouldBeginRequest( |
+ new_request_data.method, new_request_data.url, |
+ new_request_data.resource_type, resource_context))) { |
+ return; |
+ } |
+ |
+ auto async_revalidation_key = GenerateAsyncRevalidationKeyFor( |
+ request_context->http_transaction_factory(), original_request_data->url); |
+ if (!in_progress_async_revalidations_.insert(async_revalidation_key).second) { |
+ // A matching async revalidation is already in progress for this cache; we |
+ // don't need another one. |
+ return; |
+ } |
+ CHECK(ContainsKey(active_resource_contexts_, resource_context)); |
+ |
+ // TODO(ricea): Make filter_ be passed around in a sane way. |
+ filter_ = info->filter(); |
+ BeginRequestFromData(request_id, new_request_data, |
+ NULL, // sync_result |
+ PROCESS_TYPE_BROWSER, child_id, route_id, |
+ true, // is_async_revalidation |
+ request_context, resource_context); |
+ filter_ = NULL; |
+} |
+ |
} // namespace content |