Chromium Code Reviews| 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 |