web_bundle_url_loader_factory_unittest.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. // Copyright 2020 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. #include "components/web_package/web_bundle_url_loader_factory.h"
  5. #include "base/callback_helpers.h"
  6. #include "base/test/metrics/histogram_tester.h"
  7. #include "base/test/task_environment.h"
  8. #include "components/web_package/web_bundle_builder.h"
  9. #include "components/web_package/web_bundle_memory_quota_consumer.h"
  10. #include "mojo/public/cpp/bindings/remote.h"
  11. #include "mojo/public/cpp/system/data_pipe_utils.h"
  12. #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
  13. #include "services/network/public/cpp/resource_request.h"
  14. #include "services/network/public/mojom/url_loader.mojom.h"
  15. #include "services/network/public/mojom/url_loader_factory.mojom.h"
  16. #include "services/network/test/mock_devtools_observer.h"
  17. #include "services/network/test/test_url_loader_client.h"
  18. #include "testing/gmock/include/gmock/gmock.h"
  19. #include "testing/gtest/include/gtest/gtest.h"
  20. namespace web_package {
  21. namespace {
  22. const char kInitiatorUrl[] = "https://example.com/";
  23. const char kBundleUrl[] = "https://example.com/bundle.wbn";
  24. const char kBundleRequestId[] = "bundle-devtools-request-id";
  25. const char kResourceUrl[] = "https://example.com/";
  26. const char kResourceUrl2[] = "https://example.com/another";
  27. const char kResourceUrl3[] = "https://example.com/yetanother";
  28. const char kInvalidResourceUrl[] = "ftp://foo";
  29. const char kResourceRequestId[] = "resource-1-devtools-request-id";
  30. const char kResourceRequestId2[] = "resource-2-devtools-request-id";
  31. const char kResourceRequestId3[] = "resource-3-devtools-request-id";
  32. // Cross origin resources
  33. const char kCrossOriginJsonUrl[] = "https://other.com/resource.json";
  34. const char kCrossOriginJsUrl[] = "https://other.com/resource.js";
  35. using ::testing::_;
  36. using ::testing::ElementsAre;
  37. using ::testing::Eq;
  38. using ::testing::Optional;
  39. using ::testing::Pointee;
  40. std::vector<uint8_t> CreateSmallBundle() {
  41. web_package::WebBundleBuilder builder;
  42. builder.AddExchange(kResourceUrl,
  43. {{":status", "200"}, {"content-type", "text/plain"}},
  44. "body");
  45. return builder.CreateBundle();
  46. }
  47. std::vector<uint8_t> CreateLargeBundle() {
  48. web_package::WebBundleBuilder builder;
  49. builder.AddExchange(kResourceUrl,
  50. {{":status", "200"}, {"content-type", "text/plain"}},
  51. "body");
  52. builder.AddExchange(kResourceUrl2,
  53. {{":status", "200"}, {"content-type", "text/plain"}},
  54. std::string(10000, 'a'));
  55. builder.AddExchange(kResourceUrl3,
  56. {{":status", "200"}, {"content-type", "text/plain"}},
  57. "body");
  58. return builder.CreateBundle();
  59. }
  60. std::vector<uint8_t> CreateCrossOriginBundle() {
  61. web_package::WebBundleBuilder builder;
  62. builder.AddExchange(
  63. kCrossOriginJsonUrl,
  64. {{":status", "200"}, {"content-type", "application/json"}},
  65. "{ secret: 1 }");
  66. builder.AddExchange(kCrossOriginJsUrl,
  67. {{":status", "200"}, {"content-type", "application/js"}},
  68. "const not_secret = 1;");
  69. return builder.CreateBundle();
  70. }
  71. class TestWebBundleHandle : public network::mojom::WebBundleHandle {
  72. public:
  73. explicit TestWebBundleHandle(
  74. mojo::PendingReceiver<network::mojom::WebBundleHandle> receiver)
  75. : receiver_(this, std::move(receiver)) {}
  76. const absl::optional<
  77. std::pair<network::mojom::WebBundleErrorType, std::string>>&
  78. last_bundle_error() const {
  79. return last_bundle_error_;
  80. }
  81. void RunUntilBundleError() {
  82. if (last_bundle_error_.has_value())
  83. return;
  84. base::RunLoop run_loop;
  85. quit_closure_for_bundle_error_ = run_loop.QuitClosure();
  86. run_loop.Run();
  87. }
  88. // network::mojom::WebBundleHandle
  89. void Clone(mojo::PendingReceiver<network::mojom::WebBundleHandle> receiver)
  90. override {
  91. NOTREACHED();
  92. }
  93. void OnWebBundleError(network::mojom::WebBundleErrorType type,
  94. const std::string& message) override {
  95. last_bundle_error_ = std::make_pair(type, message);
  96. if (quit_closure_for_bundle_error_)
  97. std::move(quit_closure_for_bundle_error_).Run();
  98. }
  99. void OnWebBundleLoadFinished(bool success) override {}
  100. private:
  101. mojo::Receiver<network::mojom::WebBundleHandle> receiver_;
  102. absl::optional<std::pair<network::mojom::WebBundleErrorType, std::string>>
  103. last_bundle_error_;
  104. base::OnceClosure quit_closure_for_bundle_error_;
  105. };
  106. class MockMemoryQuotaConsumer : public WebBundleMemoryQuotaConsumer {
  107. public:
  108. MockMemoryQuotaConsumer() = default;
  109. ~MockMemoryQuotaConsumer() override = default;
  110. bool AllocateMemory(uint64_t num_bytes) override { return true; }
  111. };
  112. class BadMessageTestHelper {
  113. public:
  114. BadMessageTestHelper()
  115. : dummy_message_(0, 0, 0, 0, nullptr), context_(&dummy_message_) {
  116. mojo::SetDefaultProcessErrorHandler(base::BindRepeating(
  117. &BadMessageTestHelper::OnBadMessage, base::Unretained(this)));
  118. }
  119. BadMessageTestHelper(const BadMessageTestHelper&) = delete;
  120. BadMessageTestHelper& operator=(const BadMessageTestHelper&) = delete;
  121. ~BadMessageTestHelper() {
  122. mojo::SetDefaultProcessErrorHandler(base::NullCallback());
  123. }
  124. const std::vector<std::string>& bad_message_reports() const {
  125. return bad_message_reports_;
  126. }
  127. private:
  128. void OnBadMessage(const std::string& reason) {
  129. bad_message_reports_.push_back(reason);
  130. }
  131. std::vector<std::string> bad_message_reports_;
  132. mojo::Message dummy_message_;
  133. mojo::internal::MessageDispatchContext context_;
  134. };
  135. } // namespace
  136. class WebBundleURLLoaderFactoryTest : public ::testing::Test {
  137. public:
  138. void SetUp() override {
  139. mojo::ScopedDataPipeConsumerHandle consumer;
  140. ASSERT_EQ(CreateDataPipe(nullptr, bundle_data_destination_, consumer),
  141. MOJO_RESULT_OK);
  142. mojo::Remote<network::mojom::WebBundleHandle> handle;
  143. handle_ = std::make_unique<TestWebBundleHandle>(
  144. handle.BindNewPipeAndPassReceiver());
  145. devtools_observer_ = std::make_unique<network::MockDevToolsObserver>();
  146. const network::ResourceRequest::WebBundleTokenParams create_params(
  147. GURL(kBundleUrl), {} /* token */, {} /* handle */);
  148. factory_ = std::make_unique<WebBundleURLLoaderFactory>(
  149. GURL(kBundleUrl), create_params, std::move(handle),
  150. std::make_unique<MockMemoryQuotaConsumer>(), devtools_observer_->Bind(),
  151. kBundleRequestId, network::CrossOriginEmbedderPolicy(),
  152. nullptr /* coep_reporter */);
  153. factory_->SetBundleStream(std::move(consumer));
  154. }
  155. void WriteBundle(base::span<const uint8_t> data) {
  156. mojo::BlockingCopyFromString(
  157. std::string(reinterpret_cast<const char*>(data.data()), data.size()),
  158. bundle_data_destination_);
  159. }
  160. void FinishWritingBundle() { bundle_data_destination_.reset(); }
  161. struct StartRequestResult {
  162. mojo::Remote<network::mojom::URLLoader> loader;
  163. std::unique_ptr<network::TestURLLoaderClient> client;
  164. };
  165. network::ResourceRequest CreateRequest(
  166. const GURL& url,
  167. const std::string& devtools_request_id) {
  168. network::ResourceRequest request;
  169. request.url = url;
  170. request.method = "GET";
  171. request.request_initiator = url::Origin::Create(GURL(kInitiatorUrl));
  172. request.web_bundle_token_params =
  173. network::ResourceRequest::WebBundleTokenParams();
  174. request.web_bundle_token_params->bundle_url = GURL(kBundleUrl);
  175. request.devtools_request_id = devtools_request_id;
  176. return request;
  177. }
  178. StartRequestResult StartRequest(const network::ResourceRequest& request) {
  179. StartRequestResult result;
  180. result.client = std::make_unique<network::TestURLLoaderClient>();
  181. factory_->StartSubresourceRequest(
  182. result.loader.BindNewPipeAndPassReceiver(), request,
  183. result.client->CreateRemote(),
  184. mojo::Remote<network::mojom::TrustedHeaderClient>(), base::Time::Now(),
  185. base::TimeTicks::Now());
  186. return result;
  187. }
  188. StartRequestResult StartRequest(const GURL& url,
  189. const std::string& devtools_request_id) {
  190. return StartRequest(CreateRequest(url, devtools_request_id));
  191. }
  192. void RunUntilBundleError() { handle_->RunUntilBundleError(); }
  193. const absl::optional<
  194. std::pair<network::mojom::WebBundleErrorType, std::string>>&
  195. last_bundle_error() const {
  196. return handle_->last_bundle_error();
  197. }
  198. protected:
  199. std::unique_ptr<network::MockDevToolsObserver> devtools_observer_;
  200. std::unique_ptr<WebBundleURLLoaderFactory> factory_;
  201. private:
  202. std::unique_ptr<TestWebBundleHandle> handle_;
  203. mojo::ScopedDataPipeProducerHandle bundle_data_destination_;
  204. base::test::TaskEnvironment task_environment;
  205. };
  206. TEST_F(WebBundleURLLoaderFactoryTest, Basic) {
  207. base::HistogramTester histogram_tester;
  208. WriteBundle(CreateSmallBundle());
  209. FinishWritingBundle();
  210. EXPECT_CALL(*devtools_observer_,
  211. OnSubresourceWebBundleMetadata(kBundleRequestId,
  212. ElementsAre(GURL(kResourceUrl))));
  213. EXPECT_CALL(*devtools_observer_,
  214. OnSubresourceWebBundleInnerResponse(
  215. kResourceRequestId, GURL(kResourceUrl),
  216. Optional(std::string(kBundleRequestId))));
  217. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  218. request.client->RunUntilComplete();
  219. EXPECT_EQ(net::OK, request.client->completion_status().error_code);
  220. EXPECT_FALSE(last_bundle_error().has_value());
  221. EXPECT_EQ(request.client->response_head()->web_bundle_url, GURL(kBundleUrl));
  222. std::string body;
  223. EXPECT_TRUE(mojo::BlockingCopyToString(
  224. request.client->response_body_release(), &body));
  225. EXPECT_EQ("body", body);
  226. histogram_tester.ExpectUniqueSample(
  227. "SubresourceWebBundles.LoadResult",
  228. WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::kSuccess, 1);
  229. }
  230. TEST_F(WebBundleURLLoaderFactoryTest, MetadataParseError) {
  231. base::HistogramTester histogram_tester;
  232. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  233. std::vector<uint8_t> bundle = CreateSmallBundle();
  234. bundle[4] ^= 1; // Mutate magic bytes.
  235. WriteBundle(bundle);
  236. FinishWritingBundle();
  237. EXPECT_CALL(*devtools_observer_,
  238. OnSubresourceWebBundleMetadataError(kBundleRequestId,
  239. Eq("Wrong magic bytes.")));
  240. EXPECT_CALL(*devtools_observer_, OnSubresourceWebBundleInnerResponse(_, _, _))
  241. .Times(0);
  242. request.client->RunUntilComplete();
  243. RunUntilBundleError();
  244. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  245. request.client->completion_status().error_code);
  246. EXPECT_EQ(last_bundle_error()->first,
  247. network::mojom::WebBundleErrorType::kMetadataParseError);
  248. EXPECT_EQ(last_bundle_error()->second, "Wrong magic bytes.");
  249. // Requests made after metadata parse error should also fail.
  250. auto request2 = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  251. request2.client->RunUntilComplete();
  252. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  253. request2.client->completion_status().error_code);
  254. histogram_tester.ExpectUniqueSample(
  255. "SubresourceWebBundles.LoadResult",
  256. WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
  257. kMetadataParseError,
  258. 1);
  259. }
  260. TEST_F(WebBundleURLLoaderFactoryTest, MetadataWithInvalidExchangeUrl) {
  261. base::HistogramTester histogram_tester;
  262. auto request = StartRequest(GURL(kInvalidResourceUrl), kResourceRequestId);
  263. web_package::WebBundleBuilder builder;
  264. builder.AddExchange(kInvalidResourceUrl,
  265. {{":status", "200"}, {"content-type", "text/plain"}},
  266. "body");
  267. WriteBundle(builder.CreateBundle());
  268. FinishWritingBundle();
  269. EXPECT_CALL(*devtools_observer_,
  270. OnSubresourceWebBundleMetadataError(
  271. kBundleRequestId, Eq("Exchange URL is not valid.")));
  272. EXPECT_CALL(*devtools_observer_, OnSubresourceWebBundleInnerResponse(_, _, _))
  273. .Times(0);
  274. request.client->RunUntilComplete();
  275. RunUntilBundleError();
  276. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  277. request.client->completion_status().error_code);
  278. EXPECT_EQ(last_bundle_error()->first,
  279. network::mojom::WebBundleErrorType::kMetadataParseError);
  280. EXPECT_EQ(last_bundle_error()->second, "Exchange URL is not valid.");
  281. // Requests made after metadata parse error should also fail.
  282. auto request2 = StartRequest(GURL(kInvalidResourceUrl), kResourceRequestId);
  283. request2.client->RunUntilComplete();
  284. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  285. request2.client->completion_status().error_code);
  286. histogram_tester.ExpectUniqueSample(
  287. "SubresourceWebBundles.LoadResult",
  288. WebBundleURLLoaderFactory::SubresourceWebBundleLoadResult::
  289. kMetadataParseError,
  290. 1);
  291. }
  292. TEST_F(WebBundleURLLoaderFactoryTest, ResponseParseError) {
  293. web_package::WebBundleBuilder builder;
  294. // An invalid response.
  295. builder.AddExchange(kResourceUrl, {{":status", "0"}}, "body");
  296. WriteBundle(builder.CreateBundle());
  297. FinishWritingBundle();
  298. EXPECT_CALL(*devtools_observer_,
  299. OnSubresourceWebBundleMetadata(kBundleRequestId,
  300. ElementsAre(GURL(kResourceUrl))));
  301. EXPECT_CALL(*devtools_observer_,
  302. OnSubresourceWebBundleInnerResponseError(
  303. kResourceRequestId, GURL(kResourceUrl),
  304. Eq(":status must be 3 ASCII decimal digits."),
  305. Optional(std::string(kBundleRequestId))));
  306. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  307. request.client->RunUntilComplete();
  308. RunUntilBundleError();
  309. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  310. request.client->completion_status().error_code);
  311. EXPECT_EQ(last_bundle_error()->first,
  312. network::mojom::WebBundleErrorType::kResponseParseError);
  313. EXPECT_EQ(last_bundle_error()->second,
  314. ":status must be 3 ASCII decimal digits.");
  315. }
  316. TEST_F(WebBundleURLLoaderFactoryTest, ResourceNotFoundInBundle) {
  317. WriteBundle(CreateSmallBundle());
  318. FinishWritingBundle();
  319. EXPECT_CALL(*devtools_observer_,
  320. OnSubresourceWebBundleMetadata(kBundleRequestId,
  321. ElementsAre(GURL(kResourceUrl))));
  322. EXPECT_CALL(*devtools_observer_, OnSubresourceWebBundleInnerResponse(_, _, _))
  323. .Times(0);
  324. auto request = StartRequest(GURL("https://example.com/no-such-resource"),
  325. kResourceRequestId);
  326. request.client->RunUntilComplete();
  327. RunUntilBundleError();
  328. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  329. request.client->completion_status().error_code);
  330. EXPECT_EQ(last_bundle_error()->first,
  331. network::mojom::WebBundleErrorType::kResourceNotFound);
  332. EXPECT_EQ(
  333. last_bundle_error()->second,
  334. "https://example.com/no-such-resource is not found in the WebBundle.");
  335. }
  336. TEST_F(WebBundleURLLoaderFactoryTest, RedirectResponseIsNotAllowed) {
  337. web_package::WebBundleBuilder builder;
  338. builder.AddExchange(kResourceUrl,
  339. {{":status", "301"}, {"location", kResourceUrl2}}, "");
  340. builder.AddExchange(kResourceUrl2,
  341. {{":status", "200"}, {"content-type", "text/plain"}},
  342. "body");
  343. WriteBundle(builder.CreateBundle());
  344. FinishWritingBundle();
  345. EXPECT_CALL(*devtools_observer_,
  346. OnSubresourceWebBundleMetadata(
  347. kBundleRequestId,
  348. ElementsAre(GURL(kResourceUrl), GURL(kResourceUrl2))));
  349. EXPECT_CALL(*devtools_observer_,
  350. OnSubresourceWebBundleInnerResponse(
  351. kResourceRequestId, GURL(kResourceUrl),
  352. Optional(std::string(kBundleRequestId))));
  353. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  354. request.client->RunUntilComplete();
  355. RunUntilBundleError();
  356. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  357. request.client->completion_status().error_code);
  358. EXPECT_EQ(last_bundle_error()->first,
  359. network::mojom::WebBundleErrorType::kResponseParseError);
  360. EXPECT_EQ(last_bundle_error()->second, "Invalid response code 301");
  361. }
  362. TEST_F(WebBundleURLLoaderFactoryTest, StartRequestBeforeReadingBundle) {
  363. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  364. WriteBundle(CreateSmallBundle());
  365. FinishWritingBundle();
  366. request.client->RunUntilComplete();
  367. EXPECT_EQ(net::OK, request.client->completion_status().error_code);
  368. }
  369. TEST_F(WebBundleURLLoaderFactoryTest, MultipleRequests) {
  370. auto request1 = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  371. auto request2 = StartRequest(GURL(kResourceUrl2), kResourceRequestId2);
  372. std::vector<uint8_t> bundle = CreateLargeBundle();
  373. // Write the first 10kB of the bundle in which the bundle's metadata and the
  374. // response for kResourceUrl are included.
  375. ASSERT_GT(bundle.size(), 10000U);
  376. WriteBundle(base::make_span(bundle).subspan(0, 10000));
  377. request1.client->RunUntilComplete();
  378. EXPECT_EQ(net::OK, request1.client->completion_status().error_code);
  379. EXPECT_FALSE(request2.client->has_received_completion());
  380. // Write the rest of the data.
  381. WriteBundle(base::make_span(bundle).subspan(10000));
  382. FinishWritingBundle();
  383. request2.client->RunUntilComplete();
  384. EXPECT_EQ(net::OK, request2.client->completion_status().error_code);
  385. }
  386. TEST_F(WebBundleURLLoaderFactoryTest, CancelRequest) {
  387. auto request_to_complete1 =
  388. StartRequest(GURL(kResourceUrl), kResourceRequestId);
  389. auto request_to_complete2 =
  390. StartRequest(GURL(kResourceUrl2), kResourceRequestId2);
  391. auto request_to_cancel1 =
  392. StartRequest(GURL(kResourceUrl), kResourceRequestId);
  393. auto request_to_cancel2 =
  394. StartRequest(GURL(kResourceUrl2), kResourceRequestId2);
  395. auto request_to_cancel3 =
  396. StartRequest(GURL(kResourceUrl3), kResourceRequestId3);
  397. // Cancel request before getting metadata.
  398. request_to_cancel1.loader.reset();
  399. std::vector<uint8_t> bundle = CreateLargeBundle();
  400. // Write the first 10kB of the bundle in which the bundle's metadata, response
  401. // for kResourceUrl, and response header for kResourceUrl2 are included.
  402. ASSERT_GT(bundle.size(), 10000U);
  403. WriteBundle(base::make_span(bundle).subspan(0, 10000));
  404. // This makes sure the bytes written above are consumed by WebBundle parser.
  405. request_to_complete1.client->RunUntilComplete();
  406. // Cancel request after reading response header, but before reading body.
  407. request_to_cancel2.loader.reset();
  408. // Cancel request after getting metadata, but before reading response header.
  409. request_to_cancel3.loader.reset();
  410. // Write the rest of the data.
  411. WriteBundle(base::make_span(bundle).subspan(10000));
  412. FinishWritingBundle();
  413. request_to_complete2.client->RunUntilComplete();
  414. EXPECT_EQ(net::OK,
  415. request_to_complete2.client->completion_status().error_code);
  416. }
  417. TEST_F(WebBundleURLLoaderFactoryTest,
  418. FactoryDestructionCancelsInflightRequests) {
  419. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  420. factory_ = nullptr;
  421. WriteBundle(CreateSmallBundle());
  422. FinishWritingBundle();
  423. request.client->RunUntilComplete();
  424. EXPECT_EQ(net::ERR_FAILED, request.client->completion_status().error_code);
  425. }
  426. TEST_F(WebBundleURLLoaderFactoryTest, TruncatedBundle) {
  427. std::vector<uint8_t> bundle = CreateSmallBundle();
  428. // Truncate in the middle of responses section.
  429. bundle.resize(bundle.size() - 10);
  430. WriteBundle(std::move(bundle));
  431. FinishWritingBundle();
  432. auto request = StartRequest(GURL(kResourceUrl), kResourceRequestId);
  433. request.client->RunUntilComplete();
  434. RunUntilBundleError();
  435. EXPECT_EQ(net::ERR_INVALID_WEB_BUNDLE,
  436. request.client->completion_status().error_code);
  437. EXPECT_EQ(last_bundle_error()->first,
  438. network::mojom::WebBundleErrorType::kResponseParseError);
  439. EXPECT_EQ(last_bundle_error()->second, "Error reading response header.");
  440. }
  441. TEST_F(WebBundleURLLoaderFactoryTest, CrossOriginJson) {
  442. WriteBundle(CreateCrossOriginBundle());
  443. FinishWritingBundle();
  444. auto request = StartRequest(GURL(kCrossOriginJsonUrl), kResourceRequestId);
  445. request.client->RunUntilComplete();
  446. EXPECT_EQ(net::OK, request.client->completion_status().error_code);
  447. EXPECT_FALSE(last_bundle_error().has_value());
  448. std::string body;
  449. ASSERT_TRUE(mojo::BlockingCopyToString(
  450. request.client->response_body_release(), &body));
  451. EXPECT_TRUE(body.empty())
  452. << "body should be empty because JSON is a CORB-protected resource";
  453. }
  454. TEST_F(WebBundleURLLoaderFactoryTest, CrossOriginJs) {
  455. WriteBundle(CreateCrossOriginBundle());
  456. FinishWritingBundle();
  457. auto request = StartRequest(GURL(kCrossOriginJsUrl), kResourceRequestId);
  458. request.client->RunUntilComplete();
  459. EXPECT_EQ(net::OK, request.client->completion_status().error_code);
  460. EXPECT_FALSE(last_bundle_error().has_value());
  461. std::string body;
  462. ASSERT_TRUE(mojo::BlockingCopyToString(
  463. request.client->response_body_release(), &body));
  464. EXPECT_EQ("const not_secret = 1;", body)
  465. << "body should be valid one because JS is not a CORB protected resource";
  466. }
  467. TEST_F(WebBundleURLLoaderFactoryTest, WrongBundleURL) {
  468. BadMessageTestHelper bad_message_helper;
  469. WriteBundle(CreateSmallBundle());
  470. FinishWritingBundle();
  471. network::ResourceRequest url_request =
  472. CreateRequest(GURL(kResourceUrl), kResourceRequestId);
  473. url_request.web_bundle_token_params->bundle_url =
  474. GURL("https://modified-bundle-url.example.com/");
  475. auto request = StartRequest(url_request);
  476. request.client->RunUntilComplete();
  477. EXPECT_EQ(net::ERR_INVALID_ARGUMENT,
  478. request.client->completion_status().error_code);
  479. EXPECT_THAT(bad_message_helper.bad_message_reports(),
  480. ::testing::ElementsAre(
  481. "WebBundleURLLoaderFactory: Bundle URL does not match"));
  482. }
  483. } // namespace web_package