Chromium Code Reviews

Side by Side Diff: content/browser/service_worker/service_worker_context_request_handler.cc

Issue 2602853002: Eliminate network fallback from ServiceWorkerContextRequestHandler. (Closed)
Patch Set: rm class Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/service_worker/service_worker_context_request_handler. h" 5 #include "content/browser/service_worker/service_worker_context_request_handler. h"
6 6
7 #include "base/time/time.h" 7 #include "base/time/time.h"
8 #include "content/browser/service_worker/service_worker_context_core.h" 8 #include "content/browser/service_worker/service_worker_context_core.h"
9 #include "content/browser/service_worker/service_worker_provider_host.h" 9 #include "content/browser/service_worker/service_worker_provider_host.h"
10 #include "content/browser/service_worker/service_worker_read_from_cache_job.h" 10 #include "content/browser/service_worker/service_worker_read_from_cache_job.h"
11 #include "content/browser/service_worker/service_worker_storage.h" 11 #include "content/browser/service_worker/service_worker_storage.h"
12 #include "content/browser/service_worker/service_worker_version.h" 12 #include "content/browser/service_worker/service_worker_version.h"
13 #include "content/browser/service_worker/service_worker_write_to_cache_job.h" 13 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
14 #include "content/public/browser/resource_context.h"
15 #include "content/public/common/resource_response_info.h" 14 #include "content/public/common/resource_response_info.h"
16 #include "net/base/load_flags.h" 15 #include "net/base/load_flags.h"
16 #include "net/log/net_log.h"
17 #include "net/log/net_log_event_type.h"
18 #include "net/log/net_log_with_source.h"
17 #include "net/url_request/url_request.h" 19 #include "net/url_request/url_request.h"
20 #include "net/url_request/url_request_error_job.h"
18 21
19 namespace content { 22 namespace content {
20 23
24 namespace {
25
26 bool IsInstalled(const ServiceWorkerVersion* version) {
27 switch (version->status()) {
28 case ServiceWorkerVersion::NEW:
29 case ServiceWorkerVersion::INSTALLING:
30 return false;
31 case ServiceWorkerVersion::INSTALLED:
32 case ServiceWorkerVersion::ACTIVATING:
33 case ServiceWorkerVersion::ACTIVATED:
34 return true;
35 case ServiceWorkerVersion::REDUNDANT:
36 return false;
37 }
38 NOTREACHED();
39 return false;
40 }
41
42 } // namespace
43
21 ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler( 44 ServiceWorkerContextRequestHandler::ServiceWorkerContextRequestHandler(
22 base::WeakPtr<ServiceWorkerContextCore> context, 45 base::WeakPtr<ServiceWorkerContextCore> context,
23 base::WeakPtr<ServiceWorkerProviderHost> provider_host, 46 base::WeakPtr<ServiceWorkerProviderHost> provider_host,
24 base::WeakPtr<storage::BlobStorageContext> blob_storage_context, 47 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
25 ResourceType resource_type) 48 ResourceType resource_type)
26 : ServiceWorkerRequestHandler(context, 49 : ServiceWorkerRequestHandler(context,
27 provider_host, 50 provider_host,
28 blob_storage_context, 51 blob_storage_context,
29 resource_type), 52 resource_type),
30 version_(provider_host_->running_hosted_version()) { 53 version_(provider_host_->running_hosted_version()) {
31 DCHECK(provider_host_->IsHostToRunningServiceWorker()); 54 DCHECK(provider_host_->IsHostToRunningServiceWorker());
32 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) 55 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER)
33 version_->NotifyMainScriptRequestHandlerCreated(); 56 version_->NotifyMainScriptRequestHandlerCreated();
34 } 57 }
35 58
36 ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() { 59 ServiceWorkerContextRequestHandler::~ServiceWorkerContextRequestHandler() {
37 } 60 }
38 61
62 // static
63 std::string ServiceWorkerContextRequestHandler::CreateJobStatusToString(
64 CreateJobStatus status) {
65 switch (status) {
66 case CreateJobStatus::UNINITIALIZED:
67 return "UNINITIALIZED";
68 case CreateJobStatus::WRITE_JOB_FOR_REGISTER:
69 return "WRITE_JOB_FOR_REGISTER";
70 case CreateJobStatus::WRITE_JOB_FOR_UPDATE:
71 return "WRITE_JOB_FOR_UPDATE";
72 case CreateJobStatus::READ_JOB:
73 return "READ_JOB";
74 case CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT:
75 return "READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT";
76 case CreateJobStatus::ERROR_NO_PROVIDER:
77 return "ERROR_NO_PROVIDER";
78 case CreateJobStatus::ERROR_NO_VERSION:
79 return "ERROR_NO_VERSION";
80 case CreateJobStatus::ERROR_REDUNDANT_VERSION:
81 return "ERROR_REDUNDANT_VERSION";
82 case CreateJobStatus::ERROR_NO_CONTEXT:
83 return "ERROR_NO_CONTEXT";
84 case CreateJobStatus::ERROR_REDIRECT:
85 return "ERROR_REDIRECT";
86 case CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT:
87 return "ERROR_UNINSTALLED_SCRIPT_IMPORT";
88 case CreateJobStatus::ERROR_OUT_OF_RESOURCE_IDS:
89 return "ERROR_OUT_OF_RESOURCE_IDS";
90 case CreateJobStatus::NUM_TYPES:
91 NOTREACHED();
92 }
93 NOTREACHED() << static_cast<int>(status);
94 return "UNKNOWN";
95 }
96
39 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob( 97 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob(
40 net::URLRequest* request, 98 net::URLRequest* request,
41 net::NetworkDelegate* network_delegate, 99 net::NetworkDelegate* network_delegate,
42 ResourceContext* resource_context) { 100 ResourceContext* resource_context) {
43 CreateJobStatus status = CreateJobStatus::DID_NOT_SET_STATUS; 101 // We only use the script cache for main script loading and
102 // importScripts(), even if a cached script is xhr'd, we don't
103 // retrieve it from the script cache.
104 // TODO(falken): Get the desired behavior clarified in the spec,
105 // and make tweak the behavior here to match.
106 if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER &&
107 resource_type_ != RESOURCE_TYPE_SCRIPT) {
108 // Fall back to network.
109 return nullptr;
110 }
111
112 CreateJobStatus status = CreateJobStatus::UNINITIALIZED;
44 net::URLRequestJob* job = 113 net::URLRequestJob* job =
45 MaybeCreateJobImpl(&status, request, network_delegate, resource_context); 114 MaybeCreateJobImpl(request, network_delegate, &status);
46 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) { 115 const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER;
116 // TODO(falken): Add UMA for CreateJobStatus.
117 if (is_main_script)
47 version_->NotifyMainScriptJobCreated(status); 118 version_->NotifyMainScriptJobCreated(status);
119 if (job)
120 return job;
121
122 // If we got here, a job couldn't be created. Return an error job rather than
123 // falling back to network. Otherwise the renderer may receive the response
124 // from network and start a service worker whose browser-side
125 // ServiceWorkerVersion is not properly initialized.
126 //
127 // As an exception, allow installed service workers to use importScripts()
128 // to import non-installed scripts.
129 // TODO(falken): This is a spec violation that should be deprecated and
130 // removed. See https://github.com/w3c/ServiceWorker/issues/1021
131 if (status == CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT) {
132 // Fall back to network.
133 return nullptr;
48 } 134 }
49 return job; 135
136 std::string error_str(CreateJobStatusToString(status));
137 request->net_log().AddEvent(
138 net::NetLogEventType::SERVICE_WORKER_SCRIPT_LOAD_UNHANDLED_REQUEST_ERROR,
139 net::NetLog::StringCallback("error", &error_str));
140
141 return new net::URLRequestErrorJob(request, network_delegate,
142 net::Error::ERR_FAILED);
50 } 143 }
51 144
52 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJobImpl( 145 net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJobImpl(
53 CreateJobStatus* out_status,
54 net::URLRequest* request, 146 net::URLRequest* request,
55 net::NetworkDelegate* network_delegate, 147 net::NetworkDelegate* network_delegate,
56 ResourceContext* resource_context) { 148 CreateJobStatus* out_status) {
57 if (!provider_host_) { 149 if (!provider_host_) {
58 *out_status = CreateJobStatus::NO_PROVIDER; 150 *out_status = CreateJobStatus::ERROR_NO_PROVIDER;
151 return nullptr;
152 }
153 if (!context_) {
154 *out_status = CreateJobStatus::ERROR_NO_CONTEXT;
59 return nullptr; 155 return nullptr;
60 } 156 }
61 if (!version_) { 157 if (!version_) {
62 *out_status = CreateJobStatus::NO_VERSION; 158 *out_status = CreateJobStatus::ERROR_NO_VERSION;
63 return nullptr; 159 return nullptr;
64 } 160 }
65 if (!context_) { 161 // This could happen if browser-side has set the status to redundant but the
66 *out_status = CreateJobStatus::NO_CONTEXT; 162 // worker has not yet stopped. The worker is already doomed so just reject the
163 // request. Handle it specially here because otherwise it'd be unclear whether
164 // "REDUNDANT" should count as installed or not installed when making
165 // decisions about how to handle the request and logging UMA.
166 if (version_->status() == ServiceWorkerVersion::REDUNDANT) {
167 *out_status = CreateJobStatus::ERROR_REDUNDANT_VERSION;
67 return nullptr; 168 return nullptr;
68 } 169 }
69 170
70 // We currently have no use case for hijacking a redirected request. 171 // We currently have no use case for hijacking a redirected request.
71 if (request->url_chain().size() > 1) { 172 if (request->url_chain().size() > 1) {
72 *out_status = CreateJobStatus::IGNORE_REDIRECT; 173 *out_status = CreateJobStatus::ERROR_REDIRECT;
73 return nullptr; 174 return nullptr;
74 } 175 }
75 176
76 // We only use the script cache for main script loading and 177 const bool is_main_script = resource_type_ == RESOURCE_TYPE_SERVICE_WORKER;
77 // importScripts(), even if a cached script is xhr'd, we don't 178 int resource_id =
78 // retrieve it from the script cache. 179 version_->script_cache_map()->LookupResourceId(request->url());
79 // TODO(michaeln): Get the desired behavior clarified in the spec, 180 if (resource_id != kInvalidServiceWorkerResourceId) {
80 // and make tweak the behavior here to match. 181 if (IsInstalled(version_.get())) {
81 if (resource_type_ != RESOURCE_TYPE_SERVICE_WORKER && 182 // An installed worker is loading a stored script.
82 resource_type_ != RESOURCE_TYPE_SCRIPT) { 183 if (is_main_script)
83 *out_status = CreateJobStatus::IGNORE_NON_SCRIPT; 184 version_->embedded_worker()->OnURLJobCreatedForMainScript();
84 return nullptr; 185 *out_status = CreateJobStatus::READ_JOB;
85 } 186 } else {
86 187 // A new worker is loading a stored script. The script was already
87 if (ShouldAddToScriptCache(request->url())) { 188 // imported (or the main script is being recursively imported).
88 ServiceWorkerRegistration* registration = 189 *out_status = CreateJobStatus::READ_JOB_FOR_DUPLICATE_SCRIPT_IMPORT;
89 context_->GetLiveRegistration(version_->registration_id());
90 DCHECK(registration); // We're registering or updating so must be there.
91
92 int64_t resource_id = context_->storage()->NewResourceId();
93 if (resource_id == kInvalidServiceWorkerResourceId) {
94 *out_status = CreateJobStatus::OUT_OF_RESOURCE_IDS;
95 return nullptr;
96 } 190 }
97
98 // Bypass the browser cache for initial installs and update
99 // checks after 24 hours have passed.
100 int extra_load_flags = 0;
101 base::TimeDelta time_since_last_check =
102 base::Time::Now() - registration->last_update_check();
103 if (time_since_last_check > base::TimeDelta::FromHours(
104 kServiceWorkerScriptMaxCacheAgeInHours) ||
105 version_->force_bypass_cache_for_scripts()) {
106 extra_load_flags = net::LOAD_BYPASS_CACHE;
107 }
108
109 ServiceWorkerVersion* stored_version = registration->waiting_version()
110 ? registration->waiting_version()
111 : registration->active_version();
112 int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId;
113 if (stored_version && stored_version->script_url() == request->url()) {
114 incumbent_resource_id =
115 stored_version->script_cache_map()->LookupResourceId(request->url());
116 }
117 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER)
118 version_->embedded_worker()->OnURLJobCreatedForMainScript();
119 *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId
120 ? CreateJobStatus::CREATED_WRITE_JOB_FOR_REGISTER
121 : CreateJobStatus::CREATED_WRITE_JOB_FOR_UPDATE;
122 return new ServiceWorkerWriteToCacheJob(
123 request, network_delegate, resource_type_, context_, version_.get(),
124 extra_load_flags, resource_id, incumbent_resource_id);
125 }
126
127 int64_t resource_id = kInvalidServiceWorkerResourceId;
128 if (ShouldReadFromScriptCache(request->url(), &resource_id)) {
129 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER)
130 version_->embedded_worker()->OnURLJobCreatedForMainScript();
131 *out_status = CreateJobStatus::CREATED_READ_JOB;
132 return new ServiceWorkerReadFromCacheJob(request, network_delegate, 191 return new ServiceWorkerReadFromCacheJob(request, network_delegate,
133 resource_type_, context_, version_, 192 resource_type_, context_, version_,
134 resource_id); 193 resource_id);
135 } 194 }
136 195
137 // NULL means use the network. 196 // An installed worker is importing a non-stored script.
138 *out_status = CreateJobStatus::IGNORE_UNKNOWN; 197 if (IsInstalled(version_.get())) {
139 return nullptr; 198 DCHECK(!is_main_script);
140 } 199 *out_status = CreateJobStatus::ERROR_UNINSTALLED_SCRIPT_IMPORT;
200 return nullptr;
201 }
141 202
142 bool ServiceWorkerContextRequestHandler::ShouldAddToScriptCache( 203 // A new worker is loading a script for the first time. Create a write job to
143 const GURL& url) { 204 // store the script.
144 // We only write imports that occur during the initial eval. 205 ServiceWorkerRegistration* registration =
145 if (version_->status() != ServiceWorkerVersion::NEW && 206 context_->GetLiveRegistration(version_->registration_id());
146 version_->status() != ServiceWorkerVersion::INSTALLING) { 207 DCHECK(registration); // We're registering or updating so must be there.
147 return false; 208
209 resource_id = context_->storage()->NewResourceId();
210 if (resource_id == kInvalidServiceWorkerResourceId) {
211 *out_status = CreateJobStatus::ERROR_OUT_OF_RESOURCE_IDS;
212 return nullptr;
148 } 213 }
149 return version_->script_cache_map()->LookupResourceId(url) ==
150 kInvalidServiceWorkerResourceId;
151 }
152 214
153 bool ServiceWorkerContextRequestHandler::ShouldReadFromScriptCache( 215 // Bypass the browser cache for initial installs and update checks after 24
154 const GURL& url, 216 // hours have passed.
155 int64_t* resource_id_out) { 217 int extra_load_flags = 0;
156 // We don't read from the script cache until the version is INSTALLED. 218 base::TimeDelta time_since_last_check =
157 if (version_->status() == ServiceWorkerVersion::NEW || 219 base::Time::Now() - registration->last_update_check();
158 version_->status() == ServiceWorkerVersion::INSTALLING) 220 if (time_since_last_check >
159 return false; 221 base::TimeDelta::FromHours(kServiceWorkerScriptMaxCacheAgeInHours) ||
160 *resource_id_out = version_->script_cache_map()->LookupResourceId(url); 222 version_->force_bypass_cache_for_scripts()) {
161 return *resource_id_out != kInvalidServiceWorkerResourceId; 223 extra_load_flags = net::LOAD_BYPASS_CACHE;
224 }
225
226 ServiceWorkerVersion* stored_version = registration->waiting_version()
227 ? registration->waiting_version()
228 : registration->active_version();
229 int64_t incumbent_resource_id = kInvalidServiceWorkerResourceId;
230 if (stored_version && stored_version->script_url() == request->url()) {
231 incumbent_resource_id =
232 stored_version->script_cache_map()->LookupResourceId(request->url());
233 }
234 if (is_main_script)
235 version_->embedded_worker()->OnURLJobCreatedForMainScript();
236 *out_status = incumbent_resource_id == kInvalidServiceWorkerResourceId
237 ? CreateJobStatus::WRITE_JOB_FOR_REGISTER
238 : CreateJobStatus::WRITE_JOB_FOR_UPDATE;
239 return new ServiceWorkerWriteToCacheJob(
240 request, network_delegate, resource_type_, context_, version_.get(),
241 extra_load_flags, resource_id, incumbent_resource_id);
162 } 242 }
163 243
164 } // namespace content 244 } // namespace content
OLDNEW

Powered by Google App Engine