1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/service_worker/service_worker_new_script_loader.h"
6
7 #include <map>
8 #include <memory>
9 #include <string>
10 #include <utility>
11 #include "base/bind_helpers.h"
12 #include "base/run_loop.h"
13 #include "base/strings/string_util.h"
14 #include "base/test/metrics/histogram_tester.h"
15 #include "base/test/scoped_feature_list.h"
16 #include "content/browser/service_worker/embedded_worker_test_helper.h"
17 #include "content/browser/service_worker/service_worker_consts.h"
18 #include "content/browser/service_worker/service_worker_context_core.h"
19 #include "content/browser/service_worker/service_worker_disk_cache.h"
20 #include "content/browser/service_worker/service_worker_test_utils.h"
21 #include "content/browser/url_loader_factory_getter.h"
22 #include "content/public/test/browser_task_environment.h"
23 #include "content/public/test/url_loader_interceptor.h"
24 #include "mojo/public/cpp/system/data_pipe_utils.h"
25 #include "net/base/features.h"
26 #include "net/base/load_flags.h"
27 #include "net/base/test_completion_callback.h"
28 #include "net/http/http_util.h"
29 #include "net/traffic_annotation/network_traffic_annotation.h"
30 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
31 #include "net/url_request/redirect_info.h"
32 #include "services/network/public/cpp/url_loader_completion_status.h"
33 #include "services/network/public/mojom/url_loader_factory.mojom.h"
34 #include "services/network/test/test_url_loader_client.h"
35 #include "third_party/blink/public/common/features.h"
36 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
37
38 namespace content {
39 namespace service_worker_new_script_loader_unittest {
40
41 const char kNormalScriptURL[] = "https://example.com/normal.js";
42 const char kNormalImportedScriptURL[] =
43 "https://my-awesome-cdn.com/import_script.js";
44 const char kHistogramWriteResponseResult[] =
45 "ServiceWorker.DiskCache.WriteResponseResult";
46
47 // MockHTTPServer is a utility to provide mocked responses for
48 // ServiceWorkerNewScriptLoader.
49 class MockHTTPServer {
50 public:
51 struct Response {
Responsecontent::service_worker_new_script_loader_unittest::MockHTTPServer::Response52 Response(const std::string& headers, const std::string& body)
53 : headers(headers), body(body) {}
54
55 const std::string headers;
56 const std::string body;
57 bool has_certificate_error = false;
58 net::CertStatus cert_status = 0;
59 };
60
Set(const GURL & url,const Response & response)61 void Set(const GURL& url, const Response& response) {
62 responses_.erase(url);
63 responses_.emplace(url, response);
64 }
65
Get(const GURL & url)66 const Response& Get(const GURL& url) {
67 auto found = responses_.find(url);
68 EXPECT_TRUE(found != responses_.end());
69 return found->second;
70 }
71
72 private:
73 std::map<GURL, Response> responses_;
74 };
75
76 // Mocks network activity. Used by URLLoaderInterceptor.
77 class MockNetwork {
78 public:
MockNetwork(MockHTTPServer * mock_server)79 explicit MockNetwork(MockHTTPServer* mock_server)
80 : mock_server_(mock_server) {}
81
82 MockNetwork(const MockNetwork&) = delete;
83 MockNetwork& operator=(const MockNetwork&) = delete;
84
set_to_access_network(bool access_network)85 void set_to_access_network(bool access_network) {
86 access_network_ = access_network;
87 }
88
last_request() const89 network::ResourceRequest last_request() const { return last_request_; }
90
InterceptNetworkRequest(URLLoaderInterceptor::RequestParams * params)91 bool InterceptNetworkRequest(URLLoaderInterceptor::RequestParams* params) {
92 const network::ResourceRequest& url_request = params->url_request;
93 last_request_ = url_request;
94 const MockHTTPServer::Response& response =
95 mock_server_->Get(url_request.url);
96
97 // Pass the response header to the client.
98 net::HttpResponseInfo info;
99 info.headers = base::MakeRefCounted<net::HttpResponseHeaders>(
100 net::HttpUtil::AssembleRawHeaders(response.headers));
101 auto response_head = network::mojom::URLResponseHead::New();
102 response_head->headers = info.headers;
103 response_head->headers->GetMimeType(&response_head->mime_type);
104 response_head->network_accessed = access_network_;
105 if (response.has_certificate_error) {
106 response_head->cert_status = response.cert_status;
107 }
108
109 mojo::Remote<network::mojom::URLLoaderClient>& client = params->client;
110 if (response_head->headers->response_code() == 307) {
111 client->OnReceiveRedirect(net::RedirectInfo(), std::move(response_head));
112 return true;
113 }
114 client->OnReceiveResponse(std::move(response_head));
115
116 uint32_t bytes_written = response.body.size();
117 mojo::ScopedDataPipeConsumerHandle consumer;
118 mojo::ScopedDataPipeProducerHandle producer;
119 CHECK_EQ(MOJO_RESULT_OK,
120 mojo::CreateDataPipe(nullptr, &producer, &consumer));
121 MojoResult result = producer->WriteData(
122 response.body.data(), &bytes_written, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
123 CHECK_EQ(MOJO_RESULT_OK, result);
124 client->OnStartLoadingResponseBody(std::move(consumer));
125
126 network::URLLoaderCompletionStatus status;
127 status.error_code = net::OK;
128 client->OnComplete(status);
129 return true;
130 }
131
132 private:
133 // |mock_server_| is owned by ServiceWorkerNewScriptLoaderTest.
134 MockHTTPServer* const mock_server_;
135
136 // The most recent request received.
137 network::ResourceRequest last_request_;
138
139 // Controls whether a load simulates accessing network or cache.
140 bool access_network_ = false;
141 };
142
143 // ServiceWorkerNewScriptLoaderTest is for testing the handling of requests for
144 // installing service worker scripts via ServiceWorkerNewScriptLoader.
145 class ServiceWorkerNewScriptLoaderTest : public testing::Test {
146 public:
ServiceWorkerNewScriptLoaderTest()147 ServiceWorkerNewScriptLoaderTest()
148 : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP),
149 mock_network_(&mock_server_),
150 interceptor_(base::BindRepeating(&MockNetwork::InterceptNetworkRequest,
151 base::Unretained(&mock_network_))) {}
152 ~ServiceWorkerNewScriptLoaderTest() override = default;
153
context()154 ServiceWorkerContextCore* context() { return helper_->context(); }
155
SetUp()156 void SetUp() override {
157 helper_ = std::make_unique<EmbeddedWorkerTestHelper>(base::FilePath());
158
159 context()->storage()->LazyInitializeForTest();
160
161 mock_server_.Set(GURL(kNormalScriptURL),
162 MockHTTPServer::Response(
163 std::string("HTTP/1.1 200 OK\n"
164 "Content-Type: text/javascript\n\n"),
165 std::string("this body came from the network")));
166 mock_server_.Set(
167 GURL(kNormalImportedScriptURL),
168 MockHTTPServer::Response(
169 std::string("HTTP/1.1 200 OK\n"
170 "Content-Type: text/javascript\n\n"),
171 std::string(
172 "this is an import script response body from the network")));
173 }
174
175 // Sets up ServiceWorkerRegistration and ServiceWorkerVersion. This should be
176 // called before DoRequest().
SetUpRegistration(const GURL & script_url)177 void SetUpRegistration(const GURL& script_url) {
178 blink::mojom::ServiceWorkerRegistrationOptions options;
179 options.scope = script_url.GetWithoutFilename();
180 SetUpRegistrationWithOptions(script_url, options);
181 }
SetUpRegistrationWithOptions(const GURL & script_url,blink::mojom::ServiceWorkerRegistrationOptions options)182 void SetUpRegistrationWithOptions(
183 const GURL& script_url,
184 blink::mojom::ServiceWorkerRegistrationOptions options) {
185 registration_ =
186 CreateNewServiceWorkerRegistration(context()->registry(), options);
187 SetUpVersion(script_url);
188 }
189
190 // Promotes |version_| to |registration_|'s active version, and then resets
191 // |version_| to null (as subsequent DoRequest() calls should not attempt to
192 // install or update |version_|).
ActivateVersion()193 void ActivateVersion() {
194 version_->set_fetch_handler_existence(
195 ServiceWorkerVersion::FetchHandlerExistence::DOES_NOT_EXIST);
196 version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
197 registration_->SetActiveVersion(version_);
198 version_ = nullptr;
199 }
200
201 // After this is called, |version_| will be a new, uninstalled version. The
202 // next time DoRequest() is called, |version_| will attempt to install,
203 // possibly updating if registration has an installed worker.
SetUpVersion(const GURL & script_url)204 void SetUpVersion(const GURL& script_url) {
205 version_ = CreateNewServiceWorkerVersion(
206 context()->registry(), registration_.get(), script_url,
207 blink::mojom::ScriptType::kClassic);
208 version_->SetStatus(ServiceWorkerVersion::NEW);
209 }
210
DoRequest(const GURL & url,std::unique_ptr<network::TestURLLoaderClient> * out_client,std::unique_ptr<ServiceWorkerNewScriptLoader> * out_loader)211 void DoRequest(const GURL& url,
212 std::unique_ptr<network::TestURLLoaderClient>* out_client,
213 std::unique_ptr<ServiceWorkerNewScriptLoader>* out_loader) {
214 DCHECK(registration_);
215 DCHECK(version_);
216
217 // Dummy values.
218 int routing_id = 0;
219 int request_id = 10;
220 uint32_t options = 0;
221 int64_t resource_id = GetNewResourceIdSync(context()->storage());
222
223 network::ResourceRequest request;
224 request.url = url;
225 request.method = "GET";
226 request.resource_type =
227 static_cast<int>((url == version_->script_url())
228 ? blink::mojom::ResourceType::kServiceWorker
229 : blink::mojom::ResourceType::kScript);
230
231 *out_client = std::make_unique<network::TestURLLoaderClient>();
232 *out_loader = ServiceWorkerNewScriptLoader::CreateAndStart(
233 routing_id, request_id, options, request, (*out_client)->CreateRemote(),
234 version_, helper_->url_loader_factory_getter()->GetNetworkFactory(),
235 net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS),
236 resource_id);
237 }
238
239 // Returns false if the entry for |url| doesn't exist in the storage.
VerifyStoredResponse(const GURL & url)240 bool VerifyStoredResponse(const GURL& url) {
241 return ServiceWorkerUpdateCheckTestUtils::VerifyStoredResponse(
242 LookupResourceId(url), context()->storage(),
243 mock_server_.Get(url).body);
244 }
245
LookupResourceId(const GURL & url)246 int64_t LookupResourceId(const GURL& url) {
247 return version_->script_cache_map()->LookupResourceId(url);
248 }
249
250 protected:
251 BrowserTaskEnvironment task_environment_;
252
253 MockHTTPServer mock_server_;
254 MockNetwork mock_network_;
255 URLLoaderInterceptor interceptor_;
256
257 std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
258
259 scoped_refptr<ServiceWorkerRegistration> registration_;
260 scoped_refptr<ServiceWorkerVersion> version_;
261 };
262
TEST_F(ServiceWorkerNewScriptLoaderTest,Success)263 TEST_F(ServiceWorkerNewScriptLoaderTest, Success) {
264 base::HistogramTester histogram_tester;
265
266 const GURL kScriptURL(kNormalScriptURL);
267 std::unique_ptr<network::TestURLLoaderClient> client;
268 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
269
270 SetUpRegistration(kScriptURL);
271 DoRequest(kScriptURL, &client, &loader);
272 client->RunUntilComplete();
273 EXPECT_EQ(net::OK, client->completion_status().error_code);
274
275 // The client should have received the response.
276 EXPECT_TRUE(client->has_received_response());
277 EXPECT_TRUE(client->response_body().is_valid());
278 std::string response;
279 EXPECT_TRUE(
280 mojo::BlockingCopyToString(client->response_body_release(), &response));
281 EXPECT_EQ(mock_server_.Get(kScriptURL).body, response);
282
283 // WRITE_OK should be recorded once plus one as we record a single write
284 // success and the end of the body.
285 EXPECT_TRUE(VerifyStoredResponse(kScriptURL));
286 histogram_tester.ExpectUniqueSample(kHistogramWriteResponseResult,
287 ServiceWorkerMetrics::WRITE_OK, 2);
288 }
289
TEST_F(ServiceWorkerNewScriptLoaderTest,Success_EmptyBody)290 TEST_F(ServiceWorkerNewScriptLoaderTest, Success_EmptyBody) {
291 base::HistogramTester histogram_tester;
292
293 const GURL kScriptURL("https://example.com/empty.js");
294 std::unique_ptr<network::TestURLLoaderClient> client;
295 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
296 mock_server_.Set(
297 kScriptURL,
298 MockHTTPServer::Response(std::string("HTTP/1.1 200 OK\n"
299 "Content-Type: text/javascript\n\n"),
300 std::string()));
301 SetUpRegistration(kScriptURL);
302 DoRequest(kScriptURL, &client, &loader);
303 client->RunUntilComplete();
304 EXPECT_EQ(net::OK, client->completion_status().error_code);
305
306 // The client should have received the response.
307 EXPECT_TRUE(client->has_received_response());
308 EXPECT_TRUE(client->response_body().is_valid());
309
310 // The response should also be stored in the storage.
311 EXPECT_TRUE(VerifyStoredResponse(kScriptURL));
312 // WRITE_OK should be recorded once as we record the end of the body.
313 histogram_tester.ExpectUniqueSample(kHistogramWriteResponseResult,
314 ServiceWorkerMetrics::WRITE_OK, 1);
315 }
316
TEST_F(ServiceWorkerNewScriptLoaderTest,Success_LargeBody)317 TEST_F(ServiceWorkerNewScriptLoaderTest, Success_LargeBody) {
318 base::HistogramTester histogram_tester;
319
320 std::unique_ptr<network::TestURLLoaderClient> client;
321 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
322
323 // Create a response that has a larger body than the script loader's buffer
324 // to test chunked data write. We chose this multiplier to avoid hitting the
325 // limit of mojo's data pipe buffer (it's about kReadBufferSize * 2 as of
326 // now).
327 const uint32_t kBodySize =
328 ServiceWorkerNewScriptLoader::kReadBufferSize * 1.6;
329 const GURL kScriptURL("https://example.com/large-body.js");
330 mock_server_.Set(
331 kScriptURL,
332 MockHTTPServer::Response(std::string("HTTP/1.1 200 OK\n"
333 "Content-Type: text/javascript\n\n"),
334 std::string(kBodySize, 'a')));
335 SetUpRegistration(kScriptURL);
336 DoRequest(kScriptURL, &client, &loader);
337 client->RunUntilComplete();
338 EXPECT_EQ(net::OK, client->completion_status().error_code);
339
340 // The client should have received the response.
341 EXPECT_TRUE(client->has_received_response());
342 EXPECT_TRUE(client->response_body().is_valid());
343 std::string response;
344 EXPECT_TRUE(
345 mojo::BlockingCopyToString(client->response_body_release(), &response));
346 EXPECT_EQ(mock_server_.Get(kScriptURL).body, response);
347
348 // The response should also be stored in the storage.
349 EXPECT_TRUE(VerifyStoredResponse(kScriptURL));
350 // WRITE_OK should be recorded twice plus one as we record every single write
351 // success and the end of the body.
352 histogram_tester.ExpectUniqueSample(kHistogramWriteResponseResult,
353 ServiceWorkerMetrics::WRITE_OK, 3);
354 }
355
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_404)356 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_404) {
357 base::HistogramTester histogram_tester;
358
359 std::unique_ptr<network::TestURLLoaderClient> client;
360 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
361
362 const GURL kScriptURL("https://example.com/nonexistent.js");
363 mock_server_.Set(kScriptURL, MockHTTPServer::Response(
364 std::string("HTTP/1.1 404 Not Found\n\n"),
365 std::string()));
366 SetUpRegistration(kScriptURL);
367 DoRequest(kScriptURL, &client, &loader);
368 client->RunUntilComplete();
369
370 // The request should be failed because of the 404 response.
371 EXPECT_EQ(net::ERR_INVALID_RESPONSE, client->completion_status().error_code);
372 EXPECT_FALSE(client->has_received_response());
373
374 // The response shouldn't be stored in the storage.
375 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
376 // No sample should be recorded since a write didn't occur.
377 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
378 }
379
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_Redirect)380 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_Redirect) {
381 base::HistogramTester histogram_tester;
382
383 std::unique_ptr<network::TestURLLoaderClient> client;
384 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
385
386 const GURL kScriptURL("https://example.com/redirect.js");
387 mock_server_.Set(
388 kScriptURL,
389 MockHTTPServer::Response(
390 std::string("HTTP/1.1 307 Temporary Redirect\n\n"), std::string()));
391 SetUpRegistration(kScriptURL);
392 DoRequest(kScriptURL, &client, &loader);
393 client->RunUntilComplete();
394
395 // The request should be failed because of the redirected response.
396 EXPECT_EQ(net::ERR_UNSAFE_REDIRECT, client->completion_status().error_code);
397 EXPECT_FALSE(client->has_received_response());
398
399 // The response shouldn't be stored in the storage.
400 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
401 // No sample should be recorded since a write didn't occur.
402 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
403 }
404
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_CertificateError)405 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_CertificateError) {
406 base::HistogramTester histogram_tester;
407
408 std::unique_ptr<network::TestURLLoaderClient> client;
409 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
410
411 // Serve a response with a certificate error.
412 const GURL kScriptURL("https://example.com/certificate-error.js");
413 MockHTTPServer::Response response(std::string("HTTP/1.1 200 OK\n\n"),
414 std::string("body"));
415 response.has_certificate_error = true;
416 response.cert_status = net::CERT_STATUS_DATE_INVALID;
417 mock_server_.Set(kScriptURL, response);
418 SetUpRegistration(kScriptURL);
419 DoRequest(kScriptURL, &client, &loader);
420 client->RunUntilComplete();
421
422 // The request should be failed because of the response with the certificate
423 // error.
424 EXPECT_EQ(net::ERR_CERT_DATE_INVALID, client->completion_status().error_code);
425 EXPECT_FALSE(client->has_received_response());
426
427 // The response shouldn't be stored in the storage.
428 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
429 // No sample should be recorded since a write didn't occur.
430 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
431 }
432
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_NoMimeType)433 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_NoMimeType) {
434 base::HistogramTester histogram_tester;
435
436 std::unique_ptr<network::TestURLLoaderClient> client;
437 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
438
439 const GURL kScriptURL("https://example.com/no-mime-type.js");
440 mock_server_.Set(kScriptURL, MockHTTPServer::Response(
441 std::string("HTTP/1.1 200 OK\n\n"),
442 std::string("body with no MIME type")));
443 SetUpRegistration(kScriptURL);
444 DoRequest(kScriptURL, &client, &loader);
445 client->RunUntilComplete();
446
447 // The request should be failed because of the response with no MIME type.
448 EXPECT_EQ(net::ERR_INSECURE_RESPONSE, client->completion_status().error_code);
449 EXPECT_FALSE(client->has_received_response());
450
451 // The response shouldn't be stored in the storage.
452 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
453 // No sample should be recorded since a write didn't occur.
454 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
455 }
456
457 class ServiceWorkerNewScriptLoaderTestWithLegacyTLSEnforced
458 : public ServiceWorkerNewScriptLoaderTest {
459 public:
ServiceWorkerNewScriptLoaderTestWithLegacyTLSEnforced()460 ServiceWorkerNewScriptLoaderTestWithLegacyTLSEnforced() {
461 feature_list_.InitAndEnableFeature(net::features::kLegacyTLSEnforced);
462 }
463
464 private:
465 base::test::ScopedFeatureList feature_list_;
466 };
467
468 // Tests that service workers fail to load over a connection with legacy TLS.
TEST_F(ServiceWorkerNewScriptLoaderTestWithLegacyTLSEnforced,Error_LegacyTLS)469 TEST_F(ServiceWorkerNewScriptLoaderTestWithLegacyTLSEnforced, Error_LegacyTLS) {
470 base::HistogramTester histogram_tester;
471
472 std::unique_ptr<network::TestURLLoaderClient> client;
473 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
474
475 // Serve a response with a certificate error.
476 const GURL kScriptURL("https://example.com/certificate-error.js");
477 MockHTTPServer::Response response(std::string("HTTP/1.1 200 OK\n\n"),
478 std::string("body"));
479 response.has_certificate_error = true;
480 response.cert_status = net::CERT_STATUS_LEGACY_TLS;
481 mock_server_.Set(kScriptURL, response);
482 SetUpRegistration(kScriptURL);
483 DoRequest(kScriptURL, &client, &loader);
484 client->RunUntilComplete();
485
486 // The request should be failed because of the response with the legacy TLS
487 // error.
488 EXPECT_EQ(net::ERR_SSL_OBSOLETE_VERSION,
489 client->completion_status().error_code);
490 EXPECT_FALSE(client->has_received_response());
491
492 // The response shouldn't be stored in the storage.
493 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
494 // No sample should be recorded since a write didn't occur.
495 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
496 }
497
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_BadMimeType)498 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_BadMimeType) {
499 base::HistogramTester histogram_tester;
500
501 std::unique_ptr<network::TestURLLoaderClient> client;
502 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
503
504 const GURL kScriptURL("https://example.com/bad-mime-type.js");
505 mock_server_.Set(kScriptURL, MockHTTPServer::Response(
506 std::string("HTTP/1.1 200 OK\n"
507 "Content-Type: text/css\n\n"),
508 std::string("body with bad MIME type")));
509 SetUpRegistration(kScriptURL);
510 DoRequest(kScriptURL, &client, &loader);
511 client->RunUntilComplete();
512
513 // The request should be failed because of the response with the bad MIME
514 // type.
515 EXPECT_EQ(net::ERR_INSECURE_RESPONSE, client->completion_status().error_code);
516 EXPECT_FALSE(client->has_received_response());
517
518 // The response shouldn't be stored in the storage.
519 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
520 // No sample should be recorded since a write didn't occur.
521 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
522 }
523
TEST_F(ServiceWorkerNewScriptLoaderTest,Success_PathRestriction)524 TEST_F(ServiceWorkerNewScriptLoaderTest, Success_PathRestriction) {
525 base::HistogramTester histogram_tester;
526
527 std::unique_ptr<network::TestURLLoaderClient> client;
528 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
529
530 // |kScope| is not under the default scope ("/out-of-scope/"), but the
531 // Service-Worker-Allowed header allows it.
532 const GURL kScriptURL("https://example.com/out-of-scope/normal.js");
533 const GURL kScope("https://example.com/in-scope/");
534 mock_server_.Set(kScriptURL,
535 MockHTTPServer::Response(
536 std::string("HTTP/1.1 200 OK\n"
537 "Content-Type: text/javascript\n"
538 "Service-Worker-Allowed: /in-scope/\n\n"),
539 std::string("٩( ’ω’ )و I'm body!")));
540 blink::mojom::ServiceWorkerRegistrationOptions options;
541 options.scope = kScope;
542 SetUpRegistrationWithOptions(kScriptURL, options);
543 DoRequest(kScriptURL, &client, &loader);
544 client->RunUntilComplete();
545 EXPECT_EQ(net::OK, client->completion_status().error_code);
546
547 // The client should have received the response.
548 EXPECT_TRUE(client->has_received_response());
549 EXPECT_TRUE(client->response_body().is_valid());
550 std::string response;
551 EXPECT_TRUE(
552 mojo::BlockingCopyToString(client->response_body_release(), &response));
553 EXPECT_EQ(mock_server_.Get(kScriptURL).body, response);
554
555 // WRITE_OK should be recorded once plus one as we record a single write
556 // success and the end of the body.
557 EXPECT_TRUE(VerifyStoredResponse(kScriptURL));
558 histogram_tester.ExpectUniqueSample(kHistogramWriteResponseResult,
559 ServiceWorkerMetrics::WRITE_OK, 2);
560 }
561
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_PathRestriction)562 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_PathRestriction) {
563 base::HistogramTester histogram_tester;
564
565 std::unique_ptr<network::TestURLLoaderClient> client;
566 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
567
568 // |kScope| is not under the default scope ("/out-of-scope/") and the
569 // Service-Worker-Allowed header is not specified.
570 const GURL kScriptURL("https://example.com/out-of-scope/normal.js");
571 const GURL kScope("https://example.com/in-scope/");
572 mock_server_.Set(
573 kScriptURL,
574 MockHTTPServer::Response(std::string("HTTP/1.1 200 OK\n"
575 "Content-Type: text/javascript\n\n"),
576 std::string()));
577 blink::mojom::ServiceWorkerRegistrationOptions options;
578 options.scope = kScope;
579 SetUpRegistrationWithOptions(kScriptURL, options);
580 DoRequest(kScriptURL, &client, &loader);
581 client->RunUntilComplete();
582
583 // The request should be failed because the scope is not allowed.
584 EXPECT_EQ(net::ERR_INSECURE_RESPONSE, client->completion_status().error_code);
585 EXPECT_FALSE(client->has_received_response());
586
587 // The response shouldn't be stored in the storage.
588 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
589 // No sample should be recorded since a write didn't occur.
590 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
591 }
592
TEST_F(ServiceWorkerNewScriptLoaderTest,Error_RedundantWorker)593 TEST_F(ServiceWorkerNewScriptLoaderTest, Error_RedundantWorker) {
594 base::HistogramTester histogram_tester;
595
596 std::unique_ptr<network::TestURLLoaderClient> client;
597 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
598
599 const GURL kScriptURL(kNormalScriptURL);
600 SetUpRegistration(kScriptURL);
601 DoRequest(kScriptURL, &client, &loader);
602
603 // Make the service worker redundant.
604 version_->Doom();
605 ASSERT_TRUE(version_->is_redundant());
606
607 client->RunUntilComplete();
608
609 // The request should be aborted.
610 EXPECT_EQ(net::ERR_FAILED, client->completion_status().error_code);
611 EXPECT_FALSE(client->has_received_response());
612
613 // The response shouldn't be stored in the storage.
614 EXPECT_FALSE(VerifyStoredResponse(kScriptURL));
615 // No sample should be recorded since a write didn't occur.
616 histogram_tester.ExpectTotalCount(kHistogramWriteResponseResult, 0);
617 }
618
619 // Tests that EmbeddedWorkerInstance's |network_accessed_for_script_| flag is
620 // set when the script loader accesses network. This flag is used to enforce the
621 // 24 hour cache validation.
TEST_F(ServiceWorkerNewScriptLoaderTest,AccessedNetwork)622 TEST_F(ServiceWorkerNewScriptLoaderTest, AccessedNetwork) {
623 const GURL kScriptURL(kNormalScriptURL);
624 const GURL kImportedScriptURL(kNormalImportedScriptURL);
625 std::unique_ptr<network::TestURLLoaderClient> client;
626 std::unique_ptr<ServiceWorkerNewScriptLoader> loader;
627
628 SetUpRegistration(kScriptURL);
629
630 // Install the main script. The network accessed flag should be flipped on.
631 version_->embedded_worker()->network_accessed_for_script_ = false;
632 mock_network_.set_to_access_network(true);
633 DoRequest(kScriptURL, &client, &loader);
634 client->RunUntilComplete();
635 EXPECT_EQ(net::OK, client->completion_status().error_code);
636 EXPECT_TRUE(version_->embedded_worker()->network_accessed_for_script());
637
638 // Install the imported script. The network accessed flag should be unchanged,
639 // as it's only meant for main scripts.
640 version_->embedded_worker()->network_accessed_for_script_ = false;
641 mock_network_.set_to_access_network(true);
642 DoRequest(kImportedScriptURL, &client, &loader);
643 client->RunUntilComplete();
644 EXPECT_EQ(net::OK, client->completion_status().error_code);
645 EXPECT_FALSE(version_->embedded_worker()->network_accessed_for_script());
646
647 // Install a new main script, this time simulating coming from cache. The
648 // network accessed flag should be off.
649 SetUpRegistration(kScriptURL);
650 version_->embedded_worker()->network_accessed_for_script_ = false;
651 mock_network_.set_to_access_network(false);
652 DoRequest(kScriptURL, &client, &loader);
653 client->RunUntilComplete();
654 EXPECT_EQ(net::OK, client->completion_status().error_code);
655 EXPECT_FALSE(version_->embedded_worker()->network_accessed_for_script());
656 }
657
658 } // namespace service_worker_new_script_loader_unittest
659 } // namespace content
660