Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

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

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

Powered by Google App Engine
This is Rietveld 408576698