diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 15:28:34 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-08-28 13:54:51 +0000 |
commit | 2a19c63448c84c1805fb1a585c3651318bb86ca7 (patch) | |
tree | eb17888e8531aa6ee5e85721bd553b832a7e5156 /chromium/content/browser/loader | |
parent | b014812705fc80bff0a5c120dfcef88f349816dc (diff) | |
download | qtwebengine-chromium-2a19c63448c84c1805fb1a585c3651318bb86ca7.tar.gz |
BASELINE: Update Chromium to 69.0.3497.70
Change-Id: I2b7b56e4e7a8b26656930def0d4575dc32b900a0
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/content/browser/loader')
69 files changed, 1145 insertions, 3392 deletions
diff --git a/chromium/content/browser/loader/cors_file_origin_browsertest.cc b/chromium/content/browser/loader/cors_file_origin_browsertest.cc index af3ed282f18..2b0e7a609ba 100644 --- a/chromium/content/browser/loader/cors_file_origin_browsertest.cc +++ b/chromium/content/browser/loader/cors_file_origin_browsertest.cc @@ -12,7 +12,10 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" +#include "base/test/scoped_command_line.h" +#include "base/test/scoped_feature_list.h" #include "content/public/common/content_paths.h" +#include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" @@ -24,6 +27,7 @@ #include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/request_handler_util.h" #include "services/network/public/cpp/cors/cors.h" +#include "services/network/public/cpp/features.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -37,11 +41,20 @@ using net::test_server::HttpResponse; // Tests end to end Origin header and CORS check behaviors without // --allow-file-access-from-files flag. -class CORSFileOriginBrowserTest : public ContentBrowserTest { +class CORSFileOriginBrowserTest : public ContentBrowserTest, + public testing::WithParamInterface<bool> { public: CORSFileOriginBrowserTest() : pass_string_(base::ASCIIToUTF16("PASS")), - fail_string_(base::ASCIIToUTF16("FAIL")) {} + fail_string_(base::ASCIIToUTF16("FAIL")) { + if (GetParam()) { + scoped_feature_list_.InitAndEnableFeature( + network::features::kOutOfBlinkCORS); + } else { + scoped_feature_list_.InitAndDisableFeature( + network::features::kOutOfBlinkCORS); + } + } ~CORSFileOriginBrowserTest() override = default; protected: @@ -84,7 +97,15 @@ class CORSFileOriginBrowserTest : public ContentBrowserTest { private: bool AllowFileAccessFromFiles() const override { return false; } + virtual bool IsWebSecurityEnabled() const { return true; } + + void SetUpCommandLine(base::CommandLine* command_line) override { + if (!IsWebSecurityEnabled()) { + command_line->AppendSwitch(switches::kDisableWebSecurity); + } + ContentBrowserTest::SetUpCommandLine(command_line); + } void SetUpOnMainThread() override { base::AutoLock lock(lock_); @@ -147,6 +168,9 @@ class CORSFileOriginBrowserTest : public ContentBrowserTest { const base::string16 pass_string_; const base::string16 fail_string_; + base::test::ScopedFeatureList scoped_command_line_; + base::test::ScopedFeatureList scoped_feature_list_; + DISALLOW_COPY_AND_ASSIGN(CORSFileOriginBrowserTest); }; @@ -158,7 +182,16 @@ class CORSFileOriginBrowserTestWithAllowFileAccessFromFiles bool AllowFileAccessFromFiles() const override { return true; } }; -IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTest, +// Tests end to end Origin header and CORS check behaviors with +// --disable-web-security flag. +class CORSFileOriginBrowserTestWithDisableWebSecurity + : public CORSFileOriginBrowserTest { + private: + bool AllowFileAccessFromFiles() const override { return false; } + bool IsWebSecurityEnabled() const override { return false; } +}; + +IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTest, AccessControlAllowOriginIsNull) { std::unique_ptr<TitleWatcher> watcher = CreateWatcher(); EXPECT_TRUE(NavigateToURL( @@ -170,7 +203,7 @@ IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTest, EXPECT_TRUE(is_preflight_requested()); } -IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTest, +IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTest, AccessControlAllowOriginIsFile) { std::unique_ptr<TitleWatcher> watcher = CreateWatcher(); EXPECT_TRUE(NavigateToURL( @@ -182,7 +215,7 @@ IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTest, EXPECT_TRUE(is_preflight_requested()); } -IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, +IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, AccessControlAllowOriginIsNull) { std::unique_ptr<TitleWatcher> watcher = CreateWatcher(); EXPECT_TRUE(NavigateToURL( @@ -194,7 +227,7 @@ IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, EXPECT_TRUE(is_preflight_requested()); } -IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, +IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, AccessControlAllowOriginIsFile) { std::unique_ptr<TitleWatcher> watcher = CreateWatcher(); EXPECT_TRUE(NavigateToURL( @@ -206,6 +239,47 @@ IN_PROC_BROWSER_TEST_F(CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, EXPECT_TRUE(is_preflight_requested()); } +IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTestWithDisableWebSecurity, + AccessControlAllowOriginIsNull) { + std::unique_ptr<TitleWatcher> watcher = CreateWatcher(); + EXPECT_TRUE(NavigateToURL( + shell(), CreateTestDataURL(base::StringPrintf( + "cors_file_origin_test.html?port=%d&allow=%s&origin=%s", + port(), "unused", "")))); + + EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle()); + EXPECT_FALSE(is_preflight_requested()); +} + +IN_PROC_BROWSER_TEST_P(CORSFileOriginBrowserTestWithDisableWebSecurity, + AccessControlAllowOriginIsFile) { + std::unique_ptr<TitleWatcher> watcher = CreateWatcher(); + EXPECT_TRUE(NavigateToURL( + shell(), CreateTestDataURL(base::StringPrintf( + "cors_file_origin_test.html?port=%d&allow=%s&origin=%s", + port(), "unused", "")))); + + EXPECT_EQ(pass_string(), watcher->WaitAndGetTitle()); + EXPECT_FALSE(is_preflight_requested()); +} + +// --allow-file-access-from-files is currently not supported by OOR-CORS. +// We may remove the feature. +INSTANTIATE_TEST_CASE_P( + /* No test prefix */, + CORSFileOriginBrowserTest, + ::testing::Values(false)); + +INSTANTIATE_TEST_CASE_P( + /* No test prefix */, + CORSFileOriginBrowserTestWithAllowFileAccessFromFiles, + ::testing::Values(false)); + +INSTANTIATE_TEST_CASE_P( + /* No test prefix */, + CORSFileOriginBrowserTestWithDisableWebSecurity, + ::testing::Values(false, true)); + } // namespace } // namespace content diff --git a/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc b/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc index 494ecda070c..321ab2c8e87 100644 --- a/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc +++ b/chromium/content/browser/loader/cross_site_document_blocking_browsertest.cc @@ -7,11 +7,12 @@ #include <utility> #include "base/command_line.h" +#include "base/feature_list.h" #include "base/macros.h" #include "base/strings/pattern.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "base/test/histogram_tester.h" +#include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "build/build_config.h" #include "content/browser/loader/cross_site_document_resource_handler.h" @@ -31,15 +32,19 @@ #include "content/public/test/url_loader_interceptor.h" #include "content/shell/browser/shell.h" #include "content/test/test_content_browser_client.h" +#include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" +#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/network_switches.h" #include "services/network/test/test_url_loader_client.h" #include "testing/gmock/include/gmock/gmock.h" +#include "third_party/blink/public/common/service_worker/service_worker_utils.h" namespace content { using testing::Not; using testing::HasSubstr; +using Action = network::CrossOriginReadBlocking::Action; namespace { @@ -83,6 +88,14 @@ void InspectHistograms(const base::HistogramTester& histograms, const HistogramExpectations& expectations, const std::string& resource_name, ResourceType resource_type) { + // //services/network doesn't have access to content::ResourceType and + // therefore cannot log some XSDB UMAs. + bool is_restricted_uma_expected = false; + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { + is_restricted_uma_expected = true; + FetchHistogramsFromChildProcesses(); + } + std::string bucket; if (base::MatchPattern(resource_name, "*.html")) { bucket = "HTML"; @@ -103,16 +116,23 @@ void InspectHistograms(const base::HistogramTester& histograms, std::string base = "SiteIsolation.XSD.Browser"; expected_counts[base + ".Action"] = 2; if ((base::MatchPattern(resource_name, "*prefixed*") || bucket == "Others") && - (0 != (expectations & kShouldBeBlocked))) { + (0 != (expectations & kShouldBeBlocked)) && !is_restricted_uma_expected) { expected_counts[base + ".BlockedForParserBreaker"] = 1; } if (0 != (expectations & kShouldBeSniffed)) expected_counts[base + ".BytesReadForSniffing"] = 1; - if (0 != (expectations & kShouldBeBlocked)) { + if (0 != (expectations & kShouldBeBlocked && !is_restricted_uma_expected)) { expected_counts[base + ".Blocked"] = 1; expected_counts[base + ".Blocked." + bucket] = 1; + } + if (0 != (expectations & kShouldBeBlocked)) { expected_counts[base + ".Blocked.ContentLength.WasAvailable"] = 1; - if (0 != (expectations & kShouldHaveContentLength)) + bool should_have_content_length = + 0 != (expectations & kShouldHaveContentLength); + histograms.ExpectBucketCount(base + ".Blocked.ContentLength.WasAvailable", + should_have_content_length, 1); + + if (should_have_content_length) expected_counts[base + ".Blocked.ContentLength.ValueIfAvailable"] = 1; } @@ -124,7 +144,7 @@ void InspectHistograms(const base::HistogramTester& histograms, << ", expectations=" << expectations; // Determine if the bucket for the resource type (XHR) was incremented. - if (0 != (expectations & kShouldBeBlocked)) { + if (0 != (expectations & kShouldBeBlocked) && !is_restricted_uma_expected) { EXPECT_THAT(histograms.GetAllSamples(base + ".Blocked"), testing::ElementsAre(base::Bucket(resource_type, 1))) << "The wrong Blocked bucket was incremented."; @@ -132,6 +152,26 @@ void InspectHistograms(const base::HistogramTester& histograms, testing::ElementsAre(base::Bucket(resource_type, 1))) << "The wrong Blocked bucket was incremented."; } + + // SiteIsolation.XSD.Browser.Action should always include kResponseStarted. + histograms.ExpectBucketCount(base + ".Action", + static_cast<int>(Action::kResponseStarted), 1); + + // Second value in SiteIsolation.XSD.Browser.Action depends on |expectations|. + Action expected_action = static_cast<Action>(-1); + if (expectations & kShouldBeBlocked) { + if (expectations & kShouldBeSniffed) + expected_action = Action::kBlockedAfterSniffing; + else + expected_action = Action::kBlockedWithoutSniffing; + } else { + if (expectations & kShouldBeSniffed) + expected_action = Action::kAllowedAfterSniffing; + else + expected_action = Action::kAllowedWithoutSniffing; + } + histograms.ExpectBucketCount(base + ".Action", + static_cast<int>(expected_action), 1); } // Helper for intercepting a resource request to the given URL and capturing the @@ -290,16 +330,18 @@ class DisableWebSecurityContentBrowserClient : public TestContentBrowserClient { // Note that this BaseTest class does not specify an isolation mode via // command-line flags. Most of the tests are in the --site-per-process subclass // below. -class CrossSiteDocumentBlockingBaseTest : public ContentBrowserTest { +class CrossSiteDocumentBlockingTest : public ContentBrowserTest { public: - CrossSiteDocumentBlockingBaseTest() {} - ~CrossSiteDocumentBlockingBaseTest() override {} + CrossSiteDocumentBlockingTest() {} + ~CrossSiteDocumentBlockingTest() override {} void SetUpCommandLine(base::CommandLine* command_line) override { // EmbeddedTestServer::InitializeAndListen() initializes its |base_url_| // which is required below. This cannot invoke Start() however as that kicks // off the "EmbeddedTestServer IO Thread" which then races with // initialization in ContentBrowserTest::SetUp(), http://crbug.com/674545. + // Additionally the server should not be started prior to setting up + // ControllableHttpResponse(s) in some individual tests below. ASSERT_TRUE(embedded_test_server()->InitializeAndListen()); // Add a host resolver rule to map all outgoing requests to the test server. @@ -313,10 +355,6 @@ class CrossSiteDocumentBlockingBaseTest : public ContentBrowserTest { } void SetUpOnMainThread() override { - // Complete the manual Start() after ContentBrowserTest's own - // initialization, ref. comment on InitializeAndListen() above. - embedded_test_server()->StartAcceptingConnections(); - // Disable web security via the ContentBrowserClient and notify the current // renderer process. old_client = SetBrowserClientForTesting(&new_client); @@ -329,22 +367,6 @@ class CrossSiteDocumentBlockingBaseTest : public ContentBrowserTest { DisableWebSecurityContentBrowserClient new_client; ContentBrowserClient* old_client = nullptr; - DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingBaseTest); -}; - -// Most tests here use --site-per-process, which enables document blocking -// everywhere. -class CrossSiteDocumentBlockingTest : public CrossSiteDocumentBlockingBaseTest { - public: - CrossSiteDocumentBlockingTest() {} - ~CrossSiteDocumentBlockingTest() override {} - - void SetUpCommandLine(base::CommandLine* command_line) override { - IsolateAllSitesForTesting(command_line); - CrossSiteDocumentBlockingBaseTest::SetUpCommandLine(command_line); - } - - private: DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentBlockingTest); }; @@ -355,6 +377,7 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockDocuments) { // possible since we run the browser without the same origin policy, allowing // it to see the response body if it makes it to the renderer (even if the // renderer would normally block access to it). + embedded_test_server()->StartAcceptingConnections(); GURL foo_url("http://foo.com/cross_site_document_blocking/request.html"); EXPECT_TRUE(NavigateToURL(shell(), foo_url)); @@ -417,7 +440,8 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockDocuments) { // jsonp.* - JSONP (i.e., script) mislabeled as a document. // img.* - Contents that won't match the document label. // valid.* - Correctly labeled responses of non-document types. - const char* sniff_allowed_resources[] = {"js.html", + const char* sniff_allowed_resources[] = {"html-prefix.txt", + "js.html", "comment_js.html", "js.xml", "js.json", @@ -469,6 +493,7 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockDocuments) { // be a problem for script files mislabeled as HTML/XML/JSON/text (i.e., the // reason for sniffing), since script tags won't send Range headers. IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, RangeRequest) { + embedded_test_server()->StartAcceptingConnections(); GURL foo_url("http://foo.com/cross_site_document_blocking/request.html"); EXPECT_TRUE(NavigateToURL(shell(), foo_url)); @@ -519,6 +544,7 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockForVariousTargets) { // TODO(nick): Split up these cases, and add positive assertions here about // what actually happens in these various resource-block cases. + embedded_test_server()->StartAcceptingConnections(); GURL foo("http://foo.com/cross_site_document_blocking/request_target.html"); EXPECT_TRUE(NavigateToURL(shell(), foo)); @@ -530,6 +556,7 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockForVariousTargets) { // Regression test for https://crbug.com/814913. IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockRequestFromErrorPage) { + embedded_test_server()->StartAcceptingConnections(); GURL error_url = embedded_test_server()->GetURL("bar.com", "/close-socket"); GURL subresource_url = embedded_test_server()->GetURL("foo.com", "/site_isolation/json.js"); @@ -560,10 +587,10 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, } IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockHeaders) { - GURL foo_url("http://foo.com/title1.html"); - EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + embedded_test_server()->StartAcceptingConnections(); // Prepare to intercept the network request at the IPC layer. + // This has to be done before the RenderFrameHostImpl is created. // // Note: we want to verify that the blocking prevents the data from being sent // over IPC. Testing later (e.g. via Response/Headers Web APIs) might give a @@ -572,6 +599,10 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockHeaders) { GURL bar_url("http://bar.com/cross_site_document_blocking/headers-test.json"); RequestInterceptor interceptor(bar_url); + // Navigate to the test page. + GURL foo_url("http://foo.com/title1.html"); + EXPECT_TRUE(NavigateToURL(shell(), foo_url)); + // Issue the request that will be intercepted EXPECT_TRUE(ExecuteScript(shell(), base::StringPrintf("fetch('%s').catch(error => {})", @@ -580,7 +611,7 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockHeaders) { // Verify that the response completed successfully and was blocked. ASSERT_EQ(net::OK, interceptor.completion_status().error_code); - ASSERT_TRUE(interceptor.completion_status().blocked_cross_site_document); + ASSERT_TRUE(interceptor.completion_status().should_report_corb_blocking); // Verify that safelisted headers have not been removed by XSDB. // See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name. @@ -623,6 +654,113 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, BlockHeaders) { EXPECT_EQ(0u, interceptor.response_head().content_length); } +IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingTest, PrefetchIsNotImpacted) { + // Prepare for intercepting the resource request for testing prefetching. + const char* kPrefetchResourcePath = "/prefetch-test"; + net::test_server::ControllableHttpResponse response(embedded_test_server(), + kPrefetchResourcePath); + + // Navigate to a webpage containing a cross-origin frame. + embedded_test_server()->StartAcceptingConnections(); + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(b)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + // Inject a cross-origin <link rel="prefetch" ...> into the main frame. + // TODO(lukasza): https://crbug.com/827633#c5: We might need to switch to + // listening to the onload event below (after/if CORB starts to consistently + // avoid injecting net errors). + const char* prefetch_injection_script_template = R"( + var link = document.createElement("link"); + link.rel = "prefetch"; + link.href = "/cross-site/b.com%s"; + link.as = "fetch"; + + window.is_prefetch_done = false; + function mark_prefetch_as_done() { window.is_prefetch_done = true } + link.onerror = mark_prefetch_as_done; + + document.getElementsByTagName('head')[0].appendChild(link); + )"; + std::string prefetch_injection_script = base::StringPrintf( + prefetch_injection_script_template, kPrefetchResourcePath); + EXPECT_TRUE( + ExecuteScript(shell()->web_contents(), prefetch_injection_script)); + + // Respond to the prefetch request in a way that: + // 1) will enable caching + // 2) won't finish until after CORB has blocked the response. + FetchHistogramsFromChildProcesses(); + base::HistogramTester histograms; + std::string response_bytes = + "HTTP/1.1 200 OK\r\n" + "Cache-Control: public, max-age=10\r\n" + "Content-Type: text/html\r\n" + "X-Content-Type-Options: nosniff\r\n" + "\r\n" + "<p>contents of the response</p>"; + response.WaitForRequest(); + response.Send(response_bytes); + + // Verify that CORB blocked the response. + // TODO(lukasza): https://crbug.com/827633#c5: We might need to switch to + // listening to the onload event below (after/if CORB starts to consistently + // avoid injecting net errors). + std::string wait_script = R"( + function notify_prefetch_is_done() { domAutomationController.send(123); } + + if (window.is_prefetch_done) { + // Can notify immediately if |window.is_prefetch_done| has already been + // set by |prefetch_injection_script|. + notify_prefetch_is_done(); + } else { + // Otherwise wait for CORB's empty response to reach the renderer. + link = document.getElementsByTagName('link')[0]; + link.onerror = notify_prefetch_is_done; + } + )"; + int answer; + EXPECT_TRUE(ExecuteScriptAndExtractInt(shell()->web_contents(), wait_script, + &answer)); + EXPECT_EQ(123, answer); + InspectHistograms(histograms, kShouldBeBlockedWithoutSniffing, "x.html", + RESOURCE_TYPE_PREFETCH); + + // Finish the HTTP response - this should store the response in the cache. + response.Done(); + + // Stop the HTTP server - this means the only way to get the response in + // the |fetch_script| below is to get it from the cache (e.g. if the request + // goes to the network there will be no HTTP server to handle it). + // Note that stopping the HTTP server is not strictly required for the test to + // be robust - ControllableHttpResponse handles only a single request, so + // wouldn't handle the |fetch_script| request even if the HTTP server was + // still running. + EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); + + // Verify that the cached response is available to the same-origin subframe + // (e.g. that the network cache in the browser process got populated despite + // CORB blocking). + const char* fetch_script_template = R"( + fetch('%s') + .then(response => response.text()) + .then(responseBody => { + domAutomationController.send(responseBody); + }) + .catch(error => { + var errorMessage = 'error: ' + error; + console.log(errorMessage); + domAutomationController.send(errorMessage); + }); )"; + std::string fetch_script = + base::StringPrintf(fetch_script_template, kPrefetchResourcePath); + std::string response_body; + EXPECT_TRUE( + ExecuteScriptAndExtractString(shell()->web_contents()->GetAllFrames()[1], + fetch_script, &response_body)); + EXPECT_EQ("<p>contents of the response</p>", response_body); +} + // This test class sets up a service worker that can be used to try to respond // to same-origin requests with cross-origin responses. class CrossSiteDocumentBlockingServiceWorkerTest : public ContentBrowserTest { @@ -735,6 +873,12 @@ class CrossSiteDocumentBlockingServiceWorkerTest : public ContentBrowserTest { // TODO(lukasza): https://crbug.com/715640: This test might become invalid // after servicification of service workers. IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingServiceWorkerTest, NoNetwork) { + // Skip this test when servicification of service workers (S13nServiceWorker) + // is enabled because the browser process doesn't see the request or response + // when the request is handled entirely within the service worker. + if (blink::ServiceWorkerUtils::IsServicificationEnabled()) + return; + SetUpServiceWorker(); base::HistogramTester histograms; @@ -766,6 +910,11 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingServiceWorkerTest, NetworkToServiceWorkerResponse) { SetUpServiceWorker(); + // Make sure that the histograms generated by a service worker registration + // have been recorded. + if (base::FeatureList::IsEnabled(network::features::kNetworkService)) + FetchHistogramsFromChildProcesses(); + // Build a script for XHR-ing a cross-origin, nosniff HTML document. GURL cross_origin_url = GetURLOnCrossOriginServer("/site_isolation/nosniff.txt"); @@ -826,6 +975,7 @@ class CrossSiteDocumentBlockingKillSwitchTest IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingKillSwitchTest, NoBlockingWithKillSwitch) { // Load a page that issues illegal cross-site document requests to bar.com. + embedded_test_server()->StartAcceptingConnections(); GURL foo_url("http://foo.com/cross_site_document_blocking/request.html"); EXPECT_TRUE(NavigateToURL(shell(), foo_url)); @@ -855,6 +1005,7 @@ class CrossSiteDocumentBlockingDisableWebSecurityTest IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingDisableWebSecurityTest, DisableBlocking) { // Load a page that issues illegal cross-site document requests. + embedded_test_server()->StartAcceptingConnections(); GURL foo_url("http://foo.com/cross_site_document_blocking/request.html"); EXPECT_TRUE(NavigateToURL(shell(), foo_url)); @@ -885,6 +1036,7 @@ class CrossSiteDocumentBlockingDisableVsFeatureTest IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingDisableVsFeatureTest, DisableBlocking) { // Load a page that issues illegal cross-site document requests. + embedded_test_server()->StartAcceptingConnections(); GURL foo_url("http://foo.com/cross_site_document_blocking/request.html"); EXPECT_TRUE(NavigateToURL(shell(), foo_url)); @@ -894,23 +1046,9 @@ IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingDisableVsFeatureTest, EXPECT_FALSE(was_blocked); } -// Even without any Site Isolation, document blocking should be turned on by -// default. -IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingBaseTest, - BlockDocumentsByDefault) { - // Load a page that issues illegal cross-site document requests to bar.com. - GURL foo_url("http://foo.com/cross_site_document_blocking/request.html"); - EXPECT_TRUE(NavigateToURL(shell(), foo_url)); - - bool was_blocked; - ASSERT_TRUE(ExecuteScriptAndExtractBool( - shell(), "sendRequest(\"valid.html\");", &was_blocked)); - EXPECT_TRUE(was_blocked); -} - // Test class to verify that documents are blocked for isolated origins as well. class CrossSiteDocumentBlockingIsolatedOriginTest - : public CrossSiteDocumentBlockingBaseTest { + : public CrossSiteDocumentBlockingTest { public: CrossSiteDocumentBlockingIsolatedOriginTest() {} ~CrossSiteDocumentBlockingIsolatedOriginTest() override {} @@ -918,7 +1056,7 @@ class CrossSiteDocumentBlockingIsolatedOriginTest void SetUpCommandLine(base::CommandLine* command_line) override { command_line->AppendSwitchASCII(switches::kIsolateOrigins, "http://bar.com"); - CrossSiteDocumentBlockingBaseTest::SetUpCommandLine(command_line); + CrossSiteDocumentBlockingTest::SetUpCommandLine(command_line); } private: @@ -927,6 +1065,7 @@ class CrossSiteDocumentBlockingIsolatedOriginTest IN_PROC_BROWSER_TEST_F(CrossSiteDocumentBlockingIsolatedOriginTest, BlockDocumentsFromIsolatedOrigin) { + embedded_test_server()->StartAcceptingConnections(); if (AreAllSitesIsolatedForTesting()) return; diff --git a/chromium/content/browser/loader/cross_site_document_resource_handler.cc b/chromium/content/browser/loader/cross_site_document_resource_handler.cc index 9924a53b147..90792270128 100644 --- a/chromium/content/browser/loader/cross_site_document_resource_handler.cc +++ b/chromium/content/browser/loader/cross_site_document_resource_handler.cc @@ -20,6 +20,7 @@ #include "content/browser/loader/detachable_resource_handler.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/site_instance_impl.h" +#include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/resource_context.h" @@ -66,16 +67,15 @@ void CrossSiteDocumentResourceHandler::LogBlockedResponseOnUIThread( if (!web_contents) return; - ukm::UkmRecorder* recorder = ukm::UkmRecorder::Get(); - ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID(); - recorder->UpdateSourceURL(source_id, web_contents->GetLastCommittedURL()); + ukm::SourceId source_id = static_cast<WebContentsImpl*>(web_contents) + ->GetUkmSourceIdForLastCommittedSource(); ukm::builders::SiteIsolation_XSD_Browser_Blocked(source_id) .SetCanonicalMimeType(static_cast<int64_t>(canonical_mime_type)) .SetContentLengthWasZero(content_length == 0) .SetContentResourceType(resource_type) .SetHttpResponseCode(http_response_code) .SetNeededSniffing(needed_sniffing) - .Record(recorder); + .Record(ukm::UkmRecorder::Get()); } void CrossSiteDocumentResourceHandler::LogBlockedResponse( @@ -166,6 +166,14 @@ class CrossSiteDocumentResourceHandler::Controller : public ResourceController { } } + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; + Resume(); + } + void Cancel() override { MarkAsUsed(); document_handler_->Cancel(); @@ -212,8 +220,6 @@ void CrossSiteDocumentResourceHandler::OnResponseStarted( network::ResourceResponse* response, std::unique_ptr<ResourceController> controller) { has_response_started_ = true; - http_response_code_ = - response->head.headers ? response->head.headers->response_code() : 0; network::CrossOriginReadBlocking::LogAction( network::CrossOriginReadBlocking::Action::kResponseStarted); @@ -388,8 +394,8 @@ void CrossSiteDocumentResourceHandler::OnReadCompleted( // If we have some new data, ask the |analyzer_| to sniff it. analyzer_->SniffResponseBody(data, new_data_offset); - const bool confirmed_allowed = analyzer_->should_allow(); - confirmed_blockable = analyzer_->should_block(); + const bool confirmed_allowed = analyzer_->ShouldAllow(); + confirmed_blockable = analyzer_->ShouldBlock(); DCHECK(!(confirmed_blockable && confirmed_allowed)); // If sniffing didn't yield a conclusive response, and we haven't read too @@ -425,11 +431,13 @@ void CrossSiteDocumentResourceHandler::OnReadCompleted( : "null", "url", request()->url().spec()); - LogBlockedResponse(info, http_response_code_); + LogBlockedResponse(info, analyzer_->http_response_code()); // Block the response and throw away the data. Report zero bytes read. blocked_read_completed_ = true; - info->set_blocked_cross_site_document(true); + info->set_blocked_response_from_reaching_renderer(true); + if (analyzer_->ShouldReportBlockedResponse()) + info->set_should_report_corb_blocking(true); network::CrossOriginReadBlocking::SanitizeBlockedResponse( pending_response_start_); @@ -535,10 +543,17 @@ void CrossSiteDocumentResourceHandler::OnResponseCompleted( bool CrossSiteDocumentResourceHandler::ShouldBlockBasedOnHeaders( const network::ResourceResponse& response) { + // Give embedder a chance to skip document blocking for this response. + const char* initiator_scheme_exception = + GetContentClient() + ->browser() + ->GetInitatorSchemeBypassingDocumentBlocking(); + + // Delegate most decisions to CrossOriginReadBlocking::ResponseAnalyzer. analyzer_ = std::make_unique<network::CrossOriginReadBlocking::ResponseAnalyzer>( - *request(), response); - if (analyzer_->should_allow()) + *request(), response, initiator_scheme_exception); + if (analyzer_->ShouldAllow()) return false; // Check if the response's site needs to have its documents protected. By @@ -567,21 +582,12 @@ bool CrossSiteDocumentResourceHandler::ShouldBlockBasedOnHeaders( if (!info || info->GetChildID() == -1) return false; - // Give embedder a chance to skip document blocking for this response. - const char* initiator_scheme_exception = - GetContentClient() - ->browser() - ->GetInitatorSchemeBypassingDocumentBlocking(); - if (initiator_scheme_exception && request()->initiator().has_value() && - request()->initiator()->scheme() == initiator_scheme_exception) { - return false; - } - - // Don't block plugin requests with universal access (e.g., Flash). Such - // requests are made without CORS, and thus dont have an Origin request - // header. Other plugin requests (e.g., NaCl) are made using CORS and have an - // Origin request header. If they fail the CORS check above, they should be - // blocked. + // Don't block plugin requests. + // TODO(lukasza): Only disable CORB for plugins with universal access (see + // PepperURLLoaderHost::has_universal_access_), because only such plugins may + // have their own CORS-like mechanisms - e.g. crossdomain.xml in Flash). We + // should still enforce CORB for other kinds of plugins (i.e. ones without + // universal access). if (info->GetResourceType() == RESOURCE_TYPE_PLUGIN_RESOURCE && is_nocors_plugin_request_) { return false; diff --git a/chromium/content/browser/loader/cross_site_document_resource_handler.h b/chromium/content/browser/loader/cross_site_document_resource_handler.h index 447c6ed2699..1598fb024bd 100644 --- a/chromium/content/browser/loader/cross_site_document_resource_handler.h +++ b/chromium/content/browser/loader/cross_site_document_resource_handler.h @@ -164,10 +164,6 @@ class CONTENT_EXPORT CrossSiteDocumentResourceHandler // completed, and thus it is safe to cancel or detach on the next read. bool blocked_read_completed_ = false; - // The HTTP response code (e.g. 200 or 404) received in response to this - // resource request. - int http_response_code_ = 0; - base::WeakPtrFactory<CrossSiteDocumentResourceHandler> weak_this_; DISALLOW_COPY_AND_ASSIGN(CrossSiteDocumentResourceHandler); diff --git a/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc b/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc index 7f66b566c8b..99405d1b48e 100644 --- a/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/cross_site_document_resource_handler_unittest.cc @@ -20,7 +20,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" -#include "base/test/histogram_tester.h" +#include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" #include "content/browser/loader/intercepting_resource_handler.h" #include "content/browser/loader/mime_sniffing_resource_handler.h" diff --git a/chromium/content/browser/loader/data_pipe_to_source_stream.cc b/chromium/content/browser/loader/data_pipe_to_source_stream.cc index 8a5c372798f..4a3694f5b01 100644 --- a/chromium/content/browser/loader/data_pipe_to_source_stream.cc +++ b/chromium/content/browser/loader/data_pipe_to_source_stream.cc @@ -4,6 +4,8 @@ #include "content/browser/loader/data_pipe_to_source_stream.h" +#include <utility> + #include "base/auto_reset.h" #include "net/base/io_buffer.h" @@ -30,7 +32,7 @@ std::string DataPipeToSourceStream::Description() const { int DataPipeToSourceStream::Read(net::IOBuffer* buf, int buf_size, - const net::CompletionCallback& callback) { + net::CompletionOnceCallback callback) { base::AutoReset<bool>(&inside_read_, true); if (!body_.get()) { @@ -56,7 +58,7 @@ int DataPipeToSourceStream::Read(net::IOBuffer* buf, return 0; case MOJO_RESULT_SHOULD_WAIT: // Data is not available yet. - pending_callback_ = callback; + pending_callback_ = std::move(callback); output_buf_ = buf; output_buf_size_ = buf_size; handle_watcher_.ArmOrNotify(); diff --git a/chromium/content/browser/loader/data_pipe_to_source_stream.h b/chromium/content/browser/loader/data_pipe_to_source_stream.h index e45d2482e87..f77287f0611 100644 --- a/chromium/content/browser/loader/data_pipe_to_source_stream.h +++ b/chromium/content/browser/loader/data_pipe_to_source_stream.h @@ -8,6 +8,7 @@ #include "content/common/content_export.h" #include "mojo/public/cpp/system/data_pipe.h" #include "mojo/public/cpp/system/simple_watcher.h" +#include "net/base/completion_once_callback.h" #include "net/filter/source_stream.h" namespace content { @@ -19,7 +20,7 @@ class CONTENT_EXPORT DataPipeToSourceStream final : public net::SourceStream { int Read(net::IOBuffer* buf, int buf_size, - const net::CompletionCallback& callback) override; + net::CompletionOnceCallback callback) override; std::string Description() const override; private: @@ -33,7 +34,7 @@ class CONTENT_EXPORT DataPipeToSourceStream final : public net::SourceStream { scoped_refptr<net::IOBuffer> output_buf_; int output_buf_size_ = 0; - net::CompletionCallback pending_callback_; + net::CompletionOnceCallback pending_callback_; DISALLOW_COPY_AND_ASSIGN(DataPipeToSourceStream); }; diff --git a/chromium/content/browser/loader/detachable_resource_handler.cc b/chromium/content/browser/loader/detachable_resource_handler.cc index b8222d203d1..26593e97ec0 100644 --- a/chromium/content/browser/loader/detachable_resource_handler.cc +++ b/chromium/content/browser/loader/detachable_resource_handler.cc @@ -38,6 +38,14 @@ class DetachableResourceHandler::Controller : public ResourceController { detachable_handler_->ResumeInternal(); } + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; + Resume(); + } + void Cancel() override { MarkAsUsed(); detachable_handler_->Cancel(); @@ -249,13 +257,6 @@ void DetachableResourceHandler::OnResponseCompleted( std::make_unique<Controller>(this)); } -void DetachableResourceHandler::OnDataDownloaded(int bytes_downloaded) { - if (!next_handler_) - return; - - next_handler_->OnDataDownloaded(bytes_downloaded); -} - void DetachableResourceHandler::ResumeInternal() { parent_read_buffer_ = nullptr; parent_read_buffer_size_ = nullptr; diff --git a/chromium/content/browser/loader/detachable_resource_handler.h b/chromium/content/browser/loader/detachable_resource_handler.h index 972ffd13523..9e7e418a2f2 100644 --- a/chromium/content/browser/loader/detachable_resource_handler.h +++ b/chromium/content/browser/loader/detachable_resource_handler.h @@ -69,7 +69,6 @@ class CONTENT_EXPORT DetachableResourceHandler : public ResourceHandler { void OnResponseCompleted( const net::URLRequestStatus& status, std::unique_ptr<ResourceController> controller) override; - void OnDataDownloaded(int bytes_downloaded) override; private: class Controller; diff --git a/chromium/content/browser/loader/navigation_loader_util.cc b/chromium/content/browser/loader/download_utils_impl.cc index 96a7879b520..4af0a41e592 100644 --- a/chromium/content/browser/loader/navigation_loader_util.cc +++ b/chromium/content/browser/loader/download_utils_impl.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "content/browser/loader/navigation_loader_util.h" +#include "content/browser/loader/download_utils_impl.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" @@ -10,13 +10,12 @@ #include "net/http/http_response_headers.h" #include "third_party/blink/public/common/mime_util/mime_util.h" #include "url/gurl.h" -#include "url/origin.h" namespace content { -namespace navigation_loader_util { +namespace download_utils { bool MustDownload(const GURL& url, - net::HttpResponseHeaders* headers, + const net::HttpResponseHeaders* headers, const std::string& mime_type) { if (headers) { std::string disposition; @@ -43,7 +42,7 @@ bool MustDownload(const GURL& url, } bool IsDownload(const GURL& url, - net::HttpResponseHeaders* headers, + const net::HttpResponseHeaders* headers, const std::string& mime_type) { if (MustDownload(url, headers, mime_type)) return true; @@ -54,5 +53,5 @@ bool IsDownload(const GURL& url, return !headers || headers->response_code() / 100 == 2; } -} // namespace navigation_loader_util +} // namespace download_utils } // namespace content diff --git a/chromium/content/browser/loader/download_utils_impl.h b/chromium/content/browser/loader/download_utils_impl.h new file mode 100644 index 00000000000..724e45ba529 --- /dev/null +++ b/chromium/content/browser/loader/download_utils_impl.h @@ -0,0 +1,22 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_LOADER_DOWNLOAD_UTILS_IMPL_H_ +#define CONTENT_BROWSER_LOADER_DOWNLOAD_UTILS_IMPL_H_ + +#include "content/public/browser/download_utils.h" + +namespace content { +namespace download_utils { + +// Determines whether given response would result in a download. +// Note this doesn't handle the case when a plugin exists for the |mime_type|. +bool IsDownload(const GURL& url, + const net::HttpResponseHeaders* headers, + const std::string& mime_type); + +} // namespace download_utils +} // namespace content + +#endif // CONTENT_BROWSER_LOADER_DOWNLOAD_UTILS_IMPL_H_ diff --git a/chromium/content/browser/loader/downloaded_temp_file_impl.cc b/chromium/content/browser/loader/downloaded_temp_file_impl.cc deleted file mode 100644 index f699d84df04..00000000000 --- a/chromium/content/browser/loader/downloaded_temp_file_impl.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/downloaded_temp_file_impl.h" - -#include "content/browser/loader/resource_dispatcher_host_impl.h" -#include "mojo/public/cpp/bindings/strong_binding.h" - -namespace content { - -// static -network::mojom::DownloadedTempFilePtr DownloadedTempFileImpl::Create( - int child_id, - int request_id) { - mojo::InterfacePtr<network::mojom::DownloadedTempFile> ptr; - mojo::MakeStrongBinding( - std::make_unique<DownloadedTempFileImpl>(child_id, request_id), - mojo::MakeRequest(&ptr)); - return ptr; -} - -DownloadedTempFileImpl::~DownloadedTempFileImpl() { - ResourceDispatcherHostImpl::Get()->UnregisterDownloadedTempFile(child_id_, - request_id_); -} -DownloadedTempFileImpl::DownloadedTempFileImpl(int child_id, int request_id) - : child_id_(child_id), request_id_(request_id) {} - -} // namespace content diff --git a/chromium/content/browser/loader/downloaded_temp_file_impl.h b/chromium/content/browser/loader/downloaded_temp_file_impl.h deleted file mode 100644 index 07deff40610..00000000000 --- a/chromium/content/browser/loader/downloaded_temp_file_impl.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_DOWNLOADED_TEMP_FILE_IMPL_H_ -#define CONTENT_BROWSER_LOADER_DOWNLOADED_TEMP_FILE_IMPL_H_ - -#include "base/macros.h" -#include "content/common/content_export.h" -#include "services/network/public/mojom/url_loader_factory.mojom.h" - -namespace content { - -// DownloadedTempFileImpl is created on the download_to_file mode of resource -// loading. The instance is held by the client and lives until the client -// destroys the interface, slightly after the URLLoader is destroyed. -class CONTENT_EXPORT DownloadedTempFileImpl final - : public network::mojom::DownloadedTempFile { - public: - // Creates a DownloadedTempFileImpl, binds it as a strong interface, and - // returns the interface ptr to be passed to the client. - // That means: - // * The DownloadedTempFile object created here is essentially owned by the - // client. It keeps alive until the client destroys the other endpoint. - static network::mojom::DownloadedTempFilePtr Create(int child_id, - int request_id); - - DownloadedTempFileImpl(int child_id, int request_id); - ~DownloadedTempFileImpl() override; - - private: - const int child_id_; - const int request_id_; - - DISALLOW_COPY_AND_ASSIGN(DownloadedTempFileImpl); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_DOWNLOADED_TEMP_FILE_IMPL_H_ diff --git a/chromium/content/browser/loader/intercepting_resource_handler.cc b/chromium/content/browser/loader/intercepting_resource_handler.cc index 0b038d238a1..a7c3a1010ea 100644 --- a/chromium/content/browser/loader/intercepting_resource_handler.cc +++ b/chromium/content/browser/loader/intercepting_resource_handler.cc @@ -29,6 +29,14 @@ class InterceptingResourceHandler::Controller : public ResourceController { intercepting_handler_->ResumeInternal(); } + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; + Resume(); + } + void Cancel() override { MarkAsUsed(); intercepting_handler_->Cancel(); diff --git a/chromium/content/browser/loader/layered_resource_handler.cc b/chromium/content/browser/loader/layered_resource_handler.cc index 340025a4b70..9a163e7d18b 100644 --- a/chromium/content/browser/loader/layered_resource_handler.cc +++ b/chromium/content/browser/loader/layered_resource_handler.cc @@ -69,9 +69,4 @@ void LayeredResourceHandler::OnResponseCompleted( next_handler_->OnResponseCompleted(status, std::move(controller)); } -void LayeredResourceHandler::OnDataDownloaded(int bytes_downloaded) { - DCHECK(next_handler_.get()); - next_handler_->OnDataDownloaded(bytes_downloaded); -} - } // namespace content diff --git a/chromium/content/browser/loader/layered_resource_handler.h b/chromium/content/browser/loader/layered_resource_handler.h index 47d37bca55f..2aa0e245127 100644 --- a/chromium/content/browser/loader/layered_resource_handler.h +++ b/chromium/content/browser/loader/layered_resource_handler.h @@ -45,7 +45,6 @@ class CONTENT_EXPORT LayeredResourceHandler : public ResourceHandler { void OnResponseCompleted( const net::URLRequestStatus& status, std::unique_ptr<ResourceController> controller) override; - void OnDataDownloaded(int bytes_downloaded) override; std::unique_ptr<ResourceHandler> next_handler_; }; diff --git a/chromium/content/browser/loader/loader_browsertest.cc b/chromium/content/browser/loader/loader_browsertest.cc index 450919d3c3b..024fadee921 100644 --- a/chromium/content/browser/loader/loader_browsertest.cc +++ b/chromium/content/browser/loader/loader_browsertest.cc @@ -273,17 +273,20 @@ namespace { // Responds with a HungResponse for the specified URL to hang on the request. // If the network service is enabled, crashes the process. If it's disabled, // cancels all requests from specifield |child_id|. +// +// |crash_network_service_callback| crashes the network service when invoked, +// and must be called on the UI thread. std::unique_ptr<net::test_server::HttpResponse> CancelOnRequest( const std::string& relative_url, int child_id, + base::RepeatingClosure crash_network_service_callback, const net::test_server::HttpRequest& request) { if (request.relative_url != relative_url) return nullptr; if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(SimulateNetworkServiceCrash)); + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + crash_network_service_callback); } else { content::BrowserThread::PostTask( content::BrowserThread::IO, FROM_HERE, @@ -303,7 +306,9 @@ std::unique_ptr<net::test_server::HttpResponse> CancelOnRequest( IN_PROC_BROWSER_TEST_F(LoaderBrowserTest, SyncXMLHttpRequest_Cancelled) { embedded_test_server()->RegisterRequestHandler(base::Bind( &CancelOnRequest, "/hung", - shell()->web_contents()->GetMainFrame()->GetProcess()->GetID())); + shell()->web_contents()->GetMainFrame()->GetProcess()->GetID(), + base::BindRepeating(&BrowserTestBase::SimulateNetworkServiceCrash, + base::Unretained(this)))); ASSERT_TRUE(embedded_test_server()->Start()); WaitForLoadStop(shell()->web_contents()); @@ -813,25 +818,6 @@ IN_PROC_BROWSER_TEST_F(PreviewsStateBrowserTest, CheckResourcesRequested(true); } -// Test that reloading with Lo-Fi disabled doesn't call ShouldEnableLoFiMode and -// already has LOFI_OFF. -IN_PROC_BROWSER_TEST_F(PreviewsStateBrowserTest, - ShouldEnableLoFiModeReloadDisableLoFi) { - // Navigate with GetPreviewsState returning SERVER_LOFI_ON. - Reset(SERVER_LOFI_ON); - NavigateToURLBlockUntilNavigationsComplete( - shell(), embedded_test_server()->GetURL("/page_with_iframe.html"), 1); - CheckResourcesRequested(true); - - // Reload with Lo-Fi disabled. - Reset(PREVIEWS_NO_TRANSFORM); - TestNavigationObserver tab_observer(shell()->web_contents(), 1); - shell()->web_contents()->GetController().Reload(ReloadType::DISABLE_PREVIEWS, - true); - tab_observer.Wait(); - CheckResourcesRequested(false); -} - namespace { struct RequestData { @@ -871,6 +857,19 @@ class RequestDataBrowserTest : public ContentBrowserTest { return copy; } + void WaitForRequests(size_t count) { + while (true) { + base::RunLoop run_loop; + { + base::AutoLock auto_lock(requests_lock_); + if (requests_.size() == count) + return; + requests_closure_ = run_loop.QuitClosure(); + } + run_loop.Run(); + } + } + private: void SetUpOnMainThread() override { ContentBrowserTest::SetUpOnMainThread(); @@ -893,10 +892,13 @@ class RequestDataBrowserTest : public ContentBrowserTest { void RequestCreated(RequestData data) { base::AutoLock auto_lock(requests_lock_); requests_.push_back(data); + if (requests_closure_) + requests_closure_.Run(); } base::Lock requests_lock_; std::vector<RequestData> requests_; + base::Closure requests_closure_; std::unique_ptr<URLLoaderInterceptor> interceptor_; }; @@ -928,6 +930,7 @@ IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, LinkRelPrefetch) { url::Origin top_origin = url::Origin::Create(top_url); NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1); + WaitForRequests(2u); auto requests = data(); EXPECT_EQ(2u, requests.size()); @@ -944,6 +947,7 @@ IN_PROC_BROWSER_TEST_F(RequestDataBrowserTest, LinkRelPrefetchReferrerPolicy) { url::Origin top_origin = url::Origin::Create(top_url); NavigateToURLBlockUntilNavigationsComplete(shell(), top_url, 1); + WaitForRequests(2u); auto requests = data(); EXPECT_EQ(2u, requests.size()); diff --git a/chromium/content/browser/loader/merkle_integrity_source_stream.cc b/chromium/content/browser/loader/merkle_integrity_source_stream.cc index 4643113b761..94c532f18e2 100644 --- a/chromium/content/browser/loader/merkle_integrity_source_stream.cc +++ b/chromium/content/browser/loader/merkle_integrity_source_stream.cc @@ -19,7 +19,7 @@ namespace { // maximum record size in TLS and the default maximum frame size in HTTP/2. constexpr uint64_t kMaxRecordSize = 16 * 1024; -constexpr char kMiSha256Header[] = "mi-sha256="; +constexpr char kMiSha256Header[] = "mi-sha256-draft2="; constexpr size_t kMiSha256HeaderLength = sizeof(kMiSha256Header) - 1; // Copies as many bytes from |input| as will fit in |output| and advances both. diff --git a/chromium/content/browser/loader/merkle_integrity_source_stream_unittest.cc b/chromium/content/browser/loader/merkle_integrity_source_stream_unittest.cc index dc1d802e457..61c61692ec5 100644 --- a/chromium/content/browser/loader/merkle_integrity_source_stream_unittest.cc +++ b/chromium/content/browser/loader/merkle_integrity_source_stream_unittest.cc @@ -17,13 +17,13 @@ const int kBigBufferSize = 4096; const int kSmallBufferSize = 1; const char kMIEmptyBody[] = - "mi-sha256=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0"; + "mi-sha256-draft2=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0"; const char kMISingleRecord[] = - "mi-sha256=dcRDgR2GM35DluAV13PzgnG6-pvQwPywfFvAu1UeFrs"; + "mi-sha256-draft2=dcRDgR2GM35DluAV13PzgnG6-pvQwPywfFvAu1UeFrs"; const char kMIMultipleRecords[] = - "mi-sha256=IVa9shfs0nyKEhHqtB3WVNANJ2Njm5KjQLjRtnbkYJ4"; + "mi-sha256-draft2=IVa9shfs0nyKEhHqtB3WVNANJ2Njm5KjQLjRtnbkYJ4"; const char kMIWholeNumberOfRecords[] = - "mi-sha256=L2vdwBplKvIr0ZPkcuskWZfEVDgVdHa6aD363UpKuZs"; + "mi-sha256-draft2=L2vdwBplKvIr0ZPkcuskWZfEVDgVdHa6aD363UpKuZs"; enum class ReadResultType { // Each call to AddReadResult is a separate read from the lower layer @@ -180,7 +180,7 @@ TEST_P(MerkleIntegritySourceStreamTest, MalformedMIHeader) { } TEST_P(MerkleIntegritySourceStreamTest, WrongMIAttributeName) { - Init("mi-sha255=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0"); + Init("mi-sha256-draft1=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0"); source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode); std::string actual_output; int result = ReadStream(&actual_output); @@ -188,7 +188,7 @@ TEST_P(MerkleIntegritySourceStreamTest, WrongMIAttributeName) { } TEST_P(MerkleIntegritySourceStreamTest, HashTooShort) { - Init("mi-sha256=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoA"); + Init("mi-sha256-draft2=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoA"); source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode); std::string actual_output; int result = ReadStream(&actual_output); @@ -196,7 +196,7 @@ TEST_P(MerkleIntegritySourceStreamTest, HashTooShort) { } TEST_P(MerkleIntegritySourceStreamTest, HashTooLong) { - Init("mi-sha256=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0A"); + Init("mi-sha256-draft2=bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0A"); source()->AddReadResult(nullptr, 0, net::OK, GetParam().mode); std::string actual_output; int result = ReadStream(&actual_output); @@ -470,7 +470,7 @@ TEST_P(MerkleIntegritySourceStreamTest, Truncated) { // represent the empty string. Update the code and possibly this test depending // on how https://github.com/martinthomson/http-mice/issues/3 is resolved. TEST_P(MerkleIntegritySourceStreamTest, EmptyFinalRecord) { - Init("mi-sha256=JJnIuaOEc2247K9V88VQAQy1GJuQ6ylaVM7mG69QkE4"); + Init("mi-sha256-draft2=JJnIuaOEc2247K9V88VQAQy1GJuQ6ylaVM7mG69QkE4"); const uint8_t kRecordSize[] = {0, 0, 0, 0, 0, 0, 0, 16}; const std::string kMessage( "When I grow up, I want to be a watermelon!! \xf0\x9f\x8d\x89"); diff --git a/chromium/content/browser/loader/mime_sniffing_resource_handler.cc b/chromium/content/browser/loader/mime_sniffing_resource_handler.cc index bed70acf54d..89e2c5351c4 100644 --- a/chromium/content/browser/loader/mime_sniffing_resource_handler.cc +++ b/chromium/content/browser/loader/mime_sniffing_resource_handler.cc @@ -24,7 +24,6 @@ #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/loader/stream_resource_handler.h" #include "content/browser/web_package/signed_exchange_utils.h" -#include "content/browser/web_package/web_package_request_handler.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/plugin_service.h" #include "content/public/browser/resource_context.h" @@ -75,6 +74,14 @@ class MimeSniffingResourceHandler::Controller : public ResourceController { mime_handler_->ResumeInternal(); } + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; + Resume(); + } + void Cancel() override { MarkAsUsed(); mime_handler_->Cancel(); diff --git a/chromium/content/browser/loader/mock_resource_loader.cc b/chromium/content/browser/loader/mock_resource_loader.cc index 7444b0ad792..e43c857841a 100644 --- a/chromium/content/browser/loader/mock_resource_loader.cc +++ b/chromium/content/browser/loader/mock_resource_loader.cc @@ -10,6 +10,7 @@ #include "content/browser/loader/resource_controller.h" #include "content/browser/loader/resource_handler.h" #include "net/base/io_buffer.h" +#include "net/http/http_request_headers.h" #include "net/url_request/url_request_status.h" #include "services/network/public/cpp/resource_response.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,6 +25,14 @@ class MockResourceLoader::TestResourceController : public ResourceController { void Resume() override { mock_loader_->OnResume(); } + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override { + DCHECK(!modified_request_headers.has_value()) + << "Redirect with modified headers was not supported yet. " + "crbug.com/845683"; + Resume(); + } + void Cancel() override { CancelWithError(net::ERR_ABORTED); } void CancelWithError(int error_code) override { diff --git a/chromium/content/browser/loader/mojo_async_resource_handler.cc b/chromium/content/browser/loader/mojo_async_resource_handler.cc index 08d0708f93c..4910201a2d1 100644 --- a/chromium/content/browser/loader/mojo_async_resource_handler.cc +++ b/chromium/content/browser/loader/mojo_async_resource_handler.cc @@ -12,9 +12,7 @@ #include "base/location.h" #include "base/logging.h" #include "base/macros.h" -#include "base/metrics/histogram_macros.h" #include "base/time/time.h" -#include "content/browser/loader/downloaded_temp_file_impl.h" #include "content/browser/loader/resource_controller.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" @@ -40,24 +38,19 @@ constexpr size_t kMinAllocationSize = 2 * net::kMaxBytesToSniff; constexpr size_t kMaxChunkSize = 32 * 1024; -// Records histograms for the time spent between several events in the -// MojoAsyncResourceHandler for a navigation. -// - |response_started| is when the response's headers and metadata are -// available. Loading is paused at this time. -// - |proceed_with_response| is when loading is resumed. -// - |first_read_completed| is when the first part of the body has been read. -void RecordNavigationResourceHandlerMetrics( - base::TimeTicks response_started, - base::TimeTicks proceed_with_response, - base::TimeTicks first_read_completed) { - UMA_HISTOGRAM_TIMES( - "Navigation.ResourceHandler." - "ResponseStartedUntilProceedWithResponse", - proceed_with_response - response_started); - UMA_HISTOGRAM_TIMES( - "Navigation.ResourceHandler." - "ProceedWithResponseUntilFirstReadCompleted", - first_read_completed - proceed_with_response); +// Time between sending the transfer size updates to renderer. This threshold is +// chosen as a compromise between sending too frequent updates and the limit its +// consumers (DevTools and page load metrics) expect. +constexpr base::TimeDelta kTransferSizeReportInterval = + base::TimeDelta::FromMilliseconds(500); + +bool ShouldReportTransferSize( + const ResourceRequestInfoImpl* resource_request_info) { + // Transfer size is reported only when report_raw_headers is set or the + // renderer is allowed to receive the resource response metadata (e.g. by + // Cross-Origin Read Blocking). + return resource_request_info->ShouldReportRawHeaders() || + !resource_request_info->blocked_response_from_reaching_renderer(); } } // namespace @@ -124,6 +117,7 @@ MojoAsyncResourceHandler::MojoAsyncResourceHandler( url_loader_client_(std::move(url_loader_client)), weak_factory_(this) { DCHECK(IsResourceTypeFrame(resource_type) || + resource_type == RESOURCE_TYPE_SERVICE_WORKER || !(url_loader_options_ & network::mojom::kURLLoadOptionSendSSLInfoWithResponse)); DCHECK(resource_type == RESOURCE_TYPE_MAIN_FRAME || @@ -178,37 +172,26 @@ void MojoAsyncResourceHandler::OnResponseStarted( network::ResourceResponse* response, std::unique_ptr<ResourceController> controller) { DCHECK(!has_controller()); - time_response_started_ = base::TimeTicks::Now(); if (upload_progress_tracker_) { upload_progress_tracker_->OnUploadCompleted(); upload_progress_tracker_ = nullptr; } - const ResourceRequestInfoImpl* info = GetRequestInfo(); response->head.encoded_data_length = request()->raw_header_size(); reported_total_received_bytes_ = response->head.encoded_data_length; response->head.request_start = request()->creation_time(); - response->head.response_start = time_response_started_; + response->head.response_start = base::TimeTicks::Now(); sent_received_response_message_ = true; - network::mojom::DownloadedTempFilePtr downloaded_file_ptr; - if (!response->head.download_file_path.empty()) { - downloaded_file_ptr = DownloadedTempFileImpl::Create(info->GetChildID(), - info->GetRequestID()); - rdh_->RegisterDownloadedTempFile(info->GetChildID(), info->GetRequestID(), - response->head.download_file_path); - } - if ((url_loader_options_ & network::mojom::kURLLoadOptionSendSSLInfoWithResponse) && request()->ssl_info().cert) { response->head.ssl_info = request()->ssl_info(); } - url_loader_client_->OnReceiveResponse(response->head, - std::move(downloaded_file_ptr)); + url_loader_client_->OnReceiveResponse(response->head); net::IOBufferWithSize* metadata = GetResponseMetadata(request()); if (metadata) { @@ -335,21 +318,18 @@ void MojoAsyncResourceHandler::OnReadCompleted( return; } - const ResourceRequestInfoImpl* info = GetRequestInfo(); - if (info->ShouldReportRawHeaders()) { + if (ShouldReportTransferSize(GetRequestInfo()) && + (time_transfer_size_next_report_.is_null() || + time_transfer_size_next_report_ <= base::TimeTicks::Now())) { auto transfer_size_diff = CalculateRecentlyReceivedBytes(); - if (transfer_size_diff > 0) + if (transfer_size_diff > 0) { url_loader_client_->OnTransferSizeUpdated(transfer_size_diff); + time_transfer_size_next_report_ = + base::TimeTicks::Now() + kTransferSizeReportInterval; + } } if (response_body_consumer_handle_.is_valid()) { - if (url_loader_options_ & - network::mojom::kURLLoadOptionPauseOnResponseStarted) { - base::TimeTicks time_first_read_completed = base::TimeTicks::Now(); - RecordNavigationResourceHandlerMetrics(time_response_started_, - time_proceed_with_response_, - time_first_read_completed); - } // Send the data pipe on the first OnReadCompleted call. url_loader_client_->OnStartLoadingResponseBody( std::move(response_body_consumer_handle_)); @@ -384,16 +364,10 @@ void MojoAsyncResourceHandler::OnReadCompleted( controller->Resume(); } -void MojoAsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { - url_loader_client_->OnDataDownloaded(bytes_downloaded, - CalculateRecentlyReceivedBytes()); -} - void MojoAsyncResourceHandler::FollowRedirect( + const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { - DCHECK(!modified_request_headers.has_value()) << "Redirect with modified " - "headers was not supported " - "yet. crbug.com/845683"; if (!request()->status().is_success()) { DVLOG(1) << "FollowRedirect for invalid request"; return; @@ -408,14 +382,12 @@ void MojoAsyncResourceHandler::FollowRedirect( DCHECK(!did_defer_on_writing_); did_defer_on_redirect_ = false; request()->LogUnblocked(); - Resume(); + ResumeForRedirect(modified_request_headers); } void MojoAsyncResourceHandler::ProceedWithResponse() { DCHECK(did_defer_on_response_started_); - time_proceed_with_response_ = base::TimeTicks::Now(); - request()->LogUnblocked(); Resume(); } @@ -524,8 +496,8 @@ void MojoAsyncResourceHandler::OnResponseCompleted( loader_status.encoded_data_length = request()->GetTotalReceivedBytes(); loader_status.encoded_body_length = request()->GetRawBodyBytes(); loader_status.decoded_body_length = total_written_bytes_; - loader_status.blocked_cross_site_document = - GetRequestInfo()->blocked_cross_site_document(); + loader_status.should_report_corb_blocking = + GetRequestInfo()->should_report_corb_blocking(); if ((url_loader_options_ & network::mojom::kURLLoadOptionSendSSLInfoForCertificateError) && @@ -534,6 +506,12 @@ void MojoAsyncResourceHandler::OnResponseCompleted( loader_status.ssl_info = request()->ssl_info(); } + if (ShouldReportTransferSize(GetRequestInfo())) { + auto transfer_size_diff = CalculateRecentlyReceivedBytes(); + if (transfer_size_diff > 0) + url_loader_client_->OnTransferSizeUpdated(transfer_size_diff); + } + url_loader_client_->OnComplete(loader_status); controller->Resume(); } diff --git a/chromium/content/browser/loader/mojo_async_resource_handler.h b/chromium/content/browser/loader/mojo_async_resource_handler.h index c612b2f72e2..ad87065771e 100644 --- a/chromium/content/browser/loader/mojo_async_resource_handler.h +++ b/chromium/content/browser/loader/mojo_async_resource_handler.h @@ -13,7 +13,6 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/time/time.h" #include "content/browser/loader/resource_handler.h" #include "content/common/content_export.h" #include "content/public/common/resource_type.h" @@ -82,10 +81,11 @@ class CONTENT_EXPORT MojoAsyncResourceHandler void OnResponseCompleted( const net::URLRequestStatus& status, std::unique_ptr<ResourceController> controller) override; - void OnDataDownloaded(int bytes_downloaded) override; // network::mojom::URLLoader implementation: - void FollowRedirect(const base::Optional<net::HttpRequestHeaders>& + void FollowRedirect(const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, @@ -149,13 +149,12 @@ class CONTENT_EXPORT MojoAsyncResourceHandler bool did_defer_on_writing_ = false; bool did_defer_on_redirect_ = false; bool did_defer_on_response_started_ = false; + + // The time transfer size should be reported next. + base::TimeTicks time_transfer_size_next_report_; int64_t reported_total_received_bytes_ = 0; int64_t total_written_bytes_ = 0; - // Used for UMA histograms. - base::TimeTicks time_response_started_; - base::TimeTicks time_proceed_with_response_; - // Pointer to parent's information about the read buffer. Only non-null while // OnWillRead is deferred. scoped_refptr<net::IOBuffer>* parent_buffer_ = nullptr; diff --git a/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc b/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc index f6ca9da8125..a4170df9f58 100644 --- a/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc +++ b/chromium/content/browser/loader/mojo_async_resource_handler_unittest.cc @@ -10,6 +10,7 @@ #include <vector> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/callback.h" #include "base/files/file_path.h" #include "base/logging.h" @@ -49,7 +50,9 @@ #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_job_factory_impl.h" #include "net/url_request/url_request_status.h" +#include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" #include "services/network/public/cpp/resource_response.h" #include "services/network/public/cpp/url_loader_completion_status.h" @@ -304,9 +307,9 @@ class MojoAsyncResourceHandlerTestBase { // Create and initialize |request_|. None of this matters, for these tests, // just need something non-NULL. - net::URLRequestContext* request_context = + request_context_ = browser_context_->GetResourceContext()->GetRequestContext(); - request_ = request_context->CreateRequest( + request_ = request_context_->CreateRequest( GURL("http://foo/"), net::DEFAULT_PRIORITY, &url_request_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS); request_->set_upload(std::move(upload_stream)); @@ -403,6 +406,7 @@ class MojoAsyncResourceHandlerTestBase { network::TestURLLoaderClient url_loader_client_; std::unique_ptr<TestBrowserContext> browser_context_; net::TestDelegate url_request_delegate_; + net::URLRequestContext* request_context_; std::unique_ptr<net::URLRequest> request_; std::unique_ptr<MojoAsyncResourceHandlerWithStubOperations> handler_; std::unique_ptr<MockResourceLoader> mock_loader_; @@ -1211,7 +1215,7 @@ TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, RedirectHandling) { ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, mock_loader_->status()); - handler_->FollowRedirect(base::nullopt); + handler_->FollowRedirect(base::nullopt, base::nullopt); ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); url_loader_client_.ClearHasReceivedRedirect(); @@ -1232,7 +1236,7 @@ TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, RedirectHandling) { ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, mock_loader_->status()); - handler_->FollowRedirect(base::nullopt); + handler_->FollowRedirect(base::nullopt, base::nullopt); ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); // Give the final response. @@ -1256,7 +1260,7 @@ TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, RedirectHandling) { // redirect, despite the fact that no redirect has been received yet. TEST_P(MojoAsyncResourceHandlerWithAllocationSizeTest, MalformedFollowRedirectRequest) { - handler_->FollowRedirect(base::nullopt); + handler_->FollowRedirect(base::nullopt, base::nullopt); EXPECT_TRUE(handler_->has_received_bad_message()); } @@ -1459,6 +1463,66 @@ TEST_F(MojoAsyncResourceHandlerSendSSLInfoForCertificateError, EXPECT_FALSE(url_loader_client_.completion_status().ssl_info); }; +TEST_F(MojoAsyncResourceHandlerTest, + TransferSizeUpdateCalledForNonBlockedResponse) { + net::URLRequestJobFactoryImpl test_job_factory_; + auto test_job = std::make_unique<net::URLRequestTestJob>( + request_.get(), request_context_->network_delegate(), "response headers", + "response body", true); + auto test_job_interceptor = std::make_unique<net::TestJobInterceptor>(); + net::TestJobInterceptor* raw_test_job_interceptor = + test_job_interceptor.get(); + EXPECT_TRUE(test_job_factory_.SetProtocolHandler( + url::kHttpScheme, std::move(test_job_interceptor))); + + request_context_->set_job_factory(&test_job_factory_); + raw_test_job_interceptor->set_main_intercept_job(std::move(test_job)); + request_->Start(); + + ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); + url_request_delegate_.RunUntilComplete(); + + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); + ASSERT_EQ(MockResourceLoader::Status::IDLE, + mock_loader_->OnResponseCompleted(status)); + url_loader_client_.RunUntilComplete(); + EXPECT_LT(0, url_loader_client_.body_transfer_size()); + EXPECT_EQ(request_->GetTotalReceivedBytes(), + url_loader_client_.body_transfer_size()); +} + +TEST_F(MojoAsyncResourceHandlerTest, + TransferSizeUpdateNotCalledForBlockedResponse) { + net::URLRequestJobFactoryImpl test_job_factory_; + auto test_job = std::make_unique<net::URLRequestTestJob>( + request_.get(), request_context_->network_delegate(), "response headers", + "response body", true); + auto test_job_interceptor = std::make_unique<net::TestJobInterceptor>(); + net::TestJobInterceptor* raw_test_job_interceptor = + test_job_interceptor.get(); + EXPECT_TRUE(test_job_factory_.SetProtocolHandler( + url::kHttpScheme, std::move(test_job_interceptor))); + + request_context_->set_job_factory(&test_job_factory_); + raw_test_job_interceptor->set_main_intercept_job(std::move(test_job)); + request_->Start(); + + // Block the response to reach renderer. + ResourceRequestInfoImpl::ForRequest(request_.get()) + ->set_blocked_response_from_reaching_renderer(true); + + ASSERT_TRUE(CallOnWillStartAndOnResponseStarted()); + + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, net::OK); + ASSERT_EQ(MockResourceLoader::Status::IDLE, + mock_loader_->OnResponseCompleted(status)); + url_request_delegate_.RunUntilComplete(); + EXPECT_TRUE(ResourceRequestInfoImpl::ForRequest(request_.get()) + ->blocked_response_from_reaching_renderer()); + EXPECT_EQ(0, url_loader_client_.body_transfer_size()); + EXPECT_LT(0, request_->GetTotalReceivedBytes()); +} + INSTANTIATE_TEST_CASE_P(MojoAsyncResourceHandlerWithAllocationSizeTest, MojoAsyncResourceHandlerWithAllocationSizeTest, ::testing::Values(8, 32 * 2014)); diff --git a/chromium/content/browser/loader/navigation_loader_util.h b/chromium/content/browser/loader/navigation_loader_util.h deleted file mode 100644 index d5741554aff..00000000000 --- a/chromium/content/browser/loader/navigation_loader_util.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_NAVIGATION_LOADER_UTIL_H_ -#define CONTENT_BROWSER_LOADER_NAVIGATION_LOADER_UTIL_H_ - -#include "base/optional.h" - -#include <string> - -class GURL; -namespace net { -class HttpResponseHeaders; -} - -namespace content { -namespace navigation_loader_util { - -// Returns true if the given response must be downloaded because of the headers. -bool MustDownload(const GURL& url, - net::HttpResponseHeaders* headers, - const std::string& mime_type); - -// Determines whether given response would result in a download. -// Note this doesn't handle the case when a plugin exists for the |mime_type|. -bool IsDownload(const GURL& url, - net::HttpResponseHeaders* headers, - const std::string& mime_type); - -} // namespace navigation_loader_util -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_NAVIGATION_LOADER_UTIL_H_ diff --git a/chromium/content/browser/loader/navigation_url_loader.h b/chromium/content/browser/loader/navigation_url_loader.h index e0151260cfc..e2b961d6715 100644 --- a/chromium/content/browser/loader/navigation_url_loader.h +++ b/chromium/content/browser/loader/navigation_url_loader.h @@ -8,8 +8,13 @@ #include <memory> #include "base/macros.h" +#include "base/optional.h" #include "content/common/content_export.h" +namespace net { +class HttpRequestHeaders; +} + namespace content { class AppCacheNavigationHandle; @@ -51,7 +56,10 @@ class CONTENT_EXPORT NavigationURLLoader { // Called in response to OnRequestRedirected to continue processing the // request. - virtual void FollowRedirect() = 0; + virtual void FollowRedirect(const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, + const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) = 0; // Called in response to OnResponseStarted to process the response. virtual void ProceedWithResponse() = 0; diff --git a/chromium/content/browser/loader/navigation_url_loader_impl.cc b/chromium/content/browser/loader/navigation_url_loader_impl.cc index 3dc15070498..0fb15c7c1be 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl.cc +++ b/chromium/content/browser/loader/navigation_url_loader_impl.cc @@ -10,6 +10,7 @@ #include "base/bind_helpers.h" #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" +#include "base/stl_util.h" #include "base/task_scheduler/post_task.h" #include "base/trace_event/trace_event.h" #include "components/download/public/common/download_stats.h" @@ -22,7 +23,6 @@ #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/loader/navigation_loader_interceptor.h" -#include "content/browser/loader/navigation_loader_util.h" #include "content/browser/loader/navigation_url_loader_delegate.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" @@ -34,17 +34,18 @@ #include "content/browser/url_loader_factory_getter.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_package/signed_exchange_consts.h" +#include "content/browser/web_package/signed_exchange_request_handler.h" #include "content/browser/web_package/signed_exchange_url_loader_factory_for_non_network_service.h" #include "content/browser/web_package/signed_exchange_utils.h" -#include "content/browser/web_package/web_package_request_handler.h" #include "content/browser/webui/url_data_manager_backend.h" #include "content/browser/webui/web_ui_url_loader_factory_internal.h" #include "content/common/navigation_subresource_loader_params.h" -#include "content/common/service_worker/service_worker_utils.h" +#include "content/common/net/record_load_histograms.h" #include "content/common/throttling_url_loader.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" +#include "content/public/browser/download_utils.h" #include "content/public/browser/global_request_id.h" #include "content/public/browser/navigation_data.h" #include "content/public/browser/navigation_ui_data.h" @@ -59,6 +60,7 @@ #include "content/public/common/webplugininfo.h" #include "net/base/load_flags.h" #include "net/http/http_content_disposition.h" +#include "net/http/http_request_headers.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/redirect_util.h" #include "net/url_request/url_request.h" @@ -73,6 +75,7 @@ #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "services/service_manager/public/cpp/connector.h" #include "third_party/blink/public/common/mime_util/mime_util.h" +#include "third_party/blink/public/common/service_worker/service_worker_utils.h" namespace content { @@ -108,7 +111,7 @@ base::LazyInstance<NavigationURLLoaderImpl::BeginNavigationInterceptor>::Leaky // of them is enabled. bool IsLoaderInterceptionEnabled() { return base::FeatureList::IsEnabled(network::features::kNetworkService) || - ServiceWorkerUtils::IsServicificationEnabled() || + blink::ServiceWorkerUtils::IsServicificationEnabled() || signed_exchange_utils::IsSignedExchangeHandlingEnabled(); } @@ -243,6 +246,8 @@ std::unique_ptr<network::ResourceRequest> CreateResourceRequest( new_request->fetch_redirect_mode = network::mojom::FetchRedirectMode::kManual; new_request->fetch_request_context_type = request_info->begin_params->request_context_type; + new_request->upgrade_if_insecure = request_info->upgrade_if_insecure; + new_request->throttling_profile_id = request_info->devtools_frame_token; return new_request; } @@ -258,12 +263,11 @@ std::unique_ptr<NavigationRequestInfo> CreateNavigationRequestInfoForRedirect( previous_request_info.common_params; new_common_params.url = updated_resource_request.url; new_common_params.referrer = - Referrer(updated_resource_request.url, + Referrer(updated_resource_request.referrer, Referrer::NetReferrerPolicyToBlinkReferrerPolicy( updated_resource_request.referrer_policy)); new_common_params.method = updated_resource_request.method; new_common_params.post_data = updated_resource_request.request_body; - // TODO(shimazu): Set correct base url and history url for a data URL. mojom::BeginNavigationParamsPtr new_begin_params = previous_request_info.begin_params.Clone(); @@ -279,8 +283,10 @@ std::unique_ptr<NavigationRequestInfo> CreateNavigationRequestInfoForRedirect( previous_request_info.is_for_guests_only, previous_request_info.report_raw_headers, previous_request_info.is_prerendering, + previous_request_info.upgrade_if_insecure, nullptr /* blob_url_loader_factory */, - previous_request_info.devtools_navigation_token); + previous_request_info.devtools_navigation_token, + previous_request_info.devtools_frame_token); } // Called for requests that we don't have a URLLoaderFactory for. @@ -308,7 +314,6 @@ class NavigationURLLoaderImpl::URLLoaderRequestController initial_interceptors, std::unique_ptr<network::ResourceRequest> resource_request, ResourceContext* resource_context, - scoped_refptr<URLLoaderFactoryGetter> default_url_loader_factory_getter, const GURL& url, network::mojom::URLLoaderFactoryRequest proxied_factory_request, network::mojom::URLLoaderFactoryPtrInfo proxied_factory_info, @@ -317,7 +322,6 @@ class NavigationURLLoaderImpl::URLLoaderRequestController : interceptors_(std::move(initial_interceptors)), resource_request_(std::move(resource_request)), resource_context_(resource_context), - default_url_loader_factory_getter_(default_url_loader_factory_getter), url_(url), owner_(owner), response_loader_binding_(this), @@ -328,6 +332,18 @@ class NavigationURLLoaderImpl::URLLoaderRequestController ~URLLoaderRequestController() override { DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // If neither OnCompleted nor OnReceivedResponse has been invoked, the + // request was canceled before receiving a response, so log a cancellation. + // Results after receiving a non-error response are logged in the renderer, + // if the request is passed to one. If it's a download, or not passed to a + // renderer for some other reason, results will not be logged for the + // request. The net::OK check may not be necessary - the case where OK is + // received without receiving any headers looks broken, anyways. + if (!received_response_ && (!status_ || status_->error_code != net::OK)) { + RecordLoadHistograms(url_, resource_request_->resource_type, + status_ ? status_->error_code : net::ERR_ABORTED); + } } static uint32_t GetURLLoaderOptions(bool is_main_frame) { @@ -352,15 +368,30 @@ class NavigationURLLoaderImpl::URLLoaderRequestController net::URLRequestContextGetter* url_request_context_getter, storage::FileSystemContext* upload_file_system_context, ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, - AppCacheNavigationHandleCore* appcache_handle_core) const { + AppCacheNavigationHandleCore* appcache_handle_core, + bool was_request_intercepted) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); + DCHECK(started_); + return base::BindOnce( &URLLoaderRequestController::CreateNonNetworkServiceURLLoader, weak_factory_.GetWeakPtr(), base::Unretained(url_request_context_getter), base::Unretained(upload_file_system_context), std::make_unique<NavigationRequestInfo>(*request_info_), - base::Unretained(service_worker_navigation_handle_core), - base::Unretained(appcache_handle_core)); + // If the request has already been intercepted, the request should not + // be intercepted again. + // S13nServiceWorker: Requests are intercepted by S13nServiceWorker + // before the default request handler when needed, so we never need to + // pass |service_worker_navigation_handle_core| here. + base::Unretained( + blink::ServiceWorkerUtils::IsServicificationEnabled() || + was_request_intercepted + ? nullptr + : service_worker_navigation_handle_core), + base::Unretained(was_request_intercepted ? nullptr + : appcache_handle_core)); } void CreateNonNetworkServiceURLLoader( @@ -371,23 +402,25 @@ class NavigationURLLoaderImpl::URLLoaderRequestController AppCacheNavigationHandleCore* appcache_handle_core, network::mojom::URLLoaderRequest url_loader, network::mojom::URLLoaderClientPtr url_loader_client) { - DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); + DCHECK(started_); default_loader_used_ = true; if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) { - DCHECK(!default_url_loader_factory_getter_); + DCHECK(!network_loader_factory_); // It is safe to pass the callback of CreateURLLoaderThrottles with the // unretained |this|, because the passed callback will be used by a // SignedExchangeHandler which is indirectly owned by |this| until its // header is verified and parsed, that's where the getter is used. - interceptors_.push_back(std::make_unique<WebPackageRequestHandler>( + interceptors_.push_back(std::make_unique<SignedExchangeRequestHandler>( url::Origin::Create(request_info->common_params.url), request_info->common_params.url, GetURLLoaderOptions(request_info->is_main_frame), request_info->frame_tree_node_id, request_info->devtools_navigation_token, - request_info->report_raw_headers, + request_info->devtools_frame_token, request_info->report_raw_headers, + request_info->begin_params->load_flags, base::MakeRefCounted< SignedExchangeURLLoaderFactoryForNonNetworkService>( resource_context_, url_request_context_getter), @@ -415,7 +448,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController upload_file_system_context, *request_info, std::move(navigation_ui_data_), std::move(url_loader_client), std::move(url_loader), service_worker_navigation_handle_core, - appcache_handle_core, options, &global_request_id_); + appcache_handle_core, options, global_request_id_); } // TODO(arthursonzogni): Detect when the ResourceDispatcherHost didn't @@ -434,8 +467,8 @@ class NavigationURLLoaderImpl::URLLoaderRequestController AppCacheNavigationHandleCore* appcache_handle_core, std::unique_ptr<NavigationRequestInfo> request_info, std::unique_ptr<NavigationUIData> navigation_ui_data) { - DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK(!started_); started_ = true; request_info_ = std::move(request_info); @@ -443,6 +476,11 @@ class NavigationURLLoaderImpl::URLLoaderRequestController web_contents_getter_ = base::BindRepeating( &GetWebContentsFromFrameTreeNodeID, frame_tree_node_id_); navigation_ui_data_ = std::move(navigation_ui_data); + // The ResourceDispatcherHostImpl can be null in unit tests. + ResourceDispatcherHostImpl* rph = ResourceDispatcherHostImpl::Get(); + if (rph) + global_request_id_ = rph->MakeGlobalRequestID(); + default_request_handler_factory_ = base::BindRepeating( &URLLoaderRequestController:: CreateDefaultRequestHandlerForNonNetworkService, @@ -453,16 +491,31 @@ class NavigationURLLoaderImpl::URLLoaderRequestController base::Unretained(service_worker_navigation_handle_core), base::Unretained(appcache_handle_core)); + // Requests to Blob scheme won't get redirected to/from other schemes + // or be intercepted, so we just let it go here. + if (request_info_->common_params.url.SchemeIsBlob() && + request_info_->blob_url_loader_factory) { + url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( + network::SharedURLLoaderFactory::Create( + std::move(request_info_->blob_url_loader_factory)), + CreateURLLoaderThrottles(), -1 /* routing_id */, 0 /* request_id? */, + network::mojom::kURLLoadOptionNone, resource_request_.get(), this, + kNavigationUrlLoaderTrafficAnnotation, + base::ThreadTaskRunnerHandle::Get()); + return; + } + // If S13nServiceWorker is disabled, just use // |default_request_handler_factory_| and return. The non network service // request handling goes through ResourceDispatcherHost which has legacy // hooks for service worker (ServiceWorkerRequestInterceptor), so no service // worker interception is needed here. - if (!ServiceWorkerUtils::IsServicificationEnabled() || + if (!blink::ServiceWorkerUtils::IsServicificationEnabled() || !service_worker_navigation_handle_core) { url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( base::MakeRefCounted<SingleRequestURLLoaderFactory>( - default_request_handler_factory_.Run()), + default_request_handler_factory_.Run( + false /* was_request_intercepted */)), CreateURLLoaderThrottles(), -1 /* routing_id */, 0 /* request_id */, network::mojom::kURLLoadOptionNone, resource_request_.get(), this /* client */, kNavigationUrlLoaderTrafficAnnotation, @@ -481,7 +534,8 @@ class NavigationURLLoaderImpl::URLLoaderRequestController if (!service_worker_interceptor) { url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( base::MakeRefCounted<SingleRequestURLLoaderFactory>( - default_request_handler_factory_.Run()), + default_request_handler_factory_.Run( + false /* was_request_intercepted */)), CreateURLLoaderThrottles(), -1 /* routing_id */, 0 /* request_id */, network::mojom::kURLLoadOptionNone, resource_request_.get(), this /* client */, kNavigationUrlLoaderTrafficAnnotation, @@ -491,14 +545,13 @@ class NavigationURLLoaderImpl::URLLoaderRequestController interceptors_.push_back(std::move(service_worker_interceptor)); - // TODO(shimazu): Make sure we have a consistent global id for the - // navigation request. - global_request_id_ = MakeGlobalRequestID(); Restart(); } void Start( net::URLRequestContextGetter* url_request_context_getter, + std::unique_ptr<network::SharedURLLoaderFactoryInfo> + network_loader_factory_info, ServiceWorkerNavigationHandleCore* service_worker_navigation_handle_core, AppCacheNavigationHandleCore* appcache_handle_core, std::unique_ptr<NavigationRequestInfo> request_info, @@ -506,8 +559,8 @@ class NavigationURLLoaderImpl::URLLoaderRequestController network::mojom::URLLoaderFactoryPtrInfo factory_for_webui, int frame_tree_node_id, std::unique_ptr<service_manager::Connector> connector) { - DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); DCHECK(!started_); global_request_id_ = MakeGlobalRequestID(); frame_tree_node_id_ = frame_tree_node_id; @@ -516,6 +569,10 @@ class NavigationURLLoaderImpl::URLLoaderRequestController base::Bind(&GetWebContentsFromFrameTreeNodeID, frame_tree_node_id); navigation_ui_data_ = std::move(navigation_ui_data); + DCHECK(network_loader_factory_info); + network_loader_factory_ = network::SharedURLLoaderFactory::Create( + std::move(network_loader_factory_info)); + if (resource_request_->request_body) { GetBodyBlobDataHandles(resource_request_->request_body.get(), resource_context_, &blob_handles_); @@ -527,9 +584,9 @@ class NavigationURLLoaderImpl::URLLoaderRequestController url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( std::move(factory_for_webui)), - CreateURLLoaderThrottles(), 0 /* routing_id */, 0 /* request_id? */, - network::mojom::kURLLoadOptionNone, resource_request_.get(), this, - kNavigationUrlLoaderTrafficAnnotation, + CreateURLLoaderThrottles(), 0 /* routing_id */, + global_request_id_.request_id, network::mojom::kURLLoadOptionNone, + resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, base::ThreadTaskRunnerHandle::Get()); return; } @@ -541,9 +598,9 @@ class NavigationURLLoaderImpl::URLLoaderRequestController url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( network::SharedURLLoaderFactory::Create( std::move(request_info->blob_url_loader_factory)), - CreateURLLoaderThrottles(), 0 /* routing_id */, 0 /* request_id? */, - network::mojom::kURLLoadOptionNone, resource_request_.get(), this, - kNavigationUrlLoaderTrafficAnnotation, + CreateURLLoaderThrottles(), 0 /* routing_id */, + global_request_id_.request_id, network::mojom::kURLLoadOptionNone, + resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, base::ThreadTaskRunnerHandle::Get()); return; } @@ -560,24 +617,29 @@ class NavigationURLLoaderImpl::URLLoaderRequestController std::unique_ptr<NavigationLoaderInterceptor> appcache_interceptor = AppCacheRequestHandler::InitializeForNavigationNetworkService( *resource_request_, appcache_handle_core, - default_url_loader_factory_getter_.get()); + network_loader_factory_); if (appcache_interceptor) interceptors_.push_back(std::move(appcache_interceptor)); } if (signed_exchange_utils::IsSignedExchangeHandlingEnabled()) { + // Signed Exchange is currently disabled when Network Service is enabled + // (https://crbug.com/849935), but still create + // SignedExchangeRequestHandler in order to show error message (and + // devtools warning) to users. + // It is safe to pass the callback of CreateURLLoaderThrottles with the // unretained |this|, because the passed callback will be used by a // SignedExchangeHandler which is indirectly owned by |this| until its // header is verified and parsed, that's where the getter is used. - interceptors_.push_back(std::make_unique<WebPackageRequestHandler>( + interceptors_.push_back(std::make_unique<SignedExchangeRequestHandler>( url::Origin::Create(request_info->common_params.url), request_info->common_params.url, GetURLLoaderOptions(request_info->is_main_frame), request_info->frame_tree_node_id, request_info->devtools_navigation_token, - request_info->report_raw_headers, - default_url_loader_factory_getter_->GetNetworkFactory(), + request_info->devtools_frame_token, request_info->report_raw_headers, + request_info->begin_params->load_flags, network_loader_factory_, base::BindRepeating( &URLLoaderRequestController::CreateURLLoaderThrottles, base::Unretained(this)), @@ -624,7 +686,10 @@ class NavigationURLLoaderImpl::URLLoaderRequestController void MaybeStartLoader( NavigationLoaderInterceptor* interceptor, SingleRequestURLLoaderFactory::RequestHandler single_request_handler) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(IsLoaderInterceptionEnabled()); + DCHECK(started_); + if (single_request_handler) { // |interceptor| wants to handle the request with // |single_request_handler|. @@ -633,9 +698,9 @@ class NavigationURLLoaderImpl::URLLoaderRequestController url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( base::MakeRefCounted<SingleRequestURLLoaderFactory>( std::move(single_request_handler)), - CreateURLLoaderThrottles(), frame_tree_node_id_, 0 /* request_id? */, - network::mojom::kURLLoadOptionNone, resource_request_.get(), this, - kNavigationUrlLoaderTrafficAnnotation, + CreateURLLoaderThrottles(), frame_tree_node_id_, + global_request_id_.request_id, network::mojom::kURLLoadOptionNone, + resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, base::ThreadTaskRunnerHandle::Get()); subresource_loader_params_ = @@ -677,7 +742,8 @@ class NavigationURLLoaderImpl::URLLoaderRequestController // so let it just follow the redirect. if (url_loader_) { DCHECK(!redirect_info_.new_url.is_empty()); - url_loader_->FollowRedirect(); + url_loader_->FollowRedirect( + std::move(url_loader_modified_request_headers_)); return; } @@ -693,28 +759,36 @@ class NavigationURLLoaderImpl::URLLoaderRequestController if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { DCHECK(!interceptors_.empty()); DCHECK(default_request_handler_factory_); + // The only way to come here is to enable ServiceWorkerServicification + // without NetworkService. We know that the service worker's request + // interceptor has already intercepted and decided not to handle the + // request. + DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled()); default_loader_used_ = true; // Update |request_info_| when following a redirect. if (url_chain_.size() > 0) { request_info_ = CreateNavigationRequestInfoForRedirect( *request_info_, *resource_request_); } + // When |subresource_loader_params_| has its value, the request should not + // be intercepted by any other interceptors since it means that a request + // interceptor already intercepted the request and it attached its info to + // the request. url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( base::MakeRefCounted<SingleRequestURLLoaderFactory>( - default_request_handler_factory_.Run()), - CreateURLLoaderThrottles(), frame_tree_node_id_, 0 /* request_id */, - network::mojom::kURLLoadOptionNone, resource_request_.get(), - this /* client */, kNavigationUrlLoaderTrafficAnnotation, + default_request_handler_factory_.Run( + subresource_loader_params_.has_value() + /* was_request_intercepted */)), + CreateURLLoaderThrottles(), frame_tree_node_id_, + global_request_id_.request_id, network::mojom::kURLLoadOptionNone, + resource_request_.get(), this /* client */, + kNavigationUrlLoaderTrafficAnnotation, base::ThreadTaskRunnerHandle::Get()); return; } - if (resource_request_->url.SchemeIs(url::kBlobScheme)) { - factory = - base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( - default_url_loader_factory_getter_->GetBlobFactory()); - } else if (!IsURLHandledByNetworkService(resource_request_->url) && - !resource_request_->url.SchemeIs(url::kDataScheme)) { + if (!IsURLHandledByNetworkService(resource_request_->url) && + !resource_request_->url.SchemeIs(url::kDataScheme)) { if (known_schemes_.find(resource_request_->url.scheme()) == known_schemes_.end()) { bool handled = GetContentClient()->browser()->HandleExternalProtocol( @@ -754,12 +828,11 @@ class NavigationURLLoaderImpl::URLLoaderRequestController !resource_request_->url.SchemeIs(url::kDataScheme)) { DCHECK(proxied_factory_info_.is_valid()); // We don't worry about reconnection since it's a single navigation. - default_url_loader_factory_getter_->CloneNetworkFactory( - std::move(proxied_factory_request_)); + network_loader_factory_->Clone(std::move(proxied_factory_request_)); factory = base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>( std::move(proxied_factory_info_)); } else { - factory = default_url_loader_factory_getter_->GetNetworkFactory(); + factory = network_loader_factory_; } } url_chain_.push_back(resource_request_->url); @@ -767,17 +840,18 @@ class NavigationURLLoaderImpl::URLLoaderRequestController RESOURCE_TYPE_MAIN_FRAME); url_loader_ = ThrottlingURLLoader::CreateLoaderAndStart( factory, CreateURLLoaderThrottles(), frame_tree_node_id_, - 0 /* request_id? */, options, resource_request_.get(), this, + global_request_id_.request_id, options, resource_request_.get(), this, kNavigationUrlLoaderTrafficAnnotation, base::ThreadTaskRunnerHandle::Get()); } - void FollowRedirect() { + void FollowRedirect( + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!redirect_info_.new_url.is_empty()); if (!IsLoaderInterceptionEnabled()) { - url_loader_->FollowRedirect(); + url_loader_->FollowRedirect(modified_request_headers); return; } @@ -794,7 +868,8 @@ class NavigationURLLoaderImpl::URLLoaderRequestController bool should_clear_upload = false; net::RedirectUtil::UpdateHttpRequest( resource_request_->url, resource_request_->method, redirect_info_, - &resource_request_->headers, &should_clear_upload); + modified_request_headers, &resource_request_->headers, + &should_clear_upload); if (should_clear_upload) { // The request body is no longer applicable. resource_request_->request_body = nullptr; @@ -808,6 +883,10 @@ class NavigationURLLoaderImpl::URLLoaderRequestController resource_request_->referrer_policy = redirect_info_.new_referrer_policy; url_chain_.push_back(redirect_info_.new_url); + // Need to cache modified headers for |url_loader_| since it doesn't use + // |resource_request_| during redirect. + url_loader_modified_request_headers_ = modified_request_headers; + Restart(); } @@ -817,9 +896,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController private: // network::mojom::URLLoaderClient implementation: - void OnReceiveResponse( - const network::ResourceResponseHead& head, - network::mojom::DownloadedTempFilePtr downloaded_file) override { + void OnReceiveResponse(const network::ResourceResponseHead& head) override { received_response_ = true; // If the default loader (network) was used to handle the URL load request @@ -847,15 +924,14 @@ class NavigationURLLoaderImpl::URLLoaderRequestController bool is_stream; std::unique_ptr<NavigationData> cloned_navigation_data; if (IsLoaderInterceptionEnabled()) { - bool must_download = navigation_loader_util::MustDownload( + bool must_download = download_utils::MustDownload( url_, head.headers.get(), head.mime_type); bool known_mime_type = blink::IsSupportedMimeType(head.mime_type); #if BUILDFLAG(ENABLE_PLUGINS) if (!response_intercepted && !must_download && !known_mime_type) { CheckPluginAndContinueOnReceiveResponse( - head, std::move(downloaded_file), - std::move(url_loader_client_endpoints), + head, std::move(url_loader_client_endpoints), std::vector<WebPluginInfo>()); return; } @@ -900,14 +976,16 @@ class NavigationURLLoaderImpl::URLLoaderRequestController subresource_loader_params_ = SubresourceLoaderParams(); subresource_loader_params_->controller_service_worker_info = mojom::ControllerServiceWorkerInfo::New(); - base::WeakPtr<ServiceWorkerHandle> sw_handle = - sw_provider_host->GetOrCreateServiceWorkerHandle( + subresource_loader_params_->controller_service_worker_info->mode = + sw_provider_host->GetControllerMode(); + base::WeakPtr<ServiceWorkerObjectHost> sw_object_host = + sw_provider_host->GetOrCreateServiceWorkerObjectHost( sw_provider_host->controller()); - if (sw_handle) { - subresource_loader_params_->controller_service_worker_handle = - sw_handle; + if (sw_object_host) { + subresource_loader_params_->controller_service_worker_object_host = + sw_object_host; subresource_loader_params_->controller_service_worker_info - ->object_info = sw_handle->CreateIncompleteObjectInfo(); + ->object_info = sw_object_host->CreateIncompleteObjectInfo(); } } } else { @@ -915,8 +993,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController } } - CallOnReceivedResponse(head, std::move(downloaded_file), - std::move(url_loader_client_endpoints), + CallOnReceivedResponse(head, std::move(url_loader_client_endpoints), std::move(cloned_navigation_data), is_download, is_stream); } @@ -924,7 +1001,6 @@ class NavigationURLLoaderImpl::URLLoaderRequestController #if BUILDFLAG(ENABLE_PLUGINS) void CheckPluginAndContinueOnReceiveResponse( const network::ResourceResponseHead& head, - network::mojom::DownloadedTempFilePtr downloaded_file, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, const std::vector<WebPluginInfo>& plugins) { bool stale; @@ -943,7 +1019,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController // Refresh the plugins asynchronously. PluginService::GetInstance()->GetPlugins(base::BindOnce( &URLLoaderRequestController::CheckPluginAndContinueOnReceiveResponse, - weak_factory_.GetWeakPtr(), head, std::move(downloaded_file), + weak_factory_.GetWeakPtr(), head, std::move(url_loader_client_endpoints))); return; } @@ -952,15 +1028,13 @@ class NavigationURLLoaderImpl::URLLoaderRequestController !has_plugin && (!head.headers || head.headers->response_code() / 100 == 2); - CallOnReceivedResponse(head, std::move(downloaded_file), - std::move(url_loader_client_endpoints), nullptr, - is_download, false /* is_stream */); + CallOnReceivedResponse(head, std::move(url_loader_client_endpoints), + nullptr, is_download, false /* is_stream */); } #endif void CallOnReceivedResponse( const network::ResourceResponseHead& head, - network::mojom::DownloadedTempFilePtr downloaded_file, network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, std::unique_ptr<NavigationData> cloned_navigation_data, bool is_download, @@ -981,7 +1055,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController response->DeepCopy(), std::move(url_loader_client_endpoints), std::move(cloned_navigation_data), global_request_id_, - is_download, is_stream, std::move(downloaded_file))); + is_download, is_stream)); } void OnReceiveRedirect(const net::RedirectInfo& redirect_info, @@ -1013,7 +1087,6 @@ class NavigationURLLoaderImpl::URLLoaderRequestController redirect_info, response->DeepCopy())); } - void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override {} void OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback) override {} @@ -1117,10 +1190,14 @@ class NavigationURLLoaderImpl::URLLoaderRequestController ResourceContext* resource_context_; base::Callback<WebContents*()> web_contents_getter_; std::unique_ptr<NavigationUIData> navigation_ui_data_; - scoped_refptr<URLLoaderFactoryGetter> default_url_loader_factory_getter_; + scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; std::unique_ptr<ThrottlingURLLoader> url_loader_; + // Caches the modified request headers provided by clients during redirect, + // will be consumed by next |url_loader_->FollowRedirect()|. + base::Optional<net::HttpRequestHeaders> url_loader_modified_request_headers_; + BlobHandles blob_handles_; std::vector<GURL> url_chain_; @@ -1162,7 +1239,11 @@ class NavigationURLLoaderImpl::URLLoaderRequestController // captures all of parameters to create a // SingleRequestURLLoaderFactory::RequestHandler. Used only when // NetworkService is disabled but IsLoaderInterceptionEnabled() is true. - base::RepeatingCallback<SingleRequestURLLoaderFactory::RequestHandler()> + // Set |was_request_intercepted| to true if the request was intercepted by an + // interceptor and the request is falling back to the network. In that case, + // any interceptors won't intercept the request. + base::RepeatingCallback<SingleRequestURLLoaderFactory::RequestHandler( + bool /* was_request_intercepted */)> default_request_handler_factory_; // The completion status if it has been received. This is needed to handle @@ -1223,6 +1304,7 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( std::unique_ptr<network::ResourceRequest> new_request = CreateResourceRequest( request_info.get(), frame_tree_node_id, allow_download_); + new_request->transition_type = request_info->common_params.transition; if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { DCHECK(!request_controller_); @@ -1230,7 +1312,6 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( /* initial_interceptors = */ std::vector<std::unique_ptr<NavigationLoaderInterceptor>>(), std::move(new_request), resource_context, - /* default_url_factory_getter = */ nullptr, request_info->common_params.url, /* proxied_url_loader_factory_request */ nullptr, /* proxied_url_loader_factory_info */ nullptr, std::set<std::string>(), @@ -1255,7 +1336,7 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( network::mojom::URLLoaderFactoryPtrInfo factory_for_webui; const auto& schemes = URLDataManagerBackend::GetWebUISchemes(); std::string scheme = new_request->url.scheme(); - if (std::find(schemes.begin(), schemes.end(), scheme) != schemes.end()) { + if (base::ContainsValue(schemes, scheme)) { factory_for_webui = CreateWebUIURLLoaderBinding( frame_tree_node->current_frame_host(), scheme) .PassInterface(); @@ -1278,8 +1359,8 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( network::mojom::URLLoaderFactoryPtrInfo factory_info; auto factory_request = mojo::MakeRequest(&factory_info); bool use_proxy = GetContentClient()->browser()->WillCreateURLLoaderFactory( - frame_tree_node->current_frame_host(), true /* is_navigation */, - &factory_request); + partition->browser_context(), frame_tree_node->current_frame_host(), + true /* is_navigation */, &factory_request); if (RenderFrameDevToolsAgentHost::WillCreateURLLoaderFactory( frame_tree_node->current_frame_host(), true, false, &factory_request)) { @@ -1311,15 +1392,16 @@ NavigationURLLoaderImpl::NavigationURLLoaderImpl( DCHECK(!request_controller_); request_controller_ = std::make_unique<URLLoaderRequestController>( std::move(initial_interceptors), std::move(new_request), resource_context, - partition->url_loader_factory_getter(), request_info->common_params.url, - std::move(proxied_factory_request), std::move(proxied_factory_info), - std::move(known_schemes), weak_factory_.GetWeakPtr()); + request_info->common_params.url, std::move(proxied_factory_request), + std::move(proxied_factory_info), std::move(known_schemes), + weak_factory_.GetWeakPtr()); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce( &URLLoaderRequestController::Start, base::Unretained(request_controller_.get()), base::RetainedRef(storage_partition->GetURLRequestContext()), + partition->url_loader_factory_getter()->GetNetworkFactoryInfo(), service_worker_navigation_handle_core, appcache_handle_core, std::move(request_info), std::move(navigation_ui_data), std::move(factory_for_webui), frame_tree_node_id, @@ -1331,11 +1413,15 @@ NavigationURLLoaderImpl::~NavigationURLLoaderImpl() { request_controller_.release()); } -void NavigationURLLoaderImpl::FollowRedirect() { +void NavigationURLLoaderImpl::FollowRedirect( + const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::BindOnce(&URLLoaderRequestController::FollowRedirect, - base::Unretained(request_controller_.get()))); + base::Unretained(request_controller_.get()), + modified_request_headers)); } void NavigationURLLoaderImpl::ProceedWithResponse() {} @@ -1346,8 +1432,7 @@ void NavigationURLLoaderImpl::OnReceiveResponse( std::unique_ptr<NavigationData> navigation_data, const GlobalRequestID& global_request_id, bool is_download, - bool is_stream, - network::mojom::DownloadedTempFilePtr downloaded_file) { + bool is_stream) { TRACE_EVENT_ASYNC_END2("navigation", "Navigation timeToResponseStarted", this, "&NavigationURLLoaderImpl", this, "success", true); @@ -1403,9 +1488,10 @@ void NavigationURLLoaderImpl::BindNonNetworkURLLoaderFactoryRequest( } FrameTreeNode* frame_tree_node = FrameTreeNode::GloballyFindByID(frame_tree_node_id); + auto* frame = frame_tree_node->current_frame_host(); GetContentClient()->browser()->WillCreateURLLoaderFactory( - frame_tree_node->current_frame_host(), true /* is_navigation */, - &factory); + frame->GetSiteInstance()->GetBrowserContext(), frame, + true /* is_navigation */, &factory); it->second->Clone(std::move(factory)); } diff --git a/chromium/content/browser/loader/navigation_url_loader_impl.h b/chromium/content/browser/loader/navigation_url_loader_impl.h index 67c1d5bb8f7..ac5e384a04d 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl.h +++ b/chromium/content/browser/loader/navigation_url_loader_impl.h @@ -44,7 +44,10 @@ class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { ~NavigationURLLoaderImpl() override; // NavigationURLLoader implementation: - void FollowRedirect() override; + void FollowRedirect(const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, + const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override; void ProceedWithResponse() override; void OnReceiveResponse( @@ -53,8 +56,7 @@ class CONTENT_EXPORT NavigationURLLoaderImpl : public NavigationURLLoader { std::unique_ptr<NavigationData> navigation_data, const GlobalRequestID& global_request_id, bool is_download, - bool is_stream, - network::mojom::DownloadedTempFilePtr downloaded_file); + bool is_stream); void OnReceiveRedirect(const net::RedirectInfo& redirect_info, scoped_refptr<network::ResourceResponse> response); void OnComplete(const network::URLLoaderCompletionStatus& status); diff --git a/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc b/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc index 4cafe900bb2..538274d42a9 100644 --- a/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc +++ b/chromium/content/browser/loader/navigation_url_loader_impl_unittest.cc @@ -104,7 +104,7 @@ class TestNavigationLoaderInterceptor : public NavigationLoaderInterceptor { } private: - void DeleteURLLoader(network::URLLoader* url_loader) { + void DeleteURLLoader(network::mojom::URLLoader* url_loader) { DCHECK_EQ(url_loader_.get(), url_loader); url_loader_.reset(); } @@ -154,7 +154,8 @@ class NavigationURLLoaderImplTest : public testing::Test { const std::string& method, NavigationURLLoaderDelegate* delegate, bool allow_download = false, - bool is_main_frame = true) { + bool is_main_frame = true, + bool upgrade_if_insecure = false) { mojom::BeginNavigationParamsPtr begin_params = mojom::BeginNavigationParams::New( headers, net::LOAD_NORMAL, false /* skip_service_worker */, @@ -176,8 +177,10 @@ class NavigationURLLoaderImplTest : public testing::Test { false /* parent_is_main_frame */, false /* are_ancestors_secure */, -1 /* frame_tree_node_id */, false /* is_for_guests_only */, false /* report_raw_headers */, false /* is_prerenering */, + upgrade_if_insecure /* upgrade_if_insecure */, nullptr /* blob_url_loader_factory */, - base::UnguessableToken::Create() /* devtools_navigation_token */)); + base::UnguessableToken::Create() /* devtools_navigation_token */, + base::UnguessableToken::Create() /* devtools_frame_token */)); std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; most_recent_resource_request_ = base::nullopt; interceptors.push_back(std::make_unique<TestNavigationLoaderInterceptor>( @@ -210,7 +213,7 @@ class NavigationURLLoaderImplTest : public testing::Test { redirect_url.GetOrigin().spec().c_str()), request_method, &delegate); delegate.WaitForRequestRedirected(); - loader->FollowRedirect(); + loader->FollowRedirect(base::nullopt, base::nullopt); EXPECT_EQ(expected_redirect_method, delegate.redirect_info().new_method); @@ -251,12 +254,32 @@ class NavigationURLLoaderImplTest : public testing::Test { url.GetOrigin().spec().c_str()), "GET", &delegate, false /* allow_download */, is_main_frame); delegate.WaitForRequestRedirected(); - loader->FollowRedirect(); + loader->FollowRedirect(base::nullopt, base::nullopt); delegate.WaitForResponseStarted(); return most_recent_resource_request_.value().priority; } + net::RedirectInfo NavigateAndReturnRedirectInfo(const GURL& url, + bool upgrade_if_insecure, + bool expect_request_fail) { + TestNavigationURLLoaderDelegate delegate; + std::unique_ptr<NavigationURLLoader> loader = CreateTestLoader( + url, + base::StringPrintf("%s: %s", net::HttpRequestHeaders::kOrigin, + url.GetOrigin().spec().c_str()), + "GET", &delegate, false /* allow_download */, true /*is_main_frame*/, + upgrade_if_insecure); + delegate.WaitForRequestRedirected(); + loader->FollowRedirect(base::nullopt, base::nullopt); + if (expect_request_fail) { + delegate.WaitForRequestFailed(); + } else { + delegate.WaitForResponseStarted(); + } + return delegate.redirect_info(); + } + protected: base::test::ScopedFeatureList feature_list_; TestBrowserThreadBundle thread_bundle_; @@ -345,4 +368,68 @@ TEST_F(NavigationURLLoaderImplTest, Redirect308Tests) { true); } +TEST_F(NavigationURLLoaderImplTest, RedirectModifiedHeaders) { + ASSERT_TRUE(http_test_server_.Start()); + + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + + TestNavigationURLLoaderDelegate delegate; + std::unique_ptr<NavigationURLLoader> loader = CreateTestLoader( + redirect_url, "Header1: Value1\r\nHeader2: Value2", "GET", &delegate); + delegate.WaitForRequestRedirected(); + + ASSERT_TRUE(most_recent_resource_request_); + + // Initial request should only have initial headers. + std::string header1, header2; + EXPECT_TRUE( + most_recent_resource_request_->headers.GetHeader("Header1", &header1)); + EXPECT_EQ("Value1", header1); + EXPECT_TRUE( + most_recent_resource_request_->headers.GetHeader("Header2", &header2)); + EXPECT_EQ("Value2", header2); + EXPECT_FALSE(most_recent_resource_request_->headers.HasHeader("Header3")); + + // Overwrite Header2 and add Header3. + net::HttpRequestHeaders redirect_headers; + redirect_headers.SetHeader("Header2", ""); + redirect_headers.SetHeader("Header3", "Value3"); + loader->FollowRedirect(base::nullopt, redirect_headers); + delegate.WaitForResponseStarted(); + + // Redirected request should also have modified headers. + EXPECT_TRUE( + most_recent_resource_request_->headers.GetHeader("Header1", &header1)); + EXPECT_EQ("Value1", header1); + EXPECT_TRUE( + most_recent_resource_request_->headers.GetHeader("Header2", &header2)); + EXPECT_EQ("", header2); + std::string header3; + EXPECT_TRUE( + most_recent_resource_request_->headers.GetHeader("Header3", &header3)); + EXPECT_EQ("Value3", header3); +} + +// Tests that the Upgrade If Insecure flag is obeyed. +TEST_F(NavigationURLLoaderImplTest, UpgradeIfInsecureTest) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL url = http_test_server_.GetURL("/redirect301-to-http"); + GURL expected_url = GURL("http://test.test/test"); + // We expect the request to fail since there is no server listening at + // test.test, but for the purpose of this test we only need to validate the + // redirect URL was not changed. + net::RedirectInfo redirect_info = NavigateAndReturnRedirectInfo( + url, false /* upgrade_if_insecure */, true /* expect_request_fail */); + EXPECT_FALSE(redirect_info.insecure_scheme_was_upgraded); + EXPECT_EQ(expected_url, redirect_info.new_url); + GURL::Replacements replacements; + replacements.SetSchemeStr("https"); + expected_url = expected_url.ReplaceComponents(replacements); + redirect_info = NavigateAndReturnRedirectInfo( + url, true /* upgrade_if_insecure */, true /* expect_request_fail */); + // Same as above, but validating the URL is upgraded to https. + EXPECT_TRUE(redirect_info.insecure_scheme_was_upgraded); + EXPECT_EQ(expected_url, redirect_info.new_url); +} + } // namespace content diff --git a/chromium/content/browser/loader/navigation_url_loader_unittest.cc b/chromium/content/browser/loader/navigation_url_loader_unittest.cc index ea806d87222..75f70ba50a4 100644 --- a/chromium/content/browser/loader/navigation_url_loader_unittest.cc +++ b/chromium/content/browser/loader/navigation_url_loader_unittest.cc @@ -95,7 +95,9 @@ class NavigationURLLoaderTest : public testing::Test { std::unique_ptr<NavigationRequestInfo> request_info( new NavigationRequestInfo(common_params, std::move(begin_params), url, true, false, false, -1, false, false, false, - nullptr, base::UnguessableToken::Create())); + false, nullptr, + base::UnguessableToken::Create(), + base::UnguessableToken::Create())); return NavigationURLLoader::Create( browser_context_->GetResourceContext(), BrowserContext::GetDefaultStoragePartition(browser_context_.get()), @@ -226,7 +228,7 @@ TEST_F(NavigationURLLoaderTest, CancelResponseRace) { // In the same event loop iteration, follow the redirect (allowing the // response to go through) and destroy the loader. - loader->FollowRedirect(); + loader->FollowRedirect(base::nullopt, base::nullopt); loader.reset(); // Verify the URLRequestTestJob no longer has anything paused and that no diff --git a/chromium/content/browser/loader/null_resource_controller.cc b/chromium/content/browser/loader/null_resource_controller.cc index da79bb5843e..8ac719d3a6a 100644 --- a/chromium/content/browser/loader/null_resource_controller.cc +++ b/chromium/content/browser/loader/null_resource_controller.cc @@ -5,6 +5,7 @@ #include "content/browser/loader/null_resource_controller.h" #include "base/logging.h" +#include "net/http/http_request_headers.h" namespace content { @@ -27,4 +28,12 @@ void NullResourceController::Resume() { *was_resumed_ = true; } +void NullResourceController::ResumeForRedirect( + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { + DCHECK(!modified_request_headers.has_value()) << "Redirect with modified " + "headers was not supported " + "yet. crbug.com/845683"; + Resume(); +} + } // namespace content diff --git a/chromium/content/browser/loader/null_resource_controller.h b/chromium/content/browser/loader/null_resource_controller.h index 8dd10f7102a..66c9e039467 100644 --- a/chromium/content/browser/loader/null_resource_controller.h +++ b/chromium/content/browser/loader/null_resource_controller.h @@ -23,6 +23,8 @@ class NullResourceController : public ResourceController { void Cancel() override; void CancelWithError(int error_code) override; void Resume() override; + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override; private: bool* was_resumed_; diff --git a/chromium/content/browser/loader/prefetch_browsertest.cc b/chromium/content/browser/loader/prefetch_browsertest.cc index da0ea6f9f64..fb0726ef37c 100644 --- a/chromium/content/browser/loader/prefetch_browsertest.cc +++ b/chromium/content/browser/loader/prefetch_browsertest.cc @@ -14,7 +14,7 @@ #include "content/browser/loader/prefetch_url_loader_service.h" #include "content/browser/storage_partition_impl.h" #include "content/browser/web_package/mock_signed_exchange_handler.h" -#include "content/browser/web_package/web_package_loader.h" +#include "content/browser/web_package/signed_exchange_loader.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_features.h" @@ -40,10 +40,10 @@ struct PrefetchBrowserTestParam { struct ScopedSignedExchangeHandlerFactory { explicit ScopedSignedExchangeHandlerFactory( SignedExchangeHandlerFactory* factory) { - WebPackageLoader::SetSignedExchangeHandlerFactoryForTest(factory); + SignedExchangeLoader::SetSignedExchangeHandlerFactoryForTest(factory); } ~ScopedSignedExchangeHandlerFactory() { - WebPackageLoader::SetSignedExchangeHandlerFactoryForTest(nullptr); + SignedExchangeLoader::SetSignedExchangeHandlerFactoryForTest(nullptr); } }; @@ -333,31 +333,31 @@ IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, WebPackageWithPreload) { int target_fetch_count = 0; int preload_fetch_count = 0; const char* prefetch_url = "/prefetch.html"; - const char* target_htxg = "/target.htxg"; + const char* target_sxg = "/target.sxg"; const char* target_url = "/target.html"; - const char* preload_url_in_htxg = "/preload.js"; + const char* preload_url_in_sxg = "/preload.js"; RegisterResponse( prefetch_url, ResponseEntry(base::StringPrintf( - "<body><link rel='prefetch' href='%s'></body>", target_htxg))); + "<body><link rel='prefetch' href='%s'></body>", target_sxg))); RegisterResponse( - target_htxg, + target_sxg, // We mock the SignedExchangeHandler, so just return a HTML content // as "application/signed-exchange;v=b0". - ResponseEntry("<head><title>Prefetch Target (HTXG)</title></head>", + ResponseEntry("<head><title>Prefetch Target (SXG)</title></head>", "application/signed-exchange;v=b0")); - RegisterResponse(preload_url_in_htxg, + RegisterResponse(preload_url_in_sxg, ResponseEntry("function foo() {}", "text/javascript")); base::RunLoop preload_waiter; base::RunLoop prefetch_waiter; embedded_test_server()->RegisterRequestMonitor(base::BindRepeating( &PrefetchBrowserTest::WatchURLAndRunClosure, base::Unretained(this), - target_htxg, &target_fetch_count, prefetch_waiter.QuitClosure())); + target_sxg, &target_fetch_count, prefetch_waiter.QuitClosure())); embedded_test_server()->RegisterRequestMonitor(base::BindRepeating( &PrefetchBrowserTest::WatchURLAndRunClosure, base::Unretained(this), - preload_url_in_htxg, &preload_fetch_count, preload_waiter.QuitClosure())); + preload_url_in_sxg, &preload_fetch_count, preload_waiter.QuitClosure())); embedded_test_server()->RegisterRequestHandler(base::BindRepeating( &PrefetchBrowserTest::ServeResponses, base::Unretained(this))); ASSERT_TRUE(embedded_test_server()->Start()); @@ -367,7 +367,7 @@ IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, WebPackageWithPreload) { net::OK, GURL(target_url), "text/html", {base::StringPrintf( "Link: <%s>;rel=\"preload\";as=\"script\"", - embedded_test_server()->GetURL(preload_url_in_htxg).spec().c_str())}); + embedded_test_server()->GetURL(preload_url_in_sxg).spec().c_str())}); ScopedSignedExchangeHandlerFactory scoped_factory(&factory); // Loading a page that prefetches the target URL would increment both @@ -377,11 +377,15 @@ IN_PROC_BROWSER_TEST_P(PrefetchBrowserTest, WebPackageWithPreload) { EXPECT_EQ(1, target_fetch_count); EXPECT_TRUE(CheckPrefetchURLLoaderCountIfSupported(1)); - // Test after this point requires SignedHTTPExchange support. - if (!base::FeatureList::IsEnabled(features::kSignedHTTPExchange)) + // Test after this point requires SignedHTTPExchange support, which is now + // disabled when Network Service is enabled. + // TODO(https://crbug.com/849935): Remove the second condition once we + // re-enable Signed Exchange with Network Service. + if (!base::FeatureList::IsEnabled(features::kSignedHTTPExchange) || + base::FeatureList::IsEnabled(network::features::kNetworkService)) return; - // If the header in the .htxg file is correctly extracted, we should + // If the header in the .sxg file is correctly extracted, we should // be able to also see the preload. preload_waiter.Run(); EXPECT_EQ(1, preload_fetch_count); diff --git a/chromium/content/browser/loader/prefetch_url_loader.cc b/chromium/content/browser/loader/prefetch_url_loader.cc index c0d59319dd0..bbfd85fcc3c 100644 --- a/chromium/content/browser/loader/prefetch_url_loader.cc +++ b/chromium/content/browser/loader/prefetch_url_loader.cc @@ -5,8 +5,8 @@ #include "content/browser/loader/prefetch_url_loader.h" #include "base/feature_list.h" +#include "content/browser/web_package/signed_exchange_prefetch_handler.h" #include "content/browser/web_package/signed_exchange_utils.h" -#include "content/browser/web_package/web_package_prefetch_handler.h" #include "content/public/common/content_features.h" #include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/features.h" @@ -29,6 +29,8 @@ PrefetchURLLoader::PrefetchURLLoader( : frame_tree_node_id_getter_(frame_tree_node_id_getter), url_(resource_request.url), report_raw_headers_(resource_request.report_raw_headers), + load_flags_(resource_request.load_flags), + throttling_profile_id_(resource_request.throttling_profile_id), network_loader_factory_(std::move(network_loader_factory)), client_binding_(this), forwarding_client_(std::move(client)), @@ -52,18 +54,20 @@ PrefetchURLLoader::PrefetchURLLoader( PrefetchURLLoader::~PrefetchURLLoader() = default; void PrefetchURLLoader::FollowRedirect( + const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { DCHECK(!modified_request_headers.has_value()) << "Redirect with modified " "headers was not supported " "yet. crbug.com/845683"; - if (web_package_prefetch_handler_) { + if (signed_exchange_prefetch_handler_) { // Rebind |client_binding_| and |loader_|. - client_binding_.Bind(web_package_prefetch_handler_->FollowRedirect( + client_binding_.Bind(signed_exchange_prefetch_handler_->FollowRedirect( mojo::MakeRequest(&loader_))); return; } - loader_->FollowRedirect(base::nullopt); + loader_->FollowRedirect(base::nullopt, base::nullopt); } void PrefetchURLLoader::ProceedWithResponse() { @@ -84,21 +88,22 @@ void PrefetchURLLoader::ResumeReadingBodyFromNet() { } void PrefetchURLLoader::OnReceiveResponse( - const network::ResourceResponseHead& response, - network::mojom::DownloadedTempFilePtr downloaded_file) { + const network::ResourceResponseHead& response) { if (signed_exchange_utils::ShouldHandleAsSignedHTTPExchange(url_, response)) { - DCHECK(!web_package_prefetch_handler_); + DCHECK(!signed_exchange_prefetch_handler_); // Note that after this point this doesn't directly get upcalls from the // network. (Until |this| calls the handler's FollowRedirect.) - web_package_prefetch_handler_ = std::make_unique<WebPackagePrefetchHandler>( - frame_tree_node_id_getter_, report_raw_headers_, response, - std::move(loader_), client_binding_.Unbind(), network_loader_factory_, - request_initiator_, url_, url_loader_throttles_getter_, - resource_context_, request_context_getter_, this); + signed_exchange_prefetch_handler_ = + std::make_unique<SignedExchangePrefetchHandler>( + frame_tree_node_id_getter_, report_raw_headers_, load_flags_, + throttling_profile_id_, response, std::move(loader_), + client_binding_.Unbind(), network_loader_factory_, + request_initiator_, url_, url_loader_throttles_getter_, + resource_context_, request_context_getter_, this); return; } - forwarding_client_->OnReceiveResponse(response, std::move(downloaded_file)); + forwarding_client_->OnReceiveResponse(response); } void PrefetchURLLoader::OnReceiveRedirect( @@ -107,11 +112,6 @@ void PrefetchURLLoader::OnReceiveRedirect( forwarding_client_->OnReceiveRedirect(redirect_info, head); } -void PrefetchURLLoader::OnDataDownloaded(int64_t data_length, - int64_t encoded_length) { - forwarding_client_->OnDataDownloaded(data_length, encoded_length); -} - void PrefetchURLLoader::OnUploadProgress(int64_t current_position, int64_t total_size, base::OnceCallback<void()> callback) { diff --git a/chromium/content/browser/loader/prefetch_url_loader.h b/chromium/content/browser/loader/prefetch_url_loader.h index 3a63bb0c4cc..ef4ff33677d 100644 --- a/chromium/content/browser/loader/prefetch_url_loader.h +++ b/chromium/content/browser/loader/prefetch_url_loader.h @@ -9,6 +9,8 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/optional.h" +#include "base/unguessable_token.h" #include "content/common/content_export.h" #include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/system/data_pipe_drainer.h" @@ -28,7 +30,7 @@ namespace content { class ResourceContext; class URLLoaderThrottle; -class WebPackagePrefetchHandler; +class SignedExchangePrefetchHandler; // PrefetchURLLoader which basically just keeps draining the data. class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, @@ -60,7 +62,9 @@ class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, private: // network::mojom::URLLoader overrides: - void FollowRedirect(const base::Optional<net::HttpRequestHeaders>& + void FollowRedirect(const base::Optional<std::vector<std::string>>& + to_be_removed_request_headers, + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) override; void ProceedWithResponse() override; void SetPriority(net::RequestPriority priority, @@ -69,12 +73,9 @@ class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, void ResumeReadingBodyFromNet() override; // network::mojom::URLLoaderClient overrides: - void OnReceiveResponse( - const network::ResourceResponseHead& head, - network::mojom::DownloadedTempFilePtr downloaded_file) override; + void OnReceiveResponse(const network::ResourceResponseHead& head) override; void OnReceiveRedirect(const net::RedirectInfo& redirect_info, const network::ResourceResponseHead& head) override; - void OnDataDownloaded(int64_t data_length, int64_t encoded_length) override; void OnUploadProgress(int64_t current_position, int64_t total_size, base::OnceCallback<void()> callback) override; @@ -94,6 +95,8 @@ class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, const base::RepeatingCallback<int(void)> frame_tree_node_id_getter_; const GURL url_; const bool report_raw_headers_; + const int load_flags_; + const base::Optional<base::UnguessableToken> throttling_profile_id_; scoped_refptr<network::SharedURLLoaderFactory> network_loader_factory_; @@ -114,7 +117,8 @@ class CONTENT_EXPORT PrefetchURLLoader : public network::mojom::URLLoader, std::unique_ptr<mojo::DataPipeDrainer> pipe_drainer_; - std::unique_ptr<WebPackagePrefetchHandler> web_package_prefetch_handler_; + std::unique_ptr<SignedExchangePrefetchHandler> + signed_exchange_prefetch_handler_; DISALLOW_COPY_AND_ASSIGN(PrefetchURLLoader); }; diff --git a/chromium/content/browser/loader/prefetch_url_loader_service.cc b/chromium/content/browser/loader/prefetch_url_loader_service.cc index a8c1638bf9e..b7baa69c5cf 100644 --- a/chromium/content/browser/loader/prefetch_url_loader_service.cc +++ b/chromium/content/browser/loader/prefetch_url_loader_service.cc @@ -20,9 +20,22 @@ namespace content { -PrefetchURLLoaderService::PrefetchURLLoaderService( - scoped_refptr<URLLoaderFactoryGetter> factory_getter) - : loader_factory_getter_(std::move(factory_getter)) {} +struct PrefetchURLLoaderService::BindContext { + BindContext(int frame_tree_node_id, + scoped_refptr<URLLoaderFactoryBundle> factory) + : frame_tree_node_id(frame_tree_node_id), factory(factory) {} + + explicit BindContext(const std::unique_ptr<BindContext>& other) + : frame_tree_node_id(other->frame_tree_node_id), + factory(other->factory) {} + + ~BindContext() = default; + + const int frame_tree_node_id; + scoped_refptr<URLLoaderFactoryBundle> factory; +}; + +PrefetchURLLoaderService::PrefetchURLLoaderService() = default; void PrefetchURLLoaderService::InitializeResourceContext( ResourceContext* resource_context, @@ -34,17 +47,16 @@ void PrefetchURLLoaderService::InitializeResourceContext( request_context_getter_ = request_context_getter; } -void PrefetchURLLoaderService::ConnectToService( +void PrefetchURLLoaderService::GetFactory( + network::mojom::URLLoaderFactoryRequest request, int frame_tree_node_id, - blink::mojom::PrefetchURLLoaderServiceRequest request) { - if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::BindOnce(&PrefetchURLLoaderService::ConnectToService, this, - frame_tree_node_id, std::move(request))); - return; - } - service_bindings_.AddBinding(this, std::move(request), frame_tree_node_id); + std::unique_ptr<URLLoaderFactoryBundleInfo> factories) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + auto factory_bundle = + base::MakeRefCounted<URLLoaderFactoryBundle>(std::move(factories)); + loader_factory_bindings_.AddBinding( + this, std::move(request), + std::make_unique<BindContext>(frame_tree_node_id, factory_bundle)); } void PrefetchURLLoaderService::CreateLoaderAndStart( @@ -82,13 +94,6 @@ void PrefetchURLLoaderService::CreateLoaderAndStart( PrefetchURLLoaderService::~PrefetchURLLoaderService() = default; -void PrefetchURLLoaderService::GetFactory( - network::mojom::URLLoaderFactoryRequest request) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - loader_factory_bindings_.AddBinding(this, std::move(request), - service_bindings_.dispatch_context()); -} - void PrefetchURLLoaderService::CreateLoaderAndStart( network::mojom::URLLoaderRequest request, int32_t routing_id, @@ -99,11 +104,11 @@ void PrefetchURLLoaderService::CreateLoaderAndStart( const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService)); - int frame_tree_node_id = loader_factory_bindings_.dispatch_context(); + const auto& dispatch_context = *loader_factory_bindings_.dispatch_context(); + int frame_tree_node_id = dispatch_context.frame_tree_node_id; CreateLoaderAndStart( std::move(request), routing_id, request_id, options, resource_request, - std::move(client), traffic_annotation, - loader_factory_getter_->GetNetworkFactory(), + std::move(client), traffic_annotation, dispatch_context.factory, base::BindRepeating([](int id) { return id; }, frame_tree_node_id)); } @@ -111,7 +116,9 @@ void PrefetchURLLoaderService::Clone( network::mojom::URLLoaderFactoryRequest request) { DCHECK_CURRENTLY_ON(BrowserThread::IO); loader_factory_bindings_.AddBinding( - this, std::move(request), loader_factory_bindings_.dispatch_context()); + this, std::move(request), + std::make_unique<BindContext>( + loader_factory_bindings_.dispatch_context())); } std::vector<std::unique_ptr<content::URLLoaderThrottle>> diff --git a/chromium/content/browser/loader/prefetch_url_loader_service.h b/chromium/content/browser/loader/prefetch_url_loader_service.h index 6573ee69d52..69c241394f2 100644 --- a/chromium/content/browser/loader/prefetch_url_loader_service.h +++ b/chromium/content/browser/loader/prefetch_url_loader_service.h @@ -9,10 +9,10 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "content/common/content_export.h" +#include "content/common/url_loader_factory_bundle.h" #include "content/public/browser/browser_thread.h" #include "mojo/public/cpp/bindings/strong_binding_set.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" -#include "third_party/blink/public/mojom/loader/prefetch_url_loader_service.mojom.h" namespace net { class URLRequestContextGetter; @@ -20,6 +20,7 @@ class URLRequestContextGetter; namespace network { class SharedURLLoaderFactory; +class URLLoaderFactoryBundleInfo; } namespace content { @@ -31,13 +32,9 @@ class URLLoaderThrottle; class CONTENT_EXPORT PrefetchURLLoaderService final : public base::RefCountedThreadSafe<PrefetchURLLoaderService, BrowserThread::DeleteOnIOThread>, - public network::mojom::URLLoaderFactory, - public blink::mojom::PrefetchURLLoaderService { + public network::mojom::URLLoaderFactory { public: - // |factory_getter| could be null in non-NetworkService case. - // Created on the UI thread. - PrefetchURLLoaderService( - scoped_refptr<URLLoaderFactoryGetter> network_loader_factory); + PrefetchURLLoaderService(); // Must be called on the IO thread. The given |resource_context| will // be valid as far as request_context_getter returns non-null context. @@ -45,8 +42,9 @@ class CONTENT_EXPORT PrefetchURLLoaderService final ResourceContext* resource_context, scoped_refptr<net::URLRequestContextGetter> request_context_getter); - void ConnectToService(int frame_tree_node_id, - blink::mojom::PrefetchURLLoaderServiceRequest request); + void GetFactory(network::mojom::URLLoaderFactoryRequest request, + int frame_tree_node_id, + std::unique_ptr<URLLoaderFactoryBundleInfo> factory_info); // Used only when NetworkService is not enabled (or indirectly via the // other CreateLoaderAndStart when NetworkService is enabled). @@ -75,10 +73,9 @@ class CONTENT_EXPORT PrefetchURLLoaderService final private: friend class base::DeleteHelper<content::PrefetchURLLoaderService>; friend struct BrowserThread::DeleteOnThread<BrowserThread::IO>; - ~PrefetchURLLoaderService() override; + struct BindContext; - // blink::mojom::PrefetchURLLoaderService: - void GetFactory(network::mojom::URLLoaderFactoryRequest request) override; + ~PrefetchURLLoaderService() override; // network::mojom::URLLoaderFactory: void CreateLoaderAndStart(network::mojom::URLLoaderRequest request, @@ -97,16 +94,12 @@ class CONTENT_EXPORT PrefetchURLLoaderService final const network::ResourceRequest& request, base::RepeatingCallback<int(void)> frame_tree_node_id_getter); - mojo::BindingSet<blink::mojom::PrefetchURLLoaderService, - int /* frame_tree_node_id */> - service_bindings_; - scoped_refptr<URLLoaderFactoryGetter> loader_factory_getter_; ResourceContext* resource_context_ = nullptr; scoped_refptr<net::URLRequestContextGetter> request_context_getter_; mojo::BindingSet<network::mojom::URLLoaderFactory, - int /* frame_tree_node_id */> + std::unique_ptr<BindContext>> loader_factory_bindings_; base::RepeatingClosure prefetch_load_callback_for_testing_; diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler.cc b/chromium/content/browser/loader/redirect_to_file_resource_handler.cc deleted file mode 100644 index a6ce3d0b48f..00000000000 --- a/chromium/content/browser/loader/redirect_to_file_resource_handler.cc +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/redirect_to_file_resource_handler.h" - -#include <utility> - -#include "base/bind.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/threading/thread_restrictions.h" -#include "content/browser/loader/resource_controller.h" -#include "content/browser/loader/resource_request_info_impl.h" -#include "content/browser/loader/temporary_file_stream.h" -#include "net/base/file_stream.h" -#include "net/base/io_buffer.h" -#include "net/base/mime_sniffer.h" -#include "net/base/net_errors.h" -#include "services/network/public/cpp/resource_response.h" -#include "storage/browser/blob/shareable_file_reference.h" - -using storage::ShareableFileReference; - -namespace { - -// This class is similar to identically named classes in AsyncResourceHandler -// and MimeTypeResourceHandler, but not quite. -// TODO(ncbray): generalize and unify these cases? -// In general, it's a bad idea to point to a subbuffer (particularly with -// GrowableIOBuffer) because the backing IOBuffer may realloc its data. In this -// particular case we know RedirectToFileResourceHandler will not realloc its -// buffer while a write is occurring, so we should be safe. This property is -// somewhat fragile, however, and depending on it is dangerous. A more -// principled approach would require significant refactoring, however, so for -// the moment we're relying on fragile properties. -class DependentIOBufferForRedirectToFile : public net::WrappedIOBuffer { - public: - DependentIOBufferForRedirectToFile(net::IOBuffer* backing, char* memory) - : net::WrappedIOBuffer(memory), backing_(backing) {} - - private: - ~DependentIOBufferForRedirectToFile() override {} - - scoped_refptr<net::IOBuffer> backing_; -}; - -} // namespace - -namespace content { - -const int RedirectToFileResourceHandler::kInitialReadBufSize = 32768; -const int RedirectToFileResourceHandler::kMaxReadBufSize = 524288; - -// A separate IO thread object to manage the lifetime of the net::FileStream and -// the ShareableFileReference. When the handler is destroyed, it asynchronously -// closes net::FileStream after all pending writes complete. Only after the -// stream is closed is the ShareableFileReference released, to ensure the -// temporary is not deleted before it is closed. -class RedirectToFileResourceHandler::Writer { - public: - Writer(RedirectToFileResourceHandler* handler, - std::unique_ptr<net::FileStream> file_stream, - ShareableFileReference* deletable_file) - : handler_(handler), - file_stream_(std::move(file_stream)), - is_writing_(false), - deletable_file_(deletable_file) { - DCHECK(!deletable_file_->path().empty()); - } - - bool is_writing() const { return is_writing_; } - const base::FilePath& path() const { return deletable_file_->path(); } - - int Write(net::IOBuffer* buf, int buf_len) { - DCHECK(!is_writing_); - DCHECK(handler_); - int result = file_stream_->Write( - buf, buf_len, - base::BindOnce(&Writer::DidWriteToFile, base::Unretained(this))); - if (result == net::ERR_IO_PENDING) - is_writing_ = true; - return result; - } - - void Close() { - handler_ = nullptr; - if (!is_writing_) - CloseAndDelete(); - } - - private: - // Only DidClose can delete this. - ~Writer() { - } - - void DidWriteToFile(int result) { - DCHECK(is_writing_); - is_writing_ = false; - if (handler_) { - handler_->DidWriteToFile(result); - } else { - CloseAndDelete(); - } - } - - void CloseAndDelete() { - DCHECK(!is_writing_); - int result = file_stream_->Close( - base::BindOnce(&Writer::DidClose, base::Unretained(this))); - if (result != net::ERR_IO_PENDING) - DidClose(result); - } - - void DidClose(int result) { - delete this; - } - - RedirectToFileResourceHandler* handler_; - - std::unique_ptr<net::FileStream> file_stream_; - bool is_writing_; - - // We create a ShareableFileReference that's deletable for the temp file - // created as a result of the download. - scoped_refptr<storage::ShareableFileReference> deletable_file_; - - DISALLOW_COPY_AND_ASSIGN(Writer); -}; - -RedirectToFileResourceHandler::RedirectToFileResourceHandler( - std::unique_ptr<ResourceHandler> next_handler, - net::URLRequest* request) - : LayeredResourceHandler(request, std::move(next_handler)), - buf_(new net::GrowableIOBuffer()), - weak_factory_(this) {} - -RedirectToFileResourceHandler::~RedirectToFileResourceHandler() { - // Orphan the writer to asynchronously close and release the temporary file. - if (writer_) { - writer_->Close(); - writer_ = nullptr; - } -} - -void RedirectToFileResourceHandler:: - SetCreateTemporaryFileStreamFunctionForTesting( - const CreateTemporaryFileStreamFunction& create_temporary_file_stream) { - create_temporary_file_stream_ = create_temporary_file_stream; -} - -void RedirectToFileResourceHandler::OnResponseStarted( - network::ResourceResponse* response, - std::unique_ptr<ResourceController> controller) { - if (!writer_) { - response_pending_file_creation_ = response; - HoldController(std::move(controller)); - request()->LogBlockedBy("RedirectToFileResourceHandler"); - return; - } - response->head.download_file_path = writer_->path(); - next_handler_->OnResponseStarted(response, std::move(controller)); -} - -void RedirectToFileResourceHandler::OnWillStart( - const GURL& url, - std::unique_ptr<ResourceController> controller) { - DCHECK(!writer_); - - // Create the file ASAP but don't block. - if (create_temporary_file_stream_.is_null()) { - CreateTemporaryFileStream( - base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, - weak_factory_.GetWeakPtr())); - } else { - create_temporary_file_stream_.Run( - base::Bind(&RedirectToFileResourceHandler::DidCreateTemporaryFile, - weak_factory_.GetWeakPtr())); - } - next_handler_->OnWillStart(url, std::move(controller)); -} - -void RedirectToFileResourceHandler::OnWillRead( - scoped_refptr<net::IOBuffer>* buf, - int* buf_size, - std::unique_ptr<ResourceController> controller) { - if (buf_->capacity() < next_buffer_size_) - buf_->SetCapacity(next_buffer_size_); - - // We should have paused this network request already if the buffer is full. - DCHECK(!BufIsFull()); - - *buf = buf_.get(); - *buf_size = buf_->RemainingCapacity(); - - buf_write_pending_ = true; - controller->Resume(); -} - -void RedirectToFileResourceHandler::OnReadCompleted( - int bytes_read, - std::unique_ptr<ResourceController> controller) { - DCHECK(buf_write_pending_); - buf_write_pending_ = false; - - // We use the buffer's offset field to record the end of the buffer. - int new_offset = buf_->offset() + bytes_read; - DCHECK(new_offset <= buf_->capacity()); - buf_->set_offset(new_offset); - - if (buf_->capacity() == bytes_read) { - // The network layer has saturated our buffer in one read. Next time, we - // should give it a bigger buffer for it to fill. - next_buffer_size_ = std::min(next_buffer_size_ * 2, kMaxReadBufSize); - } - - HoldController(std::move(controller)); - // WriteMore will resume the request if there's more buffer space. - if (!WriteMore()) { - CancelWithError(net::ERR_FAILED); - return; - } - - if (has_controller()) - request()->LogBlockedBy("RedirectToFileResourceHandler"); -} - -void RedirectToFileResourceHandler::OnResponseCompleted( - const net::URLRequestStatus& status, - std::unique_ptr<ResourceController> controller) { - if (writer_ && writer_->is_writing()) { - completed_during_write_ = true; - completed_status_ = status; - HoldController(std::move(controller)); - request()->LogBlockedBy("RedirectToFileResourceHandler"); - return; - } - next_handler_->OnResponseCompleted(status, std::move(controller)); -} - -int RedirectToFileResourceHandler::GetBufferSizeForTesting() const { - return buf_->capacity(); -} - -void RedirectToFileResourceHandler::DidCreateTemporaryFile( - base::File::Error error_code, - std::unique_ptr<net::FileStream> file_stream, - ShareableFileReference* deletable_file) { - DCHECK(!writer_); - if (error_code != base::File::FILE_OK) { - if (has_controller()) { - CancelWithError(net::FileErrorToNetError(error_code)); - } else { - OutOfBandCancel(net::FileErrorToNetError(error_code), - true /* tell_renderer */); - } - return; - } - - writer_ = new Writer(this, std::move(file_stream), deletable_file); - - if (response_pending_file_creation_) { - scoped_refptr<network::ResourceResponse> response = - std::move(response_pending_file_creation_); - request()->LogUnblocked(); - OnResponseStarted(response.get(), ReleaseController()); - } -} - -void RedirectToFileResourceHandler::DidWriteToFile(int result) { - bool failed = false; - if (result > 0) { - OnDataDownloaded(result); - write_cursor_ += result; - // WriteMore will resume the request if the request hasn't completed and - // there's more buffer space. - failed = !WriteMore(); - } else { - failed = true; - } - - if (failed) { - DCHECK(!writer_->is_writing()); - // TODO(davidben): Recover the error code from WriteMore or |result|, as - // appropriate. - if (completed_during_write_ && completed_status_.is_success()) { - // If the request successfully completed mid-write, but the write failed, - // convert the status to a failure for downstream. - completed_status_ = net::URLRequestStatus(net::URLRequestStatus::CANCELED, - net::ERR_FAILED); - } - if (!completed_during_write_) { - if (has_controller()) { - // If the write buffer is full, |this| has deferred the request, and - // can do an in-band cancel. - CancelWithError(net::ERR_FAILED); - } else { - OutOfBandCancel(net::ERR_FAILED, true /* tell_renderer */); - } - return; - } - } - - if (completed_during_write_ && !writer_->is_writing()) { - // Resume shutdown now that all data has been written to disk. Note that - // this should run even in the |failed| case above, otherwise a failed write - // leaves the handler stuck. - DCHECK(has_controller()); - request()->LogUnblocked(); - next_handler_->OnResponseCompleted(completed_status_, ReleaseController()); - } -} - -bool RedirectToFileResourceHandler::WriteMore() { - DCHECK(writer_); - - for (;;) { - if (write_cursor_ == buf_->offset()) { - // We've caught up to the network load, but it may be in the process of - // appending more data to the buffer. - if (!buf_write_pending_) { - buf_->set_offset(0); - write_cursor_ = 0; - } - break; - } - if (writer_->is_writing()) - break; - DCHECK(write_cursor_ < buf_->offset()); - - // Create a temporary buffer pointing to a subsection of the data buffer so - // that it can be passed to Write. This code makes some crazy scary - // assumptions about object lifetimes, thread sharing, and that buf_ will - // not realloc durring the write due to how the state machine in this class - // works. - // Note that buf_ is also shared with the code that writes data into the - // cache, so modifying it can cause some pretty subtle race conditions: - // https://code.google.com/p/chromium/issues/detail?id=152076 - // We're using DependentIOBuffer instead of DrainableIOBuffer to dodge some - // of these issues, for the moment. - // TODO(ncbray) make this code less crazy scary. - // Also note that Write may increase the refcount of "wrapped" deep in the - // bowels of its implementation, the use of scoped_refptr here is not - // spurious. - scoped_refptr<DependentIOBufferForRedirectToFile> wrapped = - new DependentIOBufferForRedirectToFile( - buf_.get(), buf_->StartOfBuffer() + write_cursor_); - int write_len = buf_->offset() - write_cursor_; - - int rv = writer_->Write(wrapped.get(), write_len); - if (rv == net::ERR_IO_PENDING) - break; - if (rv <= 0) - return false; - OnDataDownloaded(rv); - write_cursor_ += rv; - } - - // If the request was defered for a reason other than having been completed, - // and the buffer has space, resume the request. - if (has_controller() && !completed_during_write_ && !BufIsFull()) { - request()->LogUnblocked(); - Resume(); - } - return true; -} - -bool RedirectToFileResourceHandler::BufIsFull() const { - // This is a hack to workaround MimeTypeResourceHandler's inability to - // deal with a ResourceHandler that returns a buffer size of less than - // 2 * net::kMaxBytesToSniff from its OnWillRead method. - // TODO(darin): Fix this retardation! - return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff); -} - -} // namespace content diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler.h b/chromium/content/browser/loader/redirect_to_file_resource_handler.h deleted file mode 100644 index 41fb23c1301..00000000000 --- a/chromium/content/browser/loader/redirect_to_file_resource_handler.h +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_ -#define CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "content/browser/loader/layered_resource_handler.h" -#include "content/browser/loader/temporary_file_stream.h" -#include "content/common/content_export.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_status.h" -#include "url/gurl.h" - -namespace net { -class FileStream; -class GrowableIOBuffer; -} - -namespace storage { -class ShareableFileReference; -} - -namespace content { - -class ResourceController; - -// Redirects network data to a file. This is intended to be layered in front of -// either the AsyncResourceHandler or the SyncResourceHandler. The downstream -// resource handler does not see OnWillRead or OnReadCompleted calls. Instead, -// the ResourceResponse contains the path to a temporary file and -// OnDataDownloaded is called as the file downloads. -class CONTENT_EXPORT RedirectToFileResourceHandler - : public LayeredResourceHandler { - public: - // Exposed for testing. - static const int kInitialReadBufSize; - static const int kMaxReadBufSize; - - typedef base::Callback<void(const CreateTemporaryFileStreamCallback&)> - CreateTemporaryFileStreamFunction; - - // Create a RedirectToFileResourceHandler for |request| which wraps - // |next_handler|. - RedirectToFileResourceHandler(std::unique_ptr<ResourceHandler> next_handler, - net::URLRequest* request); - ~RedirectToFileResourceHandler() override; - - // Replace the CreateTemporaryFileStream implementation with a mocked one for - // testing purposes. The function should create a net::FileStream and a - // ShareableFileReference and then asynchronously pass them to the - // CreateTemporaryFileStreamCallback. - void SetCreateTemporaryFileStreamFunctionForTesting( - const CreateTemporaryFileStreamFunction& create_temporary_file_stream); - - // LayeredResourceHandler implementation: - void OnResponseStarted( - network::ResourceResponse* response, - std::unique_ptr<ResourceController> controller) override; - void OnWillStart(const GURL& url, - std::unique_ptr<ResourceController> controller) override; - void OnWillRead(scoped_refptr<net::IOBuffer>* buf, - int* buf_size, - std::unique_ptr<ResourceController> controller) override; - void OnReadCompleted(int bytes_read, - std::unique_ptr<ResourceController> controller) override; - void OnResponseCompleted( - const net::URLRequestStatus& status, - std::unique_ptr<ResourceController> controller) override; - - // Returns the size of |buf_|, to make sure it's being increased as expected. - int GetBufferSizeForTesting() const; - - private: - void DidCreateTemporaryFile(base::File::Error error_code, - std::unique_ptr<net::FileStream> file_stream, - storage::ShareableFileReference* deletable_file); - - // Called by RedirectToFileResourceHandler::Writer. - void DidWriteToFile(int result); - - // Attempts to write more data to the file, if possible. Returns false on - // error. Returns true if there's already a write in progress, all data was - // written successfully, or a new write was started that will complete - // asynchronously. Resumes the request if there's more data to read and more - // buffer space available. - bool WriteMore(); - - bool BufIsFull() const; - - // If populated, OnResponseStarted completion is pending on file creation. - scoped_refptr<network::ResourceResponse> response_pending_file_creation_; - CreateTemporaryFileStreamFunction create_temporary_file_stream_; - - // We allocate a single, fixed-size IO buffer (buf_) used to read from the - // network (buf_write_pending_ is true while the system is copying data into - // buf_), and then write this buffer out to disk (write_callback_pending_ is - // true while writing to disk). Reading from the network is suspended while - // the buffer is full (BufIsFull returns true). The write_cursor_ member - // tracks the offset into buf_ that we are writing to disk. - - scoped_refptr<net::GrowableIOBuffer> buf_; - bool buf_write_pending_ = false; - int write_cursor_ = 0; - - // Helper writer object which maintains references to the net::FileStream and - // storage::ShareableFileReference. This is maintained separately so that, - // on Windows, the temporary file isn't deleted until after it is closed. - class Writer; - Writer* writer_ = nullptr; - - // |next_buffer_size_| is the size of the buffer to be allocated on the next - // OnWillRead() call. We exponentially grow the size of the buffer allocated - // when our owner fills our buffers. On the first OnWillRead() call, we - // allocate a buffer of 32k and double it in OnReadCompleted() if the buffer - // was filled, up to a maximum size of 512k. - int next_buffer_size_ = kInitialReadBufSize; - - bool completed_during_write_ = false; - net::URLRequestStatus completed_status_; - - base::WeakPtrFactory<RedirectToFileResourceHandler> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(RedirectToFileResourceHandler); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_ diff --git a/chromium/content/browser/loader/redirect_to_file_resource_handler_unittest.cc b/chromium/content/browser/loader/redirect_to_file_resource_handler_unittest.cc deleted file mode 100644 index ac012a09e0e..00000000000 --- a/chromium/content/browser/loader/redirect_to_file_resource_handler_unittest.cc +++ /dev/null @@ -1,925 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/redirect_to_file_resource_handler.h" - -#include <algorithm> -#include <limits> -#include <memory> -#include <string> -#include <vector> - -#include "base/bind.h" -#include "base/callback_helpers.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/run_loop.h" -#include "base/strings/string_piece.h" -#include "base/threading/thread_task_runner_handle.h" -#include "content/browser/loader/mock_resource_loader.h" -#include "content/browser/loader/temporary_file_stream.h" -#include "content/browser/loader/test_resource_handler.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "net/base/completion_callback.h" -#include "net/base/completion_once_callback.h" -#include "net/base/file_stream.h" -#include "net/base/io_buffer.h" -#include "net/base/mime_sniffer.h" -#include "net/base/net_errors.h" -#include "net/base/request_priority.h" -#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_status.h" -#include "net/url_request/url_request_test_util.h" -#include "services/network/public/cpp/resource_response.h" -#include "storage/browser/blob/shareable_file_reference.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace content { -namespace { - -// The maximum size for which the initial read will always be sync, even when -// the wrote completes asynchronously. See -// RedirectToFileResourceHandler::BufIsFull(). -const int kMaxInitialSyncReadSize = - RedirectToFileResourceHandler::kInitialReadBufSize - - 2 * net::kMaxBytesToSniff - 1; - -// Used to indicate whether FileStream operations and the lower-layer -// TestResourceHandler operations should complete immediately or by -// asynchronously invoking a callback. Each test is run with all operations set -// by default to each mode, though some tests override the mode of some -// operations. -enum class CompletionMode { - SYNC, - ASYNC, -}; - -// Mock in-memory net::FileStream implementation that can be configured to -// return errors and complete operations synchronously or asynchronously. -class MockFileStream : public net::FileStream { - public: - struct OperationResult { - OperationResult(int result, CompletionMode completion_mode) - : result(result), completion_mode(completion_mode) {} - - OperationResult() - : OperationResult(net::ERR_UNEXPECTED, CompletionMode::SYNC) {} - - int result; - CompletionMode completion_mode; - }; - - MockFileStream() : FileStream(base::ThreadTaskRunnerHandle::Get()) {} - - ~MockFileStream() override { - EXPECT_EQ(expect_closed_, closed_); - // Most of these tests write 32k or more, which is a bit much for the - // command line. - EXPECT_TRUE(expected_written_data_ == written_data_); - } - - // net::FileStream implementation: - - int Open(const base::FilePath& path, - int open_flags, - net::CompletionOnceCallback callback) override { - return ReturnResult(open_result_, std::move(callback)); - } - - int Close(net::CompletionOnceCallback callback) override { - EXPECT_FALSE(closed_); - int result = ReturnResult( - close_result_, - base::BindOnce(&MockFileStream::SetClosedAndRunCallback, - base::Unretained(this), std::move(callback))); - if (result != net::ERR_IO_PENDING) - closed_ = true; - return result; - } - - bool IsOpen() const override { - NOTREACHED(); - return false; - } - - int Seek(int64_t offset, net::Int64CompletionOnceCallback callback) override { - NOTREACHED(); - return net::ERR_UNEXPECTED; - } - - int Read(net::IOBuffer* buf, - int buf_len, - net::CompletionOnceCallback callback) override { - NOTREACHED(); - return net::ERR_UNEXPECTED; - } - - int Write(net::IOBuffer* buf, - int buf_len, - net::CompletionOnceCallback callback) override { - // 0-byte writes aren't allowed. - EXPECT_GT(buf_len, 0); - - OperationResult write_result = next_write_result_; - next_write_result_ = all_write_results_; - if (write_result.result > buf_len) - write_result.result = buf_len; - if (write_result.result > 0) - written_data_ += std::string(buf->data(), write_result.result); - - return ReturnResult(write_result, std::move(callback)); - } - - int Flush(net::CompletionOnceCallback callback) override { - NOTREACHED(); - return net::ERR_UNEXPECTED; - } - - void set_open_result(OperationResult open_result) { - open_result_ = open_result; - } - void set_close_result(OperationResult close_result) { - close_result_ = close_result; - } - - // Sets the result for all write operations. Returned result is capped at - // number of bytes the consumer actually tried to write. Overrides - // |next_write_result_|. - void set_all_write_results(OperationResult all_write_results) { - next_write_result_ = all_write_results_ = all_write_results; - } - - // Sets the result of only the next write operation. - void set_next_write_result(OperationResult next_write_result) { - next_write_result_ = next_write_result; - } - - void set_expected_written_data(const std::string& expected_written_data) { - expected_written_data_ = expected_written_data; - } - - // Sets whether the file should expect to be closed. - void set_expect_closed(bool expect_closed) { expect_closed_ = expect_closed; } - - private: - void SetClosedAndRunCallback(net::CompletionOnceCallback callback, - int result) { - EXPECT_FALSE(closed_); - closed_ = true; - std::move(callback).Run(result); - } - - int ReturnResult(OperationResult result, - net::CompletionOnceCallback callback) { - if (result.completion_mode == CompletionMode::SYNC) - return result.result; - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), result.result)); - return net::ERR_IO_PENDING; - } - - OperationResult open_result_; - OperationResult close_result_; - OperationResult next_write_result_; - OperationResult all_write_results_; - - std::string expected_written_data_; - std::string written_data_; - - bool expect_closed_ = false; - bool closed_ = false; - - DISALLOW_COPY_AND_ASSIGN(MockFileStream); -}; - -class RedirectToFileResourceHandlerTest - : public testing::TestWithParam<CompletionMode> { - public: - RedirectToFileResourceHandlerTest() - : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), - url_request_( - url_request_context_.CreateRequest(GURL("foo://bar/"), - net::DEFAULT_PRIORITY, - &url_request_delegate_, - TRAFFIC_ANNOTATION_FOR_TESTS)) { - base::CreateTemporaryFile(&temp_file_path_); - std::unique_ptr<TestResourceHandler> test_handler = - std::make_unique<TestResourceHandler>(); - test_handler->set_expect_on_data_downloaded(true); - if (GetParam() == CompletionMode::ASYNC) { - // Don't defer OnResponseCompleted, by default, since that's really - // unusual. - test_handler->set_defer_on_response_started(true); - test_handler->set_defer_on_will_start(true); - } - test_handler_ = test_handler->GetWeakPtr(); - - redirect_to_file_handler_ = std::make_unique<RedirectToFileResourceHandler>( - std::move(test_handler), url_request_.get()); - mock_loader_ = - std::make_unique<MockResourceLoader>(redirect_to_file_handler_.get()); - redirect_to_file_handler_->SetCreateTemporaryFileStreamFunctionForTesting( - base::Bind(&RedirectToFileResourceHandlerTest:: - SetCreateTemporaryFileStreamCallback, - base::Unretained(this))); - - file_stream_ = std::make_unique<MockFileStream>(); - file_stream_->set_open_result( - MockFileStream::OperationResult(net::OK, GetParam())); - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), GetParam())); - file_stream_->set_close_result( - MockFileStream::OperationResult(net::OK, GetParam())); - } - - ~RedirectToFileResourceHandlerTest() override { - EXPECT_FALSE(test_handler_->on_read_completed_called()); - - // This should post a task to delete the temporary file. - redirect_to_file_handler_.reset(); - mock_loader_.reset(); - url_request_.reset(); - // This should delete the temporary file, and ensure - // MockFileStream::Cancel() is called. - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(base::PathExists(temp_file_path_)); - } - - // Creates a test string of the specified length, and sets that as the - // expected data written to |file_stream_|. - std::string CreateTestData(size_t length) { - std::string test_data; - test_data.reserve(length); - for (size_t i = 0; i < length; ++i) - test_data.push_back(static_cast<char>(i % 256)); - file_stream_->set_expected_written_data(test_data); - return test_data; - } - - // The "CreateTemporaryFileStream" method invoked by the - // RedirectToFileResourceHandler. Just sets a callback that will be invoked - // directly. - void SetCreateTemporaryFileStreamCallback( - const CreateTemporaryFileStreamCallback& create_file_stream_callback) { - create_file_stream_callback_ = create_file_stream_callback; - } - - void PerformOnWillStart() { - MockResourceLoader::Status expected_status; - if (GetParam() == CompletionMode::ASYNC) { - expected_status = MockResourceLoader::Status::CALLBACK_PENDING; - } else { - expected_status = MockResourceLoader::Status::IDLE; - } - EXPECT_EQ(expected_status, mock_loader_->OnWillStart(url_request_->url())); - } - - // Sets up the file stream or error, and performs the file callback. - void PerformCreateFile(base::File::Error file_error) { - DCHECK(file_stream_); - - file_stream_->set_expect_closed(file_error == base::File::FILE_OK); - if (file_error != base::File::FILE_OK) - file_stream_ = nullptr; - - base::ResetAndReturn(&create_file_stream_callback_) - .Run(file_error, std::move(file_stream_), - // Not really used by the test, but the ResourceHandler expects it - // to be non-null. - storage::ShareableFileReference::GetOrCreate( - temp_file_path_, - storage::ShareableFileReference::DELETE_ON_FINAL_RELEASE, - base::ThreadTaskRunnerHandle::Get().get()) - .get()); - } - - // Simulates starting the request, the response starting, and stream creation - // completing with the specified error code. Has |test_handler_| resume the - // request, if needed. Returns final status of |mock_loader_|. - MockResourceLoader::Status StartAndCreateStream(base::File::Error file_error) - WARN_UNUSED_RESULT { - PerformOnWillStart(); - - // Create the file right away. - PerformCreateFile(file_error); - - // If this is an async test, |test_handler_| will defer the OnWillStart - // event on success (On error, its OnWillStart method is not called). - if (file_error == base::File::FILE_OK && - GetParam() == CompletionMode::ASYNC) { - EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - test_handler_->Resume(); - mock_loader_->WaitUntilIdleOrCanceled(); - } - EXPECT_NE(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - return mock_loader_->status(); - } - - // Convenience wrapper for MockLoader methods that will Resume |test_handler_| - // and wait for it to resume the request if running an async test. - MockResourceLoader::Status OnResponseStartedAndWaitForResult() - WARN_UNUSED_RESULT { - mock_loader_->OnResponseStarted( - base::MakeRefCounted<network::ResourceResponse>()); - if (GetParam() == CompletionMode::ASYNC) { - EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - test_handler_->Resume(); - mock_loader_->WaitUntilIdleOrCanceled(); - } - EXPECT_NE(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - return mock_loader_->status(); - } - - // Utility method that simulates a final 0-byte read and response completed - // events, and checks that completion is handled correctly. Expects all data - // to already have been written to the file. - void CompleteRequestSuccessfully(int expected_total_bytes_downloaded) { - // The loader should be idle and all the data should have already been - // processed. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); - EXPECT_EQ(expected_total_bytes_downloaded, - test_handler_->total_bytes_downloaded()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted("")); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::OK))); - EXPECT_EQ(expected_total_bytes_downloaded, - test_handler_->total_bytes_downloaded()); - EXPECT_EQ(net::URLRequestStatus::SUCCESS, - test_handler_->final_status().status()); - } - - protected: - TestBrowserThreadBundle thread_bundle_; - base::FilePath temp_file_path_; - net::TestURLRequestContext url_request_context_; - net::TestDelegate url_request_delegate_; - base::WeakPtr<TestResourceHandler> test_handler_; - std::unique_ptr<net::URLRequest> url_request_; - std::unique_ptr<MockResourceLoader> mock_loader_; - std::unique_ptr<RedirectToFileResourceHandler> redirect_to_file_handler_; - std::unique_ptr<MockFileStream> file_stream_; - - CreateTemporaryFileStreamCallback create_file_stream_callback_; -}; - -TEST_P(RedirectToFileResourceHandlerTest, EmptyBody) { - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - CompleteRequestSuccessfully(0); -} - -TEST_P(RedirectToFileResourceHandlerTest, SingleBodyRead) { - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data)); - // Wait for the write to complete, in the async case. - base::RunLoop().RunUntilIdle(); - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, SingleBodyReadDelayedFileOnResponse) { - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - PerformOnWillStart(); - if (GetParam() == CompletionMode::ASYNC) { - EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - test_handler_->Resume(); - mock_loader_->WaitUntilIdleOrCanceled(); - } - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); - mock_loader_->OnResponseStarted( - base::MakeRefCounted<network::ResourceResponse>()); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - - PerformCreateFile(base::File::FILE_OK); - - if (GetParam() == CompletionMode::ASYNC) { - EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - test_handler_->Resume(); - mock_loader_->WaitUntilIdleOrCanceled(); - } - EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data)); - // Wait for the write to complete, in the async case. - base::RunLoop().RunUntilIdle(); - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, SingleBodyReadDelayedFileError) { - std::string test_data = CreateTestData(0); - - PerformOnWillStart(); - if (GetParam() == CompletionMode::ASYNC) { - EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - test_handler_->Resume(); - mock_loader_->WaitUntilIdleOrCanceled(); - } - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); - mock_loader_->OnResponseStarted( - base::MakeRefCounted<network::ResourceResponse>()); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->status()); - - PerformCreateFile(base::File::FILE_ERROR_FAILED); - - EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status()); - EXPECT_EQ(0, test_handler_->on_response_completed_called()); - EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -TEST_P(RedirectToFileResourceHandlerTest, ManySequentialBodyReads) { - const size_t kBytesPerRead = 128; - std::string test_data = - CreateTestData(RedirectToFileResourceHandler::kInitialReadBufSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - for (size_t offset = 0; offset < test_data.length(); - offset += kBytesPerRead) { - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - size_t length = std::min(kBytesPerRead, test_data.length() - offset); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted( - base::StringPiece(test_data.data() + offset, length))); - // Spin the message loop, to allow async writes to complete. - base::RunLoop().RunUntilIdle(); - } - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, PartialWrites) { - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - file_stream_->set_all_write_results(MockFileStream::OperationResult( - RedirectToFileResourceHandler::kInitialReadBufSize / 50, GetParam())); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data)); - // Wait for the writes to complete, in the async case. - base::RunLoop().RunUntilIdle(); - - CompleteRequestSuccessfully(test_data.size()); -} - -// Same as above, but read enough data to defer reading the body. -TEST_P(RedirectToFileResourceHandlerTest, PartialWrites2) { - std::string test_data = - CreateTestData(RedirectToFileResourceHandler::kInitialReadBufSize); - // Async reads, as otherwise reading won't be defered. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - RedirectToFileResourceHandler::kInitialReadBufSize / 50, - CompletionMode::ASYNC)); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnReadCompleted(test_data)); - // Wait for the writes to complete. - base::RunLoop().RunUntilIdle(); - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, ReceiveDataWhileWritingBody) { - const int kFirstWriteSize = 100; - - // This test only makes sense when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - // Will use multiple writes, with a combined size such that they don't - // saturate the buffer. - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ( - MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr(0, kFirstWriteSize))); - // Next read completes before first write succeeds. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr( - kFirstWriteSize, sizeof(test_data) - kFirstWriteSize))); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - - // Wait for both writes to succeed. - base::RunLoop().RunUntilIdle(); - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, ReceiveDataAndDeferWhileWritingBody) { - const int kFirstWriteSize = 100; - - // This test only makes sense when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - // Will use multiple writes, with a combined size such that they saturate the - // buffer. - std::string test_data = - CreateTestData(RedirectToFileResourceHandler::kInitialReadBufSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ( - MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr(0, kFirstWriteSize))); - // Next read completes before first write succeeds. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnReadCompleted(test_data.substr( - kFirstWriteSize, sizeof(test_data) - kFirstWriteSize))); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - - // Wait for both writes to succeed. - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, - ExpandBufferCapacityManySequentialBodyReads) { - // The buffer is only resized when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - const int kInitialReadSize = - RedirectToFileResourceHandler::kInitialReadBufSize; - const int kMaxReadSize = RedirectToFileResourceHandler::kMaxReadBufSize; - int next_read_size = kInitialReadSize; - int total_read_bytes = 0; - // Populate |read_sizes| with expected buffer sizes if each previous read - // filled the entire buffer. - std::vector<size_t> read_sizes; - while (true) { - total_read_bytes += next_read_size; - read_sizes.push_back(next_read_size); - if (next_read_size == kMaxReadSize) - break; - next_read_size = std::min(2 * next_read_size, kMaxReadSize); - } - // Once the max is reached, do another round to make sure it isn't increased. - total_read_bytes += kMaxReadSize; - read_sizes.push_back(kMaxReadSize); - - std::string test_data = CreateTestData(total_read_bytes); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - int offset = 0; - for (int read_size : read_sizes) { - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnReadCompleted( - base::StringPiece(test_data.data() + offset, read_size))); - offset += read_size; - - EXPECT_EQ(read_size, redirect_to_file_handler_->GetBufferSizeForTesting()); - - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status()); - } - - CompleteRequestSuccessfully(test_data.size()); -} - -TEST_P(RedirectToFileResourceHandlerTest, CompletedWhileWritingBody) { - // This test only makes sense when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data)); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - - // While data is being written to the disk, the request completes. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted("")); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::OK))); - - // Wait for the write to complete and the final status sent to the - // TestHandler. - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(static_cast<int>(test_data.size()), - test_handler_->total_bytes_downloaded()); - EXPECT_EQ(net::URLRequestStatus::SUCCESS, - test_handler_->final_status().status()); -} - -TEST_P(RedirectToFileResourceHandlerTest, - CompletedWhileWritingBodyAndWritePending) { - const int kFirstWriteSize = 100; - - // This test only makes sense when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - // Will use multiple writes, with a combined size such that they don't - // saturate the buffer. - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ( - MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr(0, kFirstWriteSize))); - // Next read completes before first write succeeds. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr( - kFirstWriteSize, sizeof(test_data) - kFirstWriteSize))); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - - // While the first write is still going on, the request completes. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted("")); - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::OK))); - - // Wait for both writes to complete and the final status to be sent to the - // TestHandler. - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(static_cast<int>(test_data.size()), - test_handler_->total_bytes_downloaded()); - EXPECT_EQ(net::URLRequestStatus::SUCCESS, - test_handler_->final_status().status()); -} - -TEST_P(RedirectToFileResourceHandlerTest, SingleBodyReadAndFail) { - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data)); - - // Wait for the write to complete. - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(static_cast<int>(test_data.size()), - test_handler_->total_bytes_downloaded()); - - // Next read fails and request is torn down synchronously. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -TEST_P(RedirectToFileResourceHandlerTest, FailedWhileWritingBody) { - // This test only makes sense when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data)); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - - // While data is being written to the disk, the request fails. - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - - // Wait for the write to complete and the final status sent to the - // TestHandler. - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(static_cast<int>(test_data.size()), - test_handler_->total_bytes_downloaded()); - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -TEST_P(RedirectToFileResourceHandlerTest, - FailededWhileWritingBodyAndWritePending) { - const int kFirstWriteSize = 100; - - // This test only makes sense when reads are async. - file_stream_->set_all_write_results(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), CompletionMode::ASYNC)); - - // Will use multiple writes, with a combined size such that they don't - // saturate the buffer. - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ( - MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr(0, kFirstWriteSize))); - // Next read completes before first write succeeds. - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted(test_data.substr( - kFirstWriteSize, sizeof(test_data) - kFirstWriteSize))); - - // While the first write is still going on, the request fails. - ASSERT_EQ(MockResourceLoader::Status::CALLBACK_PENDING, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - - // Wait for both writes to complete and the final status to be sent to the - // TestHandler. - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(static_cast<int>(test_data.size()), - test_handler_->total_bytes_downloaded()); - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -TEST_P(RedirectToFileResourceHandlerTest, CreateFileFails) { - ASSERT_EQ(MockResourceLoader::Status::CANCELED, - StartAndCreateStream(base::File::FILE_ERROR_FAILED)); - - EXPECT_EQ(0, test_handler_->on_response_completed_called()); - EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code()); - if (GetParam() == CompletionMode::ASYNC) { - EXPECT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompletedFromExternalOutOfBandCancel( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - } else { - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - } - - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -TEST_P(RedirectToFileResourceHandlerTest, FirstWriteFails) { - std::string test_data = CreateTestData(kMaxInitialSyncReadSize); - file_stream_->set_expected_written_data(""); - file_stream_->set_next_write_result( - MockFileStream::OperationResult(net::ERR_FAILED, GetParam())); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - mock_loader_->OnReadCompleted(test_data); - // Wait for the write to complete, in the async case. - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status()); - - EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code()); - EXPECT_EQ(0, test_handler_->on_response_completed_called()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - EXPECT_EQ(0, test_handler_->total_bytes_downloaded()); - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -TEST_P(RedirectToFileResourceHandlerTest, SecondWriteFails) { - const int kFirstWriteSize = kMaxInitialSyncReadSize; - std::string test_data = - CreateTestData(RedirectToFileResourceHandler::kInitialReadBufSize); - file_stream_->set_expected_written_data(test_data.substr(0, kFirstWriteSize)); - file_stream_->set_all_write_results( - MockFileStream::OperationResult(net::ERR_FAILED, GetParam())); - file_stream_->set_next_write_result(MockFileStream::OperationResult( - std::numeric_limits<int>::max(), GetParam())); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, - StartAndCreateStream(base::File::FILE_OK)); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - OnResponseStartedAndWaitForResult()); - - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnReadCompleted( - base::StringPiece(test_data.data(), kFirstWriteSize))); - ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead()); - mock_loader_->OnReadCompleted(base::StringPiece( - test_data.data() + kFirstWriteSize, test_data.size() - kFirstWriteSize)); - // Wait for the write to complete, in the async case. - base::RunLoop().RunUntilIdle(); - ASSERT_EQ(MockResourceLoader::Status::CANCELED, mock_loader_->status()); - - EXPECT_EQ(net::ERR_FAILED, mock_loader_->error_code()); - EXPECT_EQ(0, test_handler_->on_response_completed_called()); - ASSERT_EQ(MockResourceLoader::Status::IDLE, - mock_loader_->OnResponseCompleted( - net::URLRequestStatus::FromError(net::ERR_FAILED))); - EXPECT_EQ(kFirstWriteSize, test_handler_->total_bytes_downloaded()); - EXPECT_FALSE(test_handler_->final_status().is_success()); - EXPECT_EQ(net::ERR_FAILED, test_handler_->final_status().error()); -} - -INSTANTIATE_TEST_CASE_P(/* No prefix needed */, - RedirectToFileResourceHandlerTest, - testing::Values(CompletionMode::SYNC, - CompletionMode::ASYNC)); - -} // namespace -} // namespace content diff --git a/chromium/content/browser/loader/resource_buffer.cc b/chromium/content/browser/loader/resource_buffer.cc deleted file mode 100644 index 5e34dd24f15..00000000000 --- a/chromium/content/browser/loader/resource_buffer.cc +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/resource_buffer.h" - -#include <math.h> - -#include "base/logging.h" - -namespace content { - -// A circular buffer allocator. -// -// We keep track of the starting offset (alloc_start_) and the ending offset -// (alloc_end_). There are two layouts to keep in mind: -// -// #1: -// ------------[XXXXXXXXXXXXXXXXXXXXXXX]---- -// ^ ^ -// start end -// -// #2: -// XXXXXXXXXX]---------------------[XXXXXXXX -// ^ ^ -// end start -// -// If end <= start, then we have the buffer wraparound case (depicted second). -// If the buffer is empty, then start and end will be set to -1. -// -// Allocations are always contiguous. - -ResourceBuffer::ResourceBuffer() - : buf_size_(0), - min_alloc_size_(0), - max_alloc_size_(0), - alloc_start_(-1), - alloc_end_(-1) { -} - -ResourceBuffer::~ResourceBuffer() { -} - -bool ResourceBuffer::Initialize(int buffer_size, - int min_allocation_size, - int max_allocation_size) { - CHECK(!IsInitialized()); - - // It would be wasteful if these are not multiples of min_allocation_size. - CHECK_EQ(0, buffer_size % min_allocation_size); - CHECK_EQ(0, max_allocation_size % min_allocation_size); - - buf_size_ = buffer_size; - min_alloc_size_ = min_allocation_size; - max_alloc_size_ = max_allocation_size; - - return shared_mem_.CreateAndMapAnonymous(buf_size_); -} - -bool ResourceBuffer::IsInitialized() const { - return shared_mem_.memory() != nullptr; -} - -base::SharedMemory& ResourceBuffer::GetSharedMemory() { - CHECK(IsInitialized()); - return shared_mem_; -} - -bool ResourceBuffer::CanAllocate() const { - CHECK(IsInitialized()); - - if (alloc_start_ == -1) - return true; - - int diff = alloc_end_ - alloc_start_; - if (diff > 0) - return (buf_size_ - diff) >= min_alloc_size_; - - return -diff >= min_alloc_size_; -} - -char* ResourceBuffer::Allocate(int* size) { - CHECK(CanAllocate()); - - int alloc_offset = 0; - int alloc_size; - - if (alloc_start_ == -1) { - // This is the first allocation. - alloc_start_ = 0; - alloc_end_ = buf_size_; - alloc_size = buf_size_; - } else if (alloc_start_ < alloc_end_) { - // Append the next allocation if it fits. Otherwise, wraparound. - // - // NOTE: We could look to see if a larger allocation is possible by - // wrapping around sooner, but instead we just look to fill the space at - // the end of the buffer provided that meets the min_alloc_size_ - // requirement. - // - if ((buf_size_ - alloc_end_) >= min_alloc_size_) { - alloc_offset = alloc_end_; - alloc_size = buf_size_ - alloc_end_; - alloc_end_ = buf_size_; - } else { - // It must be possible to allocate a least min_alloc_size_. - CHECK(alloc_start_ >= min_alloc_size_); - alloc_size = alloc_start_; - alloc_end_ = alloc_start_; - } - } else { - // This is the wraparound case. - CHECK(alloc_end_ < alloc_start_); - alloc_offset = alloc_end_; - alloc_size = alloc_start_ - alloc_end_; - alloc_end_ = alloc_start_; - } - - // Make sure alloc_size does not exceed max_alloc_size_. We store the - // current value of alloc_size, so that we can use ShrinkLastAllocation to - // trim it back. This allows us to reuse the alloc_end_ adjustment logic. - - alloc_sizes_.push(alloc_size); - - if (alloc_size > max_alloc_size_) { - alloc_size = max_alloc_size_; - ShrinkLastAllocation(alloc_size); - } - - *size = alloc_size; - return static_cast<char*>(shared_mem_.memory()) + alloc_offset; -} - -int ResourceBuffer::GetLastAllocationOffset() const { - CHECK(!alloc_sizes_.empty()); - CHECK(alloc_end_ >= alloc_sizes_.back()); - return alloc_end_ - alloc_sizes_.back(); -} - -void ResourceBuffer::ShrinkLastAllocation(int new_size) { - CHECK(!alloc_sizes_.empty()); - - int aligned_size = (new_size / min_alloc_size_) * min_alloc_size_; - if (aligned_size < new_size) - aligned_size += min_alloc_size_; - - CHECK_LE(new_size, aligned_size); - CHECK_GE(alloc_sizes_.back(), aligned_size); - - int* last_allocation_size = &alloc_sizes_.back(); - alloc_end_ -= (*last_allocation_size - aligned_size); - *last_allocation_size = aligned_size; -} - -void ResourceBuffer::RecycleLeastRecentlyAllocated() { - CHECK(!alloc_sizes_.empty()); - int allocation_size = alloc_sizes_.front(); - alloc_sizes_.pop(); - - alloc_start_ += allocation_size; - CHECK(alloc_start_ <= buf_size_); - - if (alloc_start_ == alloc_end_) { - CHECK(alloc_sizes_.empty()); - alloc_start_ = -1; - alloc_end_ = -1; - } else if (alloc_start_ == buf_size_) { - CHECK(!alloc_sizes_.empty()); - alloc_start_ = 0; - } -} - -} // namespace content diff --git a/chromium/content/browser/loader/resource_buffer.h b/chromium/content/browser/loader/resource_buffer.h deleted file mode 100644 index 85354c4a1c7..00000000000 --- a/chromium/content/browser/loader/resource_buffer.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_RESOURCE_BUFFER_H_ -#define CONTENT_BROWSER_LOADER_RESOURCE_BUFFER_H_ - -#include "base/containers/queue.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/shared_memory.h" -#include "content/common/content_export.h" - -namespace content { - -// ResourceBuffer implements a simple "circular buffer" allocation strategy. -// Allocations are recycled in FIFO order. -// -// You can think of the ResourceBuffer as a FIFO. The Allocate method reserves -// space in the buffer. Allocate may be called multiple times until the buffer -// is fully reserved (at which point CanAllocate returns false). Allocations -// are freed in FIFO order via a call to RecycleLeastRecentlyAllocated. -// -// ResourceBuffer is reference-counted for the benefit of consumers, who need -// to ensure that ResourceBuffer stays alive while they are using its memory. -// -// EXAMPLE USAGE: -// -// // Writes data into the ResourceBuffer, and returns the location (byte -// // offset and count) of the bytes written into the ResourceBuffer's shared -// // memory buffer. -// void WriteToBuffer(ResourceBuffer* buf, int* offset, int* count) { -// DCHECK(buf->CanAllocate()); -// -// *offset = -1; -// *count = 0; -// -// int size; -// char* ptr = buf->Allocate(&size); -// if (!ptr) { /* handle error */ } -// -// int bytes_read = static_cast<int>(fread(ptr, 1, size, file_pointer_)); -// if (!bytes_read) { /* handle error */ } -// -// if (bytes_read < size) -// buf->ShrinkLastAllocation(bytes_read); -// -// *offset = buf->GetLastAllocationOffset(); -// *count = bytes_read; -// } -// -// NOTE: As the above example illustrates, the ResourceBuffer keeps track of -// the last allocation made. Calling ShrinkLastAllocation is optional, as it -// just helps the ResourceBuffer optimize storage and be more aggressive about -// returning larger allocations from the Allocate method. -// -class CONTENT_EXPORT ResourceBuffer - : public base::RefCountedThreadSafe<ResourceBuffer> { - public: - ResourceBuffer(); - - // Initialize the shared memory buffer. It will be buffer_size bytes in - // length. The min/max_allocation_size parameters control the behavior of - // the Allocate method. It will prefer to return segments that are - // max_allocation_size in length, but will return segments less than that if - // space is limited. It will not return allocations smaller than - // min_allocation_size. - bool Initialize(int buffer_size, - int min_allocation_size, - int max_allocation_size); - bool IsInitialized() const; - - // Returns a reference to the underlying shared memory. - base::SharedMemory& GetSharedMemory(); - - // Returns true if Allocate will succeed. - bool CanAllocate() const; - - // Returns a pointer into the shared memory buffer or NULL if the buffer is - // already fully allocated. The returned size will be max_allocation_size - // unless the buffer is close to being full. - char* Allocate(int* size); - - // Returns the offset into the shared memory buffer where the last allocation - // returned by Allocate can be found. - int GetLastAllocationOffset() const; - - // Called to reduce the size of the last allocation returned by Allocate. It - // is OK for new_size to match the current size of the last allocation. - void ShrinkLastAllocation(int new_size); - - // Called to allow reuse of memory that was previously allocated. See notes - // above the class for more details about this method. - void RecycleLeastRecentlyAllocated(); - - private: - friend class base::RefCountedThreadSafe<ResourceBuffer>; - ~ResourceBuffer(); - - base::SharedMemory shared_mem_; - - int buf_size_; - int min_alloc_size_; - int max_alloc_size_; - - // These point to the range of the shared memory that is currently allocated. - // If alloc_start_ is -1, then the range is empty and nothing is allocated. - // Otherwise, alloc_start_ points to the start of the allocated range, and - // alloc_end_ points just beyond the end of the previous allocation. In the - // wraparound case, alloc_end_ <= alloc_start_. See resource_buffer.cc for - // more details about these members. - int alloc_start_; - int alloc_end_; - - base::queue<int> alloc_sizes_; - - DISALLOW_COPY_AND_ASSIGN(ResourceBuffer); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_RESOURCE_BUFFER_H_ diff --git a/chromium/content/browser/loader/resource_buffer_unittest.cc b/chromium/content/browser/loader/resource_buffer_unittest.cc deleted file mode 100644 index a9e90431544..00000000000 --- a/chromium/content/browser/loader/resource_buffer_unittest.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/resource_buffer.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -TEST(ResourceBufferTest, BasicAllocations) { - scoped_refptr<ResourceBuffer> buf = new ResourceBuffer(); - EXPECT_TRUE(buf->Initialize(100, 5, 10)); - EXPECT_TRUE(buf->CanAllocate()); - - // First allocation - { - int size; - char* ptr = buf->Allocate(&size); - EXPECT_TRUE(ptr); - EXPECT_EQ(10, size); - EXPECT_TRUE(buf->CanAllocate()); - - EXPECT_EQ(0, buf->GetLastAllocationOffset()); - - buf->ShrinkLastAllocation(2); // Less than our min allocation size. - EXPECT_EQ(0, buf->GetLastAllocationOffset()); - EXPECT_TRUE(buf->CanAllocate()); - } - - // Second allocation - { - int size; - char* ptr = buf->Allocate(&size); - EXPECT_TRUE(ptr); - EXPECT_EQ(10, size); - EXPECT_TRUE(buf->CanAllocate()); - - EXPECT_EQ(5, buf->GetLastAllocationOffset()); - - buf->ShrinkLastAllocation(4); - EXPECT_EQ(5, buf->GetLastAllocationOffset()); - - EXPECT_TRUE(buf->CanAllocate()); - } -} - -TEST(ResourceBufferTest, AllocateAndRecycle) { - scoped_refptr<ResourceBuffer> buf = new ResourceBuffer(); - EXPECT_TRUE(buf->Initialize(100, 5, 10)); - - int size; - - buf->Allocate(&size); - EXPECT_EQ(0, buf->GetLastAllocationOffset()); - - buf->RecycleLeastRecentlyAllocated(); - - // Offset should again be 0. - buf->Allocate(&size); - EXPECT_EQ(0, buf->GetLastAllocationOffset()); -} - -TEST(ResourceBufferTest, WrapAround) { - scoped_refptr<ResourceBuffer> buf = new ResourceBuffer(); - EXPECT_TRUE(buf->Initialize(20, 10, 10)); - - int size; - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - // Create hole at the beginnning. Next allocation should go there. - buf->RecycleLeastRecentlyAllocated(); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - EXPECT_EQ(0, buf->GetLastAllocationOffset()); -} - -TEST(ResourceBufferTest, WrapAround2) { - scoped_refptr<ResourceBuffer> buf = new ResourceBuffer(); - EXPECT_TRUE(buf->Initialize(30, 10, 10)); - - int size; - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - EXPECT_FALSE(buf->CanAllocate()); - - // Create holes at first and second slots. - buf->RecycleLeastRecentlyAllocated(); - buf->RecycleLeastRecentlyAllocated(); - - EXPECT_TRUE(buf->CanAllocate()); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - EXPECT_EQ(0, buf->GetLastAllocationOffset()); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - EXPECT_EQ(10, buf->GetLastAllocationOffset()); - - EXPECT_FALSE(buf->CanAllocate()); -} - -TEST(ResourceBufferTest, Full) { - scoped_refptr<ResourceBuffer> buf = new ResourceBuffer(); - EXPECT_TRUE(buf->Initialize(20, 10, 10)); - - int size; - buf->Allocate(&size); - EXPECT_EQ(10, size); - - buf->Allocate(&size); - EXPECT_EQ(10, size); - - // Full. - EXPECT_FALSE(buf->CanAllocate()); - - // Still full, even if there is a small hole at the end. - buf->ShrinkLastAllocation(5); - EXPECT_FALSE(buf->CanAllocate()); -} - -} // namespace content diff --git a/chromium/content/browser/loader/resource_controller.h b/chromium/content/browser/loader/resource_controller.h index 82785346fef..55ecd8c28a5 100644 --- a/chromium/content/browser/loader/resource_controller.h +++ b/chromium/content/browser/loader/resource_controller.h @@ -5,8 +5,13 @@ #ifndef CONTENT_BROWSER_LOADER_RESOURCE_CONTROLLER_H_ #define CONTENT_BROWSER_LOADER_RESOURCE_CONTROLLER_H_ +#include "base/optional.h" #include "content/common/content_export.h" +namespace net { +class HttpRequestHeaders; +}; + namespace content { // Used to either resume a deferred resource load or cancel a resource load at @@ -25,6 +30,12 @@ class CONTENT_EXPORT ResourceController { // deferred. Guaranteed not to call back into the ResourceHandler, or destroy // it, synchronously. virtual void Resume() = 0; + + // Similar to |Resume()| but can only be called if the request was previously + // redirected. |modified_request_headers| are changes applied to the request + // headers after updating them for the redirect. + virtual void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) = 0; }; } // namespace content diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc index 1c1fb59e02c..061ad189c46 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc @@ -47,7 +47,6 @@ #include "content/browser/loader/mime_sniffing_resource_handler.h" #include "content/browser/loader/mojo_async_resource_handler.h" #include "content/browser/loader/null_resource_controller.h" -#include "content/browser/loader/redirect_to_file_resource_handler.h" #include "content/browser/loader/resource_loader.h" #include "content/browser/loader/resource_message_filter.h" #include "content/browser/loader/resource_request_info_impl.h" @@ -55,7 +54,6 @@ #include "content/browser/loader/stream_resource_handler.h" #include "content/browser/loader/throttling_resource_handler.h" #include "content/browser/loader/upload_data_stream_builder.h" -#include "content/browser/loader/wake_lock_resource_throttle.h" #include "content/browser/resource_context_impl.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_navigation_handle_core.h" @@ -112,11 +110,11 @@ #include "services/network/public/cpp/url_loader_completion_status.h" #include "services/network/public/mojom/request_context_frame_type.mojom.h" #include "services/network/resource_scheduler.h" +#include "services/network/throttling/scoped_throttling_token.h" #include "services/network/url_loader_factory.h" #include "storage/browser/blob/blob_data_handle.h" #include "storage/browser/blob/blob_storage_context.h" #include "storage/browser/blob/blob_url_request_job_factory.h" -#include "storage/browser/blob/shareable_file_reference.h" #include "storage/browser/fileapi/file_permission_policy.h" #include "storage/browser/fileapi/file_system_context.h" #include "url/third_party/mozilla/url_parse.h" @@ -171,12 +169,6 @@ void AbortRequestBeforeItStarts( url_loader_client->OnComplete(status); } -void RemoveDownloadFileFromChildSecurityPolicy(int child_id, - const base::FilePath& path) { - ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile( - child_id, path); -} - bool IsValidatedSCT( const net::SignedCertificateTimestampAndStatus& sct_status) { return sct_status.status == net::ct::SCT_STATUS_OK; @@ -193,6 +185,7 @@ PreviewsState DetermineEnabledPreviews(PreviewsState previews_to_allow, net::URLRequest* request, ResourceContext* resource_context, bool is_main_frame) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); // If previews have already been turned off, or we are inheriting values on a // sub-frame, don't check any further. if (previews_to_allow & PREVIEWS_OFF || @@ -324,8 +317,6 @@ ResourceDispatcherHostImpl::ResourceDispatcherHostImpl( max_num_in_flight_requests_ * kMaxRequestsPerProcessRatio)), max_outstanding_requests_cost_per_process_( kMaxOutstandingRequestsCostPerProcess), - largest_outstanding_request_count_seen_(0), - largest_outstanding_request_per_process_count_seen_(0), delegate_(nullptr), loader_delegate_(nullptr), allow_cross_origin_auth_prompt_(false), @@ -557,7 +548,8 @@ scoped_refptr<LoginDelegate> ResourceDispatcherHostImpl::CreateLoginDelegate( scoped_refptr<LoginDelegate> login_delegate = GetContentClient()->browser()->CreateLoginDelegate( auth_info, resource_request_info->GetWebContentsGetterForRequest(), - is_request_for_main_frame, url, + request_id, is_request_for_main_frame, url, + request->response_headers(), resource_request_info->first_auth_attempt(), base::BindOnce(&ResourceDispatcherHostImpl::RunAuthRequiredCallback, base::Unretained(this), request_id)); @@ -606,13 +598,6 @@ void ResourceDispatcherHostImpl::DidReceiveResponse( network::ResourceResponse* response) { ResourceRequestInfoImpl* info = loader->GetRequestInfo(); net::URLRequest* request = loader->request(); - if (request->was_fetched_via_proxy() && - request->was_fetched_via_spdy() && - request->url().SchemeIs(url::kHttpScheme)) { - scheduler_->OnReceivedSpdyProxiedHttpResponse( - info->GetChildID(), info->GetRouteID()); - } - if (delegate_) delegate_->OnResponseStarted(request, info->GetContext(), response); } @@ -620,39 +605,9 @@ void ResourceDispatcherHostImpl::DidReceiveResponse( void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) { ResourceRequestInfoImpl* info = loader->GetRequestInfo(); - base::TimeDelta request_loading_time(base::TimeTicks::Now() - - loader->request()->creation_time()); - // Record final result of all resource loads. if (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME) { - // This enumeration has "3" appended to its name to distinguish it from - // older versions. - base::UmaHistogramSparse("Net.ErrorCodesForMainFrame3", - -loader->request()->status().error()); - if (loader->request()->status().error() == net::OK) { - UMA_HISTOGRAM_LONG_TIMES("Net.RequestTime2Success.MainFrame", - request_loading_time); - } - if (loader->request()->status().error() == net::ERR_ABORTED) { - UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.SentBytes", - loader->request()->GetTotalSentBytes(), 1, - 50000000, 50); - UMA_HISTOGRAM_CUSTOM_COUNTS("Net.ErrAborted.ReceivedBytes", - loader->request()->GetTotalReceivedBytes(), 1, - 50000000, 50); - } - if (loader->request()->url().SchemeIsCryptographic()) { - if (loader->request()->url().host_piece() == "www.google.com") { - base::UmaHistogramSparse("Net.ErrorCodesForHTTPSGoogleMainFrame2", - -loader->request()->status().error()); - } - - if (net::IsTLS13ExperimentHost(loader->request()->url().host_piece())) { - base::UmaHistogramSparse("Net.ErrorCodesForTLS13ExperimentMainFrame", - -loader->request()->status().error()); - } - int num_valid_scts = std::count_if( loader->request()->ssl_info().signed_certificate_timestamps.begin(), loader->request()->ssl_info().signed_certificate_timestamps.end(), @@ -660,18 +615,6 @@ void ResourceDispatcherHostImpl::DidFinishLoading(ResourceLoader* loader) { UMA_HISTOGRAM_COUNTS_100( "Net.CertificateTransparency.MainFrameValidSCTCount", num_valid_scts); } - } else { - if (loader->request()->status().error() == net::OK) { - UMA_HISTOGRAM_LONG_TIMES("Net.RequestTime2Success.Subresource", - request_loading_time); - } - if (info->GetResourceType() == RESOURCE_TYPE_IMAGE) { - base::UmaHistogramSparse("Net.ErrorCodesForImages", - -loader->request()->status().error()); - } - // This enumeration has "2" appended to distinguish it from older versions. - base::UmaHistogramSparse("Net.ErrorCodesForSubresources2", - -loader->request()->status().error()); } if (delegate_) @@ -725,6 +668,7 @@ void ResourceDispatcherHostImpl::OnRequestResourceInternal( int request_id, bool is_sync_load, const network::ResourceRequest& request_data, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, const net::NetworkTrafficAnnotationTag& traffic_annotation) { @@ -732,7 +676,7 @@ void ResourceDispatcherHostImpl::OnRequestResourceInternal( requester_info->IsNavigationPreload() || requester_info->IsCertificateFetcherForSignedExchange()); BeginRequest(requester_info, request_id, request_data, is_sync_load, - routing_id, std::move(mojo_request), + routing_id, url_loader_options, std::move(mojo_request), std::move(url_loader_client), traffic_annotation); } @@ -756,6 +700,7 @@ void ResourceDispatcherHostImpl::BeginRequest( const network::ResourceRequest& request_data, bool is_sync_load, int route_id, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, const net::NetworkTrafficAnnotationTag& traffic_annotation) { @@ -862,7 +807,8 @@ void ResourceDispatcherHostImpl::BeginRequest( &ResourceDispatcherHostImpl::ContinuePendingBeginRequest, base::Unretained(this), base::WrapRefCounted(requester_info), request_id, request_data, is_sync_load, route_id, - request_data.headers, base::Passed(std::move(mojo_request)), + request_data.headers, url_loader_options, + base::Passed(std::move(mojo_request)), base::Passed(std::move(url_loader_client)), base::Passed(std::move(blob_handles)), traffic_annotation)); return; @@ -871,7 +817,7 @@ void ResourceDispatcherHostImpl::BeginRequest( } ContinuePendingBeginRequest( requester_info, request_id, request_data, is_sync_load, route_id, - request_data.headers, std::move(mojo_request), + request_data.headers, url_loader_options, std::move(mojo_request), std::move(url_loader_client), std::move(blob_handles), traffic_annotation, HeaderInterceptorResult::CONTINUE); } @@ -883,6 +829,7 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( bool is_sync_load, int route_id, const net::HttpRequestHeaders& headers, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, BlobHandles blob_handles, @@ -948,6 +895,7 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( new_request->set_site_for_cookies(request_data.site_for_cookies); new_request->set_attach_same_site_cookies( request_data.attach_same_site_cookies); + new_request->set_upgrade_if_insecure(request_data.upgrade_if_insecure); // The initiator should normally be present, unless this is a navigation. // Browser-initiated navigations don't have an initiator document, the @@ -967,6 +915,11 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( new_request->SetExtraRequestHeaders(headers); + std::unique_ptr<network::ScopedThrottlingToken> throttling_token = + network::ScopedThrottlingToken::MaybeCreate( + new_request->net_log().source().id, + request_data.throttling_profile_id); + blob_context = GetBlobStorageContext(requester_info->blob_storage_context()); // Resolve elements from request_body and prepare upload data. if (request_data.request_body.get()) { @@ -1096,7 +1049,8 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( std::unique_ptr<ResourceHandler> handler = CreateResourceHandler( requester_info.get(), new_request.get(), request_data, route_id, child_id, - resource_context, std::move(mojo_request), std::move(url_loader_client)); + resource_context, url_loader_options, std::move(mojo_request), + std::move(url_loader_client)); if (handler) { RecordFetchRequestMode(request_data.url, request_data.method, @@ -1104,7 +1058,8 @@ void ResourceDispatcherHostImpl::ContinuePendingBeginRequest( const bool is_initiated_by_fetch_api = request_data.fetch_request_context_type == REQUEST_CONTEXT_TYPE_FETCH; BeginRequestInternal(std::move(new_request), std::move(handler), - is_initiated_by_fetch_api); + is_initiated_by_fetch_api, + std::move(throttling_token)); } } @@ -1116,6 +1071,7 @@ ResourceDispatcherHostImpl::CreateResourceHandler( int route_id, int child_id, ResourceContext* resource_context, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client) { DCHECK(requester_info->IsRenderer() || @@ -1124,15 +1080,10 @@ ResourceDispatcherHostImpl::CreateResourceHandler( // Construct the IPC resource handler. std::unique_ptr<ResourceHandler> handler; handler = CreateBaseResourceHandler( - request, std::move(mojo_request), std::move(url_loader_client), + request, url_loader_options, std::move(mojo_request), + std::move(url_loader_client), static_cast<ResourceType>(request_data.resource_type)); - // The RedirectToFileResourceHandler depends on being next in the chain. - if (request_data.download_to_file) { - handler.reset( - new RedirectToFileResourceHandler(std::move(handler), request)); - } - // Prefetches outlive their child process. if (request_data.resource_type == RESOURCE_TYPE_PREFETCH) { auto detachable_handler = std::make_unique<DetachableResourceHandler>( @@ -1153,13 +1104,14 @@ ResourceDispatcherHostImpl::CreateResourceHandler( std::unique_ptr<ResourceHandler> ResourceDispatcherHostImpl::CreateBaseResourceHandler( net::URLRequest* request, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, ResourceType resource_type) { std::unique_ptr<ResourceHandler> handler; handler.reset(new MojoAsyncResourceHandler( request, this, std::move(mojo_request), std::move(url_loader_client), - resource_type, network::mojom::kURLLoadOptionNone)); + resource_type, url_loader_options)); return handler; } @@ -1191,12 +1143,6 @@ ResourceDispatcherHostImpl::AddStandardHandlers( &throttles); } - if (request->has_upload()) { - // Request wake lock while uploading data. - throttles.push_back( - std::make_unique<WakeLockResourceThrottle>(request->url().host())); - } - // The Clear-Site-Data throttle. std::unique_ptr<ResourceThrottle> clear_site_data_throttle = ClearSiteDataThrottle::MaybeCreateThrottleForRequest(request); @@ -1255,41 +1201,6 @@ ResourceDispatcherHostImpl::AddStandardHandlers( return handler; } -void ResourceDispatcherHostImpl::RegisterDownloadedTempFile( - int child_id, int request_id, const base::FilePath& file_path) { - scoped_refptr<ShareableFileReference> reference = - ShareableFileReference::Get(file_path); - DCHECK(reference.get()); - - registered_temp_files_[child_id][request_id] = reference; - ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( - child_id, reference->path()); - - // When the temp file is deleted, revoke permissions that the renderer has - // to that file. This covers an edge case where the file is deleted and then - // the same name is re-used for some other purpose, we don't want the old - // renderer to still have access to it. - // - // We do this when the file is deleted because the renderer can take a blob - // reference to the temp file that outlives the url loaded that it was - // loaded with to keep the file (and permissions) alive. - reference->AddFinalReleaseCallback( - base::BindOnce(&RemoveDownloadFileFromChildSecurityPolicy, child_id)); -} - -void ResourceDispatcherHostImpl::UnregisterDownloadedTempFile( - int child_id, int request_id) { - DeletableFilesMap& map = registered_temp_files_[child_id]; - DeletableFilesMap::iterator found = map.find(request_id); - if (found == map.end()) - return; - - map.erase(found); - - // Note that we don't remove the security bits here. This will be done - // when all file refs are deleted (see RegisterDownloadedTempFile). -} - ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( int child_id, int render_view_route_id, @@ -1353,7 +1264,6 @@ void ResourceDispatcherHostImpl::CancelRequestsForProcess(int child_id) { const auto& map = keepalive_statistics_recorder_.per_process_records(); if (map.find(child_id) != map.end()) keepalive_statistics_recorder_.Unregister(child_id); - registered_temp_files_.erase(child_id); } void ResourceDispatcherHostImpl::CancelRequestsForRoute( @@ -1523,21 +1433,6 @@ ResourceDispatcherHostImpl::IncrementOutstandingRequestsCount( DCHECK_GE(stats.num_requests, 0); UpdateOutstandingRequestsStats(*info, stats); - if (num_in_flight_requests_ > largest_outstanding_request_count_seen_) { - largest_outstanding_request_count_seen_ = num_in_flight_requests_; - UMA_HISTOGRAM_COUNTS_1M( - "Net.ResourceDispatcherHost.OutstandingRequests.Total", - largest_outstanding_request_count_seen_); - } - - if (stats.num_requests > - largest_outstanding_request_per_process_count_seen_) { - largest_outstanding_request_per_process_count_seen_ = stats.num_requests; - UMA_HISTOGRAM_COUNTS_1M( - "Net.ResourceDispatcherHost.OutstandingRequests.PerProcess", - largest_outstanding_request_per_process_count_seen_); - } - return stats; } @@ -1571,11 +1466,7 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( ServiceWorkerNavigationHandleCore* service_worker_handle_core, AppCacheNavigationHandleCore* appcache_handle_core, uint32_t url_loader_options, - GlobalRequestID* global_request_id) { - // PlzNavigate: BeginNavigationRequest currently should only be used for the - // browser-side navigations project. - CHECK(IsBrowserSideNavigationEnabled()); - + const GlobalRequestID& global_request_id) { DCHECK(url_loader_client.is_bound()); DCHECK(url_loader_request.is_pending()); @@ -1623,11 +1514,16 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( new_request->set_method(info.common_params.method); new_request->set_site_for_cookies(info.site_for_cookies); new_request->set_initiator(info.begin_params->initiator_origin); + new_request->set_upgrade_if_insecure(info.upgrade_if_insecure); if (info.is_main_frame) { new_request->set_first_party_url_policy( net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); } + std::unique_ptr<network::ScopedThrottlingToken> throttling_token = + network::ScopedThrottlingToken::MaybeCreate( + new_request->net_log().source().id, info.devtools_frame_token); + Referrer::SetReferrerForRequest(new_request.get(), info.common_params.referrer); @@ -1684,7 +1580,7 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( -1, // route_id info.frame_tree_node_id, ChildProcessHost::kInvalidUniqueID, // plugin_child_id - MakeRequestID(), + global_request_id.request_id, -1, // request_data.render_frame_id, info.is_main_frame, resource_type, info.common_params.transition, false, // is download @@ -1713,8 +1609,6 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( // Request takes ownership. extra_info->AssociateWithRequest(new_request.get()); - *global_request_id = extra_info->GetGlobalRequestID(); - if (new_request->url().SchemeIs(url::kBlobScheme)) { // Hang on to a reference to ensure the blob is not released prior // to the job being started. @@ -1760,7 +1654,8 @@ void ResourceDispatcherHostImpl::BeginNavigationRequest( RecordFetchRequestMode(new_request->url(), new_request->method(), network::mojom::FetchRequestMode::kNavigate); BeginRequestInternal(std::move(new_request), std::move(handler), - false /* is_initiated_by_fetch_api */); + false /* is_initiated_by_fetch_api */, + std::move(throttling_token)); } void ResourceDispatcherHostImpl::SetLoaderDelegate( @@ -1782,8 +1677,6 @@ void ResourceDispatcherHostImpl::OnRequestResourceWithMojo( network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, const net::NetworkTrafficAnnotationTag& traffic_annotation) { - DCHECK_EQ(network::mojom::kURLLoadOptionNone, - options & ~network::mojom::kURLLoadOptionSynchronous); if (!url_loader_client) { VLOG(1) << "Killed renderer for null client"; bad_message::ReceivedBadMessage(requester_info->filter(), @@ -1792,7 +1685,8 @@ void ResourceDispatcherHostImpl::OnRequestResourceWithMojo( } bool is_sync_load = options & network::mojom::kURLLoadOptionSynchronous; OnRequestResourceInternal(requester_info, routing_id, request_id, - is_sync_load, request, std::move(mojo_request), + is_sync_load, request, options, + std::move(mojo_request), std::move(url_loader_client), traffic_annotation); } @@ -1819,7 +1713,8 @@ int ResourceDispatcherHostImpl::CalculateApproximateMemoryCost( void ResourceDispatcherHostImpl::BeginRequestInternal( std::unique_ptr<net::URLRequest> request, std::unique_ptr<ResourceHandler> handler, - bool is_initiated_by_fetch_api) { + bool is_initiated_by_fetch_api, + std::unique_ptr<network::ScopedThrottlingToken> throttling_token) { DCHECK(!request->is_pending()); ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request.get()); @@ -1895,8 +1790,9 @@ void ResourceDispatcherHostImpl::BeginRequestInternal( } ResourceContext* resource_context = info->GetContext(); - std::unique_ptr<ResourceLoader> loader(new ResourceLoader( - std::move(request), std::move(handler), this, resource_context)); + std::unique_ptr<ResourceLoader> loader( + new ResourceLoader(std::move(request), std::move(handler), this, + resource_context, std::move(throttling_token))); GlobalFrameRoutingId id(info->GetChildID(), info->GetRenderFrameID()); BlockedLoadersMap::const_iterator iter = blocked_loaders_map_.find(id); @@ -1966,7 +1862,8 @@ void ResourceDispatcherHostImpl::BeginURLRequest( true /* force_download */, true /* is_new_request */); } BeginRequestInternal(std::move(request), std::move(handler), - false /* is_initiated_by_fetch_api */); + false /* is_initiated_by_fetch_api */, + nullptr /* throttling_token */); } int ResourceDispatcherHostImpl::MakeRequestID() { @@ -1974,6 +1871,10 @@ int ResourceDispatcherHostImpl::MakeRequestID() { return --request_id_; } +GlobalRequestID ResourceDispatcherHostImpl::MakeGlobalRequestID() { + return GlobalRequestID(ChildProcessHost::kInvalidUniqueID, MakeRequestID()); +} + void ResourceDispatcherHostImpl::CancelRequestFromRenderer( GlobalRequestID request_id) { ResourceLoader* loader = GetLoader(request_id); diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.h b/chromium/content/browser/loader/resource_dispatcher_host_impl.h index 388f7f84a00..b8a81809fff 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.h +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.h @@ -47,7 +47,6 @@ #include "url/gurl.h" namespace base { -class FilePath; class OneShotTimer; } @@ -59,11 +58,11 @@ class URLRequestContextGetter; namespace network { class ResourceScheduler; +class ScopedThrottlingToken; } // namespace network namespace storage { class FileSystemContext; -class ShareableFileReference; } namespace content { @@ -182,15 +181,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl void CancelBlockedRequestsForRoute( const GlobalFrameRoutingId& global_routing_id); - // Maintains a collection of temp files created in support of - // the download_to_file capability. Used to grant access to the - // child process and to defer deletion of the file until it's - // no longer needed. - void RegisterDownloadedTempFile( - int child_id, int request_id, - const base::FilePath& file_path); - void UnregisterDownloadedTempFile(int child_id, int request_id); - // Indicates whether third-party sub-content can pop-up HTTP basic auth // dialog boxes. bool allow_cross_origin_auth_prompt(); @@ -240,9 +230,8 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl void FinishedWithResourcesForRequest(net::URLRequest* request); // PlzNavigate: Begins a request for NavigationURLLoader. |loader| is the - // loader to attach to the leaf resource handler. - // After calling this function, |global_request_id| will contains the - // request's global id. + // loader to attach to the leaf resource handler. |global_request_id| needs to + // be created by MakeGlobalRequestID() before calling this method. void BeginNavigationRequest( ResourceContext* resource_context, net::URLRequestContext* request_context, @@ -254,7 +243,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl ServiceWorkerNavigationHandleCore* service_worker_handle_core, AppCacheNavigationHandleCore* appcache_handle_core, uint32_t url_loader_options, - GlobalRequestID* global_request_id); + const GlobalRequestID& global_request_id); int num_in_flight_requests_for_testing() const { return num_in_flight_requests_; @@ -311,6 +300,11 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl // of |request_id_| for the details. Must be called on the IO thread. int MakeRequestID(); + // Creates a new global request ID for browser initiated requests. The ID + // is consistent with the request id created by MakeRequestID(). Must be + // called on the IO thread. + GlobalRequestID MakeGlobalRequestID(); + // Cancels a request as requested by a renderer. This function is called when // a mojo connection is lost. // Note that this cancel is subtly different from the other CancelRequest @@ -420,9 +414,11 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl void OnShutdown(); // Helper function for URL requests. - void BeginRequestInternal(std::unique_ptr<net::URLRequest> request, - std::unique_ptr<ResourceHandler> handler, - bool is_initiated_by_fetch_api); + void BeginRequestInternal( + std::unique_ptr<net::URLRequest> request, + std::unique_ptr<ResourceHandler> handler, + bool is_initiated_by_fetch_api, + std::unique_ptr<network::ScopedThrottlingToken> throttling_token); void StartLoading(ResourceRequestInfoImpl* info, std::unique_ptr<ResourceLoader> loader); @@ -539,6 +535,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl int request_id, bool is_sync_load, const network::ResourceRequest& request_data, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, const net::NetworkTrafficAnnotationTag& traffic_annotation); @@ -550,6 +547,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl const network::ResourceRequest& request_data, bool is_sync_load, int route_id, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, const net::NetworkTrafficAnnotationTag& traffic_annotation); @@ -569,6 +567,7 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl bool is_sync_load, int route_id, const net::HttpRequestHeaders& headers, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, BlobHandles blob_handles, @@ -584,12 +583,14 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl int route_id, int child_id, ResourceContext* resource_context, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client); // Creates either MojoAsyncResourceHandler or AsyncResourceHandler. std::unique_ptr<ResourceHandler> CreateBaseResourceHandler( net::URLRequest* request, + uint32_t url_loader_options, network::mojom::URLLoaderRequest mojo_request, network::mojom::URLLoaderClientPtr url_loader_client, ResourceType resource_type); @@ -667,15 +668,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl LoaderMap pending_loaders_; - // Collection of temp files downloaded for child processes via - // the download_to_file mechanism. We avoid deleting them until - // the client no longer needs them. - typedef std::map<int, scoped_refptr<storage::ShareableFileReference> > - DeletableFilesMap; // key is request id - typedef std::map<int, DeletableFilesMap> - RegisteredTempFiles; // key is child process id - RegisteredTempFiles registered_temp_files_; - // A timer that periodically calls UpdateLoadInfo while |pending_loaders_| is // not empty, at least one RenderViewHost is loading, and not waiting on an // ACK from the UI thread for the last sent LoadInfoList. @@ -733,12 +725,6 @@ class CONTENT_EXPORT ResourceDispatcherHostImpl // kAvgBytesPerOutstandingRequest) int max_outstanding_requests_cost_per_process_; - // Largest number of outstanding requests seen so far across all processes. - int largest_outstanding_request_count_seen_; - - // Largest number of outstanding requests seen so far in any single process. - int largest_outstanding_request_per_process_count_seen_; - // Time of the last user gesture. Stored so that we can add a load // flag to requests occurring soon after a gesture to indicate they // may be because of explicit user action. diff --git a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc index e576ad75157..d8d744e64ff 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_unittest.cc @@ -27,7 +27,6 @@ #include "content/browser/download/download_resource_handler.h" #include "content/browser/frame_host/navigation_request_info.h" #include "content/browser/loader/detachable_resource_handler.h" -#include "content/browser/loader/downloaded_temp_file_impl.h" #include "content/browser/loader/navigation_url_loader.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_loader.h" @@ -78,6 +77,8 @@ #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" #include "services/network/public/cpp/resource_request.h" +#include "services/network/resource_scheduler.h" +#include "services/network/resource_scheduler_params_manager.h" #include "services/network/test/test_url_loader_client.h" #include "storage/browser/blob/shareable_file_reference.h" #include "testing/gtest/include/gtest/gtest.h" @@ -104,7 +105,6 @@ static network::ResourceRequest CreateResourceRequest(const char* method, request.plugin_child_id = -1; request.resource_type = type; request.appcache_host_id = kAppCacheNoHostId; - request.download_to_file = false; request.should_reset_appcache = false; request.is_main_frame = true; request.transition_type = ui::PAGE_TRANSITION_LINK; @@ -818,7 +818,9 @@ class ResourceDispatcherHostTest : public testing::Test { std::unique_ptr<NavigationRequestInfo> request_info( new NavigationRequestInfo(common_params, std::move(begin_params), url, true, false, false, -1, false, false, false, - nullptr, base::UnguessableToken::Create())); + false, nullptr, + base::UnguessableToken::Create(), + base::UnguessableToken::Create())); std::unique_ptr<NavigationURLLoader> test_loader = NavigationURLLoader::Create( browser_context_->GetResourceContext(), @@ -838,6 +840,18 @@ class ResourceDispatcherHostTest : public testing::Test { request_info->detachable_handler()->is_detached(); } + void SetMaxDelayableRequests(size_t max_delayable_requests) { + network::ResourceSchedulerParamsManager::ParamsForNetworkQualityContainer c; + for (int i = 0; i != net::EFFECTIVE_CONNECTION_TYPE_LAST; ++i) { + auto type = static_cast<net::EffectiveConnectionType>(i); + c[type] = + network::ResourceSchedulerParamsManager::ParamsForNetworkQuality( + max_delayable_requests, 0.0, false); + } + host_.scheduler()->SetResourceSchedulerParamsManagerForTests( + network::ResourceSchedulerParamsManager(c)); + } + content::TestBrowserThreadBundle thread_bundle_; std::unique_ptr<TestBrowserContext> browser_context_; std::unique_ptr<TestURLRequestJobFactory> job_factory_; @@ -1467,6 +1481,7 @@ TEST_F(ResourceDispatcherHostTest, CancelRequestsOnRenderFrameDeleted) { host_.SetDelegate(&delegate); host_.OnRenderViewHostCreated(filter_->child_id(), 0, request_context_getter_.get()); + SetMaxDelayableRequests(1); // One RenderView issues a high priority request and a low priority one. Both // should be started. @@ -2208,138 +2223,6 @@ TEST_F(ResourceDispatcherHostTest, DataSentBeforeDetach) { EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); } -// Tests the dispatcher host's temporary file management in the mojo-enabled -// loading. -TEST_F(ResourceDispatcherHostTest, RegisterDownloadedTempFileWithMojo) { - const int kRequestID = 1; - - // Create a temporary file. - base::FilePath file_path; - ASSERT_TRUE(base::CreateTemporaryFile(&file_path)); - EXPECT_TRUE(base::PathExists(file_path)); - scoped_refptr<ShareableFileReference> deletable_file = - ShareableFileReference::GetOrCreate( - file_path, ShareableFileReference::DELETE_ON_FINAL_RELEASE, - base::CreateSingleThreadTaskRunnerWithTraits({base::MayBlock()}) - .get()); - - // Not readable. - EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), file_path)); - - // Register it for a resource request. - auto downloaded_file = - DownloadedTempFileImpl::Create(filter_->child_id(), kRequestID); - network::mojom::DownloadedTempFilePtr downloaded_file_ptr = - DownloadedTempFileImpl::Create(filter_->child_id(), kRequestID); - host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path); - - // Should be readable now. - EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), file_path)); - - // The child releases from the request. - downloaded_file_ptr = nullptr; - content::RunAllTasksUntilIdle(); - - // Still readable because there is another reference to the file. (The child - // may take additional blob references.) - EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), file_path)); - - // Release extra references and wait for the file to be deleted. (This relies - // on the delete happening on the FILE thread which is mapped to main thread - // in this test.) - deletable_file = nullptr; - content::RunAllTasksUntilIdle(); - - // The file is no longer readable to the child and has been deleted. - EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), file_path)); - EXPECT_FALSE(base::PathExists(file_path)); -} - -// Tests that temporary files held on behalf of child processes are released -// when the child process dies. -TEST_F(ResourceDispatcherHostTest, ReleaseTemporiesOnProcessExit) { - const int kRequestID = 1; - - // Create a temporary file. - base::FilePath file_path; - ASSERT_TRUE(base::CreateTemporaryFile(&file_path)); - scoped_refptr<ShareableFileReference> deletable_file = - ShareableFileReference::GetOrCreate( - file_path, ShareableFileReference::DELETE_ON_FINAL_RELEASE, - base::CreateSingleThreadTaskRunnerWithTraits({base::MayBlock()}) - .get()); - - // Register it for a resource request. - host_.RegisterDownloadedTempFile(filter_->child_id(), kRequestID, file_path); - deletable_file = nullptr; - - // Should be readable now. - EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), file_path)); - - // Let the process die. - filter_->OnChannelClosing(); - content::RunAllTasksUntilIdle(); - - // The file is no longer readable to the child and has been deleted. - EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), file_path)); - EXPECT_FALSE(base::PathExists(file_path)); -} - -TEST_F(ResourceDispatcherHostTest, DownloadToFile) { - // Make a request which downloads to file. - network::mojom::URLLoaderPtr loader; - network::TestURLLoaderClient client; - network::ResourceRequest request = CreateResourceRequest( - "GET", RESOURCE_TYPE_SUB_RESOURCE, net::URLRequestTestJob::test_url_1()); - request.download_to_file = true; - filter_->CreateLoaderAndStart( - mojo::MakeRequest(&loader), 0, 1, network::mojom::kURLLoadOptionNone, - request, client.CreateInterfacePtr(), - net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); - content::RunAllTasksUntilIdle(); - - // The request should contain the following messages: - // ReceivedResponse (indicates headers received and filename) - // DataDownloaded* (bytes downloaded and total length) - // RequestComplete (request is done) - client.RunUntilComplete(); - EXPECT_FALSE(client.response_head().download_file_path.empty()); - EXPECT_EQ(net::URLRequestTestJob::test_data_1().size(), - static_cast<size_t>(client.download_data_length())); - EXPECT_EQ(net::OK, client.completion_status().error_code); - - // Verify that the data ended up in the temporary file. - std::string contents; - ASSERT_TRUE(base::ReadFileToString(client.response_head().download_file_path, - &contents)); - EXPECT_EQ(net::URLRequestTestJob::test_data_1(), contents); - - // The file should be readable by the child. - EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), client.response_head().download_file_path)); - - // When the renderer releases the file, it should be deleted. - // RunUntilIdle doesn't work because base::WorkerPool is involved. - ShareableFileReleaseWaiter waiter(client.response_head().download_file_path); - client.TakeDownloadedTempFile(); - content::RunAllTasksUntilIdle(); - waiter.Wait(); - // The release callback runs before the delete is scheduled, so pump the - // message loop for the delete itself. (This relies on the delete happening on - // the FILE thread which is mapped to main thread in this test.) - content::RunAllTasksUntilIdle(); - - EXPECT_FALSE(base::PathExists(client.response_head().download_file_path)); - EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( - filter_->child_id(), client.response_head().download_file_path)); -} - WebContents* WebContentsBinder(WebContents* rv) { return rv; } // Tests GetLoadInfoForAllRoutes when there are 3 requests from the same diff --git a/chromium/content/browser/loader/resource_handler.cc b/chromium/content/browser/loader/resource_handler.cc index a507567e950..c58d7641e4d 100644 --- a/chromium/content/browser/loader/resource_handler.cc +++ b/chromium/content/browser/loader/resource_handler.cc @@ -44,6 +44,11 @@ void ResourceHandler::Resume() { ReleaseController()->Resume(); } +void ResourceHandler::ResumeForRedirect( + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { + ReleaseController()->ResumeForRedirect(modified_request_headers); +} + void ResourceHandler::Cancel() { ReleaseController()->Cancel(); } diff --git a/chromium/content/browser/loader/resource_handler.h b/chromium/content/browser/loader/resource_handler.h index b9b6ebf554d..bb60dbe0d97 100644 --- a/chromium/content/browser/loader/resource_handler.h +++ b/chromium/content/browser/loader/resource_handler.h @@ -138,12 +138,6 @@ class CONTENT_EXPORT ResourceHandler { const net::URLRequestStatus& status, std::unique_ptr<ResourceController> controller) = 0; - // This notification is synthesized by the RedirectToFileResourceHandler - // to indicate progress of 'download_to_file' requests. OnReadCompleted - // calls are consumed by the RedirectToFileResourceHandler and replaced - // with OnDataDownloaded calls. - virtual void OnDataDownloaded(int bytes_downloaded) = 0; - protected: explicit ResourceHandler(net::URLRequest* request); @@ -165,6 +159,8 @@ class CONTENT_EXPORT ResourceHandler { // These call the corresponding methods on the ResourceController previously // passed to HoldController and then destroy it. void Resume(); + void ResumeForRedirect( + const base::Optional<net::HttpRequestHeaders>& modified_request_headers); void Cancel(); void CancelWithError(int error_code); diff --git a/chromium/content/browser/loader/resource_hints_impl.cc b/chromium/content/browser/loader/resource_hints_impl.cc index 6365bc19995..3ebe0570196 100644 --- a/chromium/content/browser/loader/resource_hints_impl.cc +++ b/chromium/content/browser/loader/resource_hints_impl.cc @@ -6,11 +6,10 @@ #include "base/memory/ref_counted.h" #include "base/trace_event/trace_event.h" -#include "content/browser/loader/resource_dispatcher_host_impl.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_hints.h" #include "net/base/address_list.h" #include "net/base/load_flags.h" -#include "net/dns/host_resolver.h" #include "net/http/http_network_session.h" #include "net/http/http_request_info.h" #include "net/http/http_stream_factory.h" @@ -24,18 +23,7 @@ namespace content { namespace { -class RequestHolder { - public: - std::unique_ptr<net::HostResolver::Request>* GetRequest() { - return &request_; - } - - private: - std::unique_ptr<net::HostResolver::Request> request_; -}; - -void OnResolveComplete(std::unique_ptr<RequestHolder> request_holder, - std::unique_ptr<net::AddressList> addresses, +void OnResolveComplete(std::unique_ptr<net::AddressList> addresses, const net::CompletionCallback& callback, int result) { // Plumb the resolution result into the callback if future consumers want @@ -50,9 +38,7 @@ void PreconnectUrl(net::URLRequestContextGetter* getter, const GURL& site_for_cookies, int count, bool allow_credentials) { - DCHECK(ResourceDispatcherHostImpl::Get() - ->io_thread_task_runner() - ->BelongsToCurrentThread()); + DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(getter); TRACE_EVENT2("net", "PreconnectUrl", "url", url.spec(), "count", count); @@ -74,6 +60,9 @@ void PreconnectUrl(net::URLRequestContextGetter* getter, user_agent); net::NetworkDelegate* delegate = request_context->network_delegate(); + // NetworkDelegate is not set in tests. + if (!delegate) + return; if (delegate->CanEnablePrivacyMode(url, site_for_cookies)) request_info.privacy_mode = net::PRIVACY_MODE_ENABLED; @@ -93,10 +82,9 @@ void PreconnectUrl(net::URLRequestContextGetter* getter, int PreresolveUrl(net::URLRequestContextGetter* getter, const GURL& url, - const net::CompletionCallback& callback) { - DCHECK(ResourceDispatcherHostImpl::Get() - ->io_thread_task_runner() - ->BelongsToCurrentThread()); + const net::CompletionCallback& callback, + std::unique_ptr<net::HostResolver::Request>* out_request) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(getter); TRACE_EVENT1("net", "PreresolveUrl", "url", url.spec()); @@ -104,21 +92,17 @@ int PreresolveUrl(net::URLRequestContextGetter* getter, if (!request_context) return net::ERR_CONTEXT_SHUT_DOWN; - auto request_holder = std::make_unique<RequestHolder>(); auto addresses = std::make_unique<net::AddressList>(); - // Save raw pointers before the unique_ptr is invalidated by base::Passed. + // Save raw pointers before the unique_ptr is invalidated by base::Passed(). net::AddressList* raw_addresses = addresses.get(); - std::unique_ptr<net::HostResolver::Request>* out_request = - request_holder->GetRequest(); net::HostResolver* resolver = request_context->host_resolver(); net::HostResolver::RequestInfo resolve_info(net::HostPortPair::FromURL(url)); resolve_info.set_is_speculative(true); return resolver->Resolve( resolve_info, net::IDLE, raw_addresses, - base::Bind(&OnResolveComplete, base::Passed(&request_holder), - base::Passed(&addresses), callback), + base::Bind(&OnResolveComplete, base::Passed(&addresses), callback), out_request, net::NetLogWithSource()); } diff --git a/chromium/content/browser/loader/resource_loader.cc b/chromium/content/browser/loader/resource_loader.cc index c06232582ab..2435233851b 100644 --- a/chromium/content/browser/loader/resource_loader.cc +++ b/chromium/content/browser/loader/resource_loader.cc @@ -43,6 +43,7 @@ #include "net/url_request/url_request_status.h" #include "services/network/loader_util.h" #include "services/network/public/cpp/resource_response.h" +#include "services/network/throttling/scoped_throttling_token.h" #include "url/url_constants.h" using base::TimeDelta; @@ -84,6 +85,8 @@ void PopulateResourceResponse( response->head.socket_address = response_info.socket_address; response->head.was_fetched_via_proxy = request->was_fetched_via_proxy(); response->head.network_accessed = response_info.network_accessed; + response->head.async_revalidation_requested = + response_info.async_revalidation_requested; const content::ResourceRequestInfo* request_info = content::ResourceRequestInfo::ForRequest(request); if (request_info) { @@ -152,7 +155,15 @@ class ResourceLoader::Controller : public ResourceController { // ResourceController implementation: void Resume() override { MarkAsUsed(); - resource_loader_->Resume(true /* called_from_resource_controller */); + resource_loader_->Resume(true /* called_from_resource_controller */, + base::nullopt); + } + + void ResumeForRedirect(const base::Optional<net::HttpRequestHeaders>& + modified_request_headers) override { + MarkAsUsed(); + resource_loader_->Resume(true /* called_from_resource_controller */, + modified_request_headers); } void Cancel() override { @@ -209,7 +220,8 @@ class ResourceLoader::ScopedDeferral { // If Resume() was called, it just advanced the state without doing // anything. Go ahead and resume the request now. if (old_deferred_stage == DEFERRED_NONE) - resource_loader_->Resume(false /* called_from_resource_controller */); + resource_loader_->Resume(false /* called_from_resource_controller */, + base::nullopt); } private: @@ -219,10 +231,12 @@ class ResourceLoader::ScopedDeferral { DISALLOW_COPY_AND_ASSIGN(ScopedDeferral); }; -ResourceLoader::ResourceLoader(std::unique_ptr<net::URLRequest> request, - std::unique_ptr<ResourceHandler> handler, - ResourceLoaderDelegate* delegate, - ResourceContext* resource_context) +ResourceLoader::ResourceLoader( + std::unique_ptr<net::URLRequest> request, + std::unique_ptr<ResourceHandler> handler, + ResourceLoaderDelegate* delegate, + ResourceContext* resource_context, + std::unique_ptr<network::ScopedThrottlingToken> throttling_token) : deferred_stage_(DEFERRED_NONE), request_(std::move(request)), handler_(std::move(handler)), @@ -231,6 +245,7 @@ ResourceLoader::ResourceLoader(std::unique_ptr<net::URLRequest> request, started_request_(false), times_cancelled_after_request_start_(0), resource_context_(resource_context), + throttling_token_(std::move(throttling_token)), weak_ptr_factory_(this) { request_->set_delegate(this); handler_->SetDelegate(this); @@ -515,9 +530,13 @@ void ResourceLoader::CancelCertificateSelection() { request_->CancelWithError(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); } -void ResourceLoader::Resume(bool called_from_resource_controller) { +void ResourceLoader::Resume( + bool called_from_resource_controller, + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { DeferredStage stage = deferred_stage_; deferred_stage_ = DEFERRED_NONE; + DCHECK(!modified_request_headers.has_value() || stage == DEFERRED_REDIRECT) + << "modified_request_headers can only be used with redirects"; switch (stage) { case DEFERRED_NONE: NOTREACHED(); @@ -536,7 +555,7 @@ void ResourceLoader::Resume(bool called_from_resource_controller) { // URLRequest::Start completes asynchronously, so starting the request now // won't result in synchronously calling into a ResourceHandler, if this // was called from Resume(). - FollowDeferredRedirectInternal(); + FollowDeferredRedirectInternal(modified_request_headers); break; case DEFERRED_ON_WILL_READ: // Always post a task, as synchronous resumes don't go through this @@ -611,8 +630,6 @@ void ResourceLoader::StartRequestInternal() { request_->SetResponseHeadersCallback(base::Bind( &ResourceLoader::SetRawResponseHeaders, base::Unretained(this))); } - UMA_HISTOGRAM_TIMES("Net.ResourceLoader.TimeToURLRequestStart", - base::TimeTicks::Now() - request_->creation_time()); request_->Start(); delegate_->DidStartRequest(this); @@ -664,14 +681,18 @@ void ResourceLoader::CancelRequestInternal(int error, bool from_renderer) { } } -void ResourceLoader::FollowDeferredRedirectInternal() { +void ResourceLoader::FollowDeferredRedirectInternal( + const base::Optional<net::HttpRequestHeaders>& modified_request_headers) { DCHECK(!deferred_redirect_url_.is_empty()); GURL redirect_url = deferred_redirect_url_; deferred_redirect_url_ = GURL(); if (delegate_->HandleExternalProtocol(this, redirect_url)) { + DCHECK(!modified_request_headers.has_value()) + << "ResourceLoaderDelegate::HandleExternalProtocol() with modified " + "headers was not supported yet. crbug.com/845683"; Cancel(); } else { - request_->FollowDeferredRedirect(); + request_->FollowDeferredRedirect(modified_request_headers); } } @@ -797,7 +818,6 @@ void ResourceLoader::ResponseCompleted() { TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); - RecordHistograms(); ScopedDeferral scoped_deferral(this, DEFERRED_FINISH); handler_->OnResponseCompleted(request_->status(), @@ -810,71 +830,6 @@ void ResourceLoader::CallDidFinishLoading() { delegate_->DidFinishLoading(this); } -void ResourceLoader::RecordHistograms() { - ResourceRequestInfoImpl* info = GetRequestInfo(); - if (request_->response_info().network_accessed) { - if (info->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME) { - UMA_HISTOGRAM_ENUMERATION("Net.HttpResponseInfo.ConnectionInfo.MainFrame", - request_->response_info().connection_info, - net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS); - } else { - UMA_HISTOGRAM_ENUMERATION( - "Net.HttpResponseInfo.ConnectionInfo.SubResource", - request_->response_info().connection_info, - net::HttpResponseInfo::NUM_OF_CONNECTION_INFOS); - } - } - - if (request_->load_flags() & net::LOAD_PREFETCH) { - // Note that RESOURCE_TYPE_PREFETCH requests are a subset of - // net::LOAD_PREFETCH requests. In the histograms below, "Prefetch" means - // RESOURCE_TYPE_PREFETCH and "LoadPrefetch" means net::LOAD_PREFETCH. - bool is_resource_type_prefetch = - info->GetResourceType() == RESOURCE_TYPE_PREFETCH; - PrefetchStatus prefetch_status = STATUS_UNDEFINED; - TimeDelta total_time = base::TimeTicks::Now() - request_->creation_time(); - - switch (request_->status().status()) { - case net::URLRequestStatus::SUCCESS: - if (request_->was_cached()) { - prefetch_status = request_->response_info().unused_since_prefetch - ? STATUS_SUCCESS_ALREADY_PREFETCHED - : STATUS_SUCCESS_FROM_CACHE; - if (is_resource_type_prefetch) { - UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentPrefetchingFromCache", - total_time); - } - } else { - prefetch_status = STATUS_SUCCESS_FROM_NETWORK; - if (is_resource_type_prefetch) { - UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentPrefetchingFromNetwork", - total_time); - } - } - break; - case net::URLRequestStatus::CANCELED: - prefetch_status = STATUS_CANCELED; - if (is_resource_type_prefetch) - UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeBeforeCancel", total_time); - break; - case net::URLRequestStatus::IO_PENDING: - case net::URLRequestStatus::FAILED: - prefetch_status = STATUS_UNDEFINED; - break; - } - - UMA_HISTOGRAM_ENUMERATION("Net.LoadPrefetch.Pattern", prefetch_status, - STATUS_MAX); - if (is_resource_type_prefetch) { - UMA_HISTOGRAM_ENUMERATION("Net.Prefetch.Pattern", prefetch_status, - STATUS_MAX); - } - } else if (request_->response_info().unused_since_prefetch) { - TimeDelta total_time = base::TimeTicks::Now() - request_->creation_time(); - UMA_HISTOGRAM_TIMES("Net.Prefetch.TimeSpentOnPrefetchHit", total_time); - } -} - void ResourceLoader::SetRawResponseHeaders( scoped_refptr<const net::HttpResponseHeaders> headers) { raw_response_headers_ = headers; diff --git a/chromium/content/browser/loader/resource_loader.h b/chromium/content/browser/loader/resource_loader.h index 93ea0021af1..bbd241e28ed 100644 --- a/chromium/content/browser/loader/resource_loader.h +++ b/chromium/content/browser/loader/resource_loader.h @@ -25,6 +25,10 @@ class HttpResponseHeaders; class X509Certificate; } +namespace network { +class ScopedThrottlingToken; +} + namespace content { class LoginDelegate; class ResourceHandler; @@ -39,10 +43,12 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, public SSLClientAuthHandler::Delegate, public ResourceHandler::Delegate { public: - ResourceLoader(std::unique_ptr<net::URLRequest> request, - std::unique_ptr<ResourceHandler> handler, - ResourceLoaderDelegate* delegate, - ResourceContext* resource_context); + ResourceLoader( + std::unique_ptr<net::URLRequest> request, + std::unique_ptr<ResourceHandler> handler, + ResourceLoaderDelegate* delegate, + ResourceContext* resource_context, + std::unique_ptr<network::ScopedThrottlingToken> throttling_token); ~ResourceLoader() override; void StartRequest(); @@ -92,14 +98,17 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, // |called_from_resource_controller| is true if called directly from a // ResourceController, in which case |resource_handler_| must not be invoked // or destroyed synchronously to avoid re-entrancy issues, and false - // otherwise. - void Resume(bool called_from_resource_controller); + // otherwise. |modified_request_headers| is used for redirects only. + void Resume( + bool called_from_resource_controller, + const base::Optional<net::HttpRequestHeaders>& modified_request_headers); void Cancel(); void CancelWithError(int error_code); void StartRequestInternal(); void CancelRequestInternal(int error, bool from_renderer); - void FollowDeferredRedirectInternal(); + void FollowDeferredRedirectInternal( + const base::Optional<net::HttpRequestHeaders>& modified_request_headers); void CompleteResponseStarted(); // If |handle_result_async| is true, the result of the following read will be // handled asynchronously if it completes synchronously, unless it's EOF or an @@ -112,7 +121,6 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, void CompleteRead(int bytes_read); void ResponseCompleted(); void CallDidFinishLoading(); - void RecordHistograms(); void SetRawResponseHeaders( scoped_refptr<const net::HttpResponseHeaders> headers); @@ -177,6 +185,8 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, ResourceContext* resource_context_; + std::unique_ptr<network::ScopedThrottlingToken> throttling_token_; + bool should_pause_reading_body_ = false; // The request is not deferred (i.e., DEFERRED_NONE) and is ready to read more // response body data. However, reading is paused because of diff --git a/chromium/content/browser/loader/resource_loader_unittest.cc b/chromium/content/browser/loader/resource_loader_unittest.cc index 469e35d26be..aeae17a85e5 100644 --- a/chromium/content/browser/loader/resource_loader_unittest.cc +++ b/chromium/content/browser/loader/resource_loader_unittest.cc @@ -59,6 +59,7 @@ #include "net/url_request/url_request_test_job.h" #include "net/url_request/url_request_test_util.h" #include "services/network/public/cpp/resource_response.h" +#include "services/network/throttling/scoped_throttling_token.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -453,7 +454,7 @@ class ResourceLoaderTest : public testing::Test, loader_.reset(new ResourceLoader( std::move(request), WrapResourceHandler(std::move(resource_handler), raw_ptr_to_request_), - this, &resource_context_)); + this, &resource_context_, nullptr /* throttling_token */)); } void SetUpResourceLoaderForUrl(const GURL& test_url) { diff --git a/chromium/content/browser/loader/resource_message_filter.cc b/chromium/content/browser/loader/resource_message_filter.cc index 4a82566e296..2f531f5ae08 100644 --- a/chromium/content/browser/loader/resource_message_filter.cc +++ b/chromium/content/browser/loader/resource_message_filter.cc @@ -4,7 +4,7 @@ #include "content/browser/loader/resource_message_filter.h" -#include "base/feature_list.h" +#include "base/command_line.h" #include "base/logging.h" #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/blob_storage/chrome_blob_storage_context.h" @@ -18,9 +18,8 @@ #include "content/common/resource_messages.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/resource_context.h" -#include "content/public/common/content_features.h" +#include "content/public/common/content_switches.h" #include "services/network/cors/cors_url_loader_factory.h" -#include "services/network/public/cpp/features.h" #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" #include "storage/browser/fileapi/file_system_context.h" @@ -146,6 +145,10 @@ void ResourceMessageFilter::CreateLoaderAndStart( void ResourceMessageFilter::Clone( network::mojom::URLLoaderFactoryRequest request) { + if (!url_loader_factory_) { + queued_clone_requests_.emplace_back(std::move(request)); + return; + } url_loader_factory_->Clone(std::move(request)); } @@ -175,15 +178,19 @@ void ResourceMessageFilter::InitializeOnIOThread() { // The WeakPtr of the filter must be created on the IO thread. So sets the // WeakPtr of |requester_info_| now. requester_info_->set_filter(GetWeakPtr()); - url_loader_factory_ = std::make_unique<URLLoaderFactoryImpl>(requester_info_); - - if (base::FeatureList::IsEnabled(network::features::kOutOfBlinkCORS)) { - url_loader_factory_ = std::make_unique<network::cors::CORSURLLoaderFactory>( - std::move(url_loader_factory_), - base::BindRepeating(&ResourceDispatcherHostImpl::CancelRequest, - base::Unretained(ResourceDispatcherHostImpl::Get()), - requester_info_->child_id())); - } + url_loader_factory_ = std::make_unique<network::cors::CORSURLLoaderFactory>( + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableWebSecurity), + std::make_unique<URLLoaderFactoryImpl>(requester_info_), + base::BindRepeating(&ResourceDispatcherHostImpl::CancelRequest, + base::Unretained(ResourceDispatcherHostImpl::Get()), + requester_info_->child_id())); + + std::vector<network::mojom::URLLoaderFactoryRequest> requests = + std::move(queued_clone_requests_); + for (auto& request : requests) + Clone(std::move(request)); + queued_clone_requests_.clear(); } } // namespace content diff --git a/chromium/content/browser/loader/resource_message_filter.h b/chromium/content/browser/loader/resource_message_filter.h index c8f2748aa4c..a0a5a0dc3cb 100644 --- a/chromium/content/browser/loader/resource_message_filter.h +++ b/chromium/content/browser/loader/resource_message_filter.h @@ -81,6 +81,7 @@ class CONTENT_EXPORT ResourceMessageFilter network::mojom::URLLoaderClientPtr client, const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) override; + // |request| could be queued when the channel has not been connected yet. void Clone(network::mojom::URLLoaderFactoryRequest request) override; int child_id() const; @@ -116,6 +117,7 @@ class CONTENT_EXPORT ResourceMessageFilter scoped_refptr<ResourceRequesterInfo> requester_info_; std::unique_ptr<network::mojom::URLLoaderFactory> url_loader_factory_; + std::vector<network::mojom::URLLoaderFactoryRequest> queued_clone_requests_; scoped_refptr<PrefetchURLLoaderService> prefetch_url_loader_service_; diff --git a/chromium/content/browser/loader/resource_request_info_impl.cc b/chromium/content/browser/loader/resource_request_info_impl.cc index 651bf20c2c3..3db8fd33813 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.cc +++ b/chromium/content/browser/loader/resource_request_info_impl.cc @@ -36,6 +36,11 @@ const void* const kResourceRequestInfoImplKey = &kResourceRequestInfoImplKey; // ResourceRequestInfo // static +ResourceRequestInfo* ResourceRequestInfo::ForRequest(net::URLRequest* request) { + return ResourceRequestInfoImpl::ForRequest(request); +} + +// static const ResourceRequestInfo* ResourceRequestInfo::ForRequest( const net::URLRequest* request) { return ResourceRequestInfoImpl::ForRequest(request); @@ -184,7 +189,8 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( previews_state_(previews_state), body_(body), initiated_in_secure_context_(initiated_in_secure_context), - blocked_cross_site_document_(false), + blocked_response_from_reaching_renderer_(false), + should_report_corb_blocking_(false), first_auth_attempt_(true) {} ResourceRequestInfoImpl::~ResourceRequestInfoImpl() { @@ -308,14 +314,6 @@ PreviewsState ResourceRequestInfoImpl::GetPreviewsState() const { return previews_state_; } -bool ResourceRequestInfoImpl::ShouldReportRawHeaders() const { - return report_raw_headers_; -} - -bool ResourceRequestInfoImpl::ShouldReportSecurityInfo() const { - return report_security_info_; -} - NavigationUIData* ResourceRequestInfoImpl::GetNavigationUIData() const { return navigation_ui_data_.get(); } @@ -325,6 +323,11 @@ ResourceRequestInfo::DevToolsStatus ResourceRequestInfoImpl::GetDevToolsStatus() return devtools_status_; } +void ResourceRequestInfoImpl::SetResourceRequestBlockedReason( + blink::ResourceRequestBlockedReason reason) { + resource_request_blocked_reason_ = reason; +} + base::Optional<blink::ResourceRequestBlockedReason> ResourceRequestInfoImpl::GetResourceRequestBlockedReason() const { return resource_request_blocked_reason_; @@ -353,6 +356,14 @@ GlobalRoutingID ResourceRequestInfoImpl::GetGlobalRoutingID() const { return GlobalRoutingID(GetChildID(), route_id_); } +bool ResourceRequestInfoImpl::ShouldReportRawHeaders() const { + return report_raw_headers_; +} + +bool ResourceRequestInfoImpl::ShouldReportSecurityInfo() const { + return report_security_info_; +} + void ResourceRequestInfoImpl::ResetBody() { body_ = nullptr; } diff --git a/chromium/content/browser/loader/resource_request_info_impl.h b/chromium/content/browser/loader/resource_request_info_impl.h index 0b65d8c115b..0bc8ea93804 100644 --- a/chromium/content/browser/loader/resource_request_info_impl.h +++ b/chromium/content/browser/loader/resource_request_info_impl.h @@ -96,14 +96,12 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, bool IsDownload() const override; // Returns a bitmask of potentially several Previews optimizations. PreviewsState GetPreviewsState() const override; - bool ShouldReportRawHeaders() const; - bool ShouldReportSecurityInfo() const; NavigationUIData* GetNavigationUIData() const override; DevToolsStatus GetDevToolsStatus() const override; - + void SetResourceRequestBlockedReason( + blink::ResourceRequestBlockedReason reason) override; base::Optional<blink::ResourceRequestBlockedReason> GetResourceRequestBlockedReason() const override; - base::StringPiece GetCustomCancelReason() const override; CONTENT_EXPORT void AssociateWithRequest(net::URLRequest* request); @@ -111,6 +109,14 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, CONTENT_EXPORT int GetRequestID() const; GlobalRoutingID GetGlobalRoutingID() const; + // Returns true if raw response headers (including sensitive data such as + // cookies) should be included with the response. + bool ShouldReportRawHeaders() const; + + // Returns true if security details (SSL/TLS connection parameters and + // certificate chain) should be included with the response. + bool ShouldReportSecurityInfo() const; + // PlzNavigate // The id of the FrameTreeNode that initiated this request (for a navigation // request). @@ -184,18 +190,19 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, devtools_status_ = devtools_status; } - void set_resource_request_blocked_reason( - base::Optional<blink::ResourceRequestBlockedReason> reason) { - resource_request_blocked_reason_ = reason; - } - void SetBlobHandles(BlobHandles blob_handles); - bool blocked_cross_site_document() const { - return blocked_cross_site_document_; + bool blocked_response_from_reaching_renderer() const { + return blocked_response_from_reaching_renderer_; + } + void set_blocked_response_from_reaching_renderer(bool value) { + blocked_response_from_reaching_renderer_ = value; } - void set_blocked_cross_site_document(bool value) { - blocked_cross_site_document_ = value; + bool should_report_corb_blocking() const { + return should_report_corb_blocking_; + } + void set_should_report_corb_blocking(bool value) { + should_report_corb_blocking_ = value; } void set_custom_cancel_reason(base::StringPiece reason) { @@ -248,7 +255,13 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, scoped_refptr<network::ResourceRequestBody> body_; bool initiated_in_secure_context_; std::unique_ptr<NavigationUIData> navigation_ui_data_; - bool blocked_cross_site_document_; + + // Whether response details (response headers, timing information, metadata) + // have been blocked from reaching the renderer process (e.g. by Cross-Origin + // Read Blocking). + bool blocked_response_from_reaching_renderer_; + + bool should_report_corb_blocking_; bool first_auth_attempt_; // Keeps upload body blobs alive for the duration of the request. diff --git a/chromium/content/browser/loader/resource_scheduler_filter.cc b/chromium/content/browser/loader/resource_scheduler_filter.cc index 3b3da1ed38c..0667df77d25 100644 --- a/chromium/content/browser/loader/resource_scheduler_filter.cc +++ b/chromium/content/browser/loader/resource_scheduler_filter.cc @@ -5,8 +5,6 @@ #include "content/browser/loader/resource_scheduler_filter.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" -#include "content/common/frame_messages.h" -#include "ipc/ipc_message_macros.h" #include "services/network/resource_scheduler.h" namespace content { @@ -18,18 +16,6 @@ network::ResourceScheduler* GetResourceSchedulerOrNullptr() { return ResourceDispatcherHostImpl::Get()->scheduler(); } -ResourceSchedulerFilter::ResourceSchedulerFilter(int child_id) - : BrowserMessageFilter(FrameMsgStart), child_id_(child_id) {} - -ResourceSchedulerFilter::~ResourceSchedulerFilter() {} - -bool ResourceSchedulerFilter::OnMessageReceived(const IPC::Message& message) { - IPC_BEGIN_MESSAGE_MAP(ResourceSchedulerFilter, message) - IPC_MESSAGE_HANDLER(FrameHostMsg_WillInsertBody, OnWillInsertBody) - IPC_END_MESSAGE_MAP() - return false; -} - // static void ResourceSchedulerFilter::OnDidCommitMainframeNavigation( int render_process_id, @@ -39,10 +25,4 @@ void ResourceSchedulerFilter::OnDidCommitMainframeNavigation( scheduler->DeprecatedOnNavigate(render_process_id, render_view_routing_id); } -void ResourceSchedulerFilter::OnWillInsertBody(int render_view_routing_id) { - auto* scheduler = GetResourceSchedulerOrNullptr(); - if (scheduler) - scheduler->DeprecatedOnWillInsertBody(child_id_, render_view_routing_id); -} - } // namespace content diff --git a/chromium/content/browser/loader/resource_scheduler_filter.h b/chromium/content/browser/loader/resource_scheduler_filter.h index 71a7b009809..573d27acd09 100644 --- a/chromium/content/browser/loader/resource_scheduler_filter.h +++ b/chromium/content/browser/loader/resource_scheduler_filter.h @@ -6,33 +6,20 @@ #define CONTENT_BROWSER_LOADER_RESOURCE_SCHEDULER_FILTER_H_ #include "base/macros.h" -#include "content/public/browser/browser_message_filter.h" namespace content { -// This class listens for incoming ViewHostMsgs that are applicable to the -// ResourceScheduler and invokes the appropriate notifications. It must be -// inserted before the RenderMessageFilter, because the ResourceScheduler runs -// on the IO thread and we want to see the messages before the view messages are -// bounced to the UI thread. -class ResourceSchedulerFilter : public BrowserMessageFilter { +// This class is used to send a signal to ResourceScheduler. This class used +// to be a ResourceMessageFilter, but is not any more. +class ResourceSchedulerFilter { public: - explicit ResourceSchedulerFilter(int child_id); - // Informs the ResourceScheduler that a main-frame, non-same-document // navigation has just committed. static void OnDidCommitMainframeNavigation(int render_process_id, int render_view_routing_id); - - // BrowserMessageFilter: - bool OnMessageReceived(const IPC::Message& message) override; - private: - ~ResourceSchedulerFilter() override; - - void OnWillInsertBody(int render_view_routing_id); - - int child_id_; + ResourceSchedulerFilter() = delete; + ~ResourceSchedulerFilter() = delete; DISALLOW_COPY_AND_ASSIGN(ResourceSchedulerFilter); }; diff --git a/chromium/content/browser/loader/stream_resource_handler.cc b/chromium/content/browser/loader/stream_resource_handler.cc index b1337ca2e17..9a487904ac1 100644 --- a/chromium/content/browser/loader/stream_resource_handler.cc +++ b/chromium/content/browser/loader/stream_resource_handler.cc @@ -73,8 +73,4 @@ void StreamResourceHandler::OnResponseCompleted( controller->Resume(); } -void StreamResourceHandler::OnDataDownloaded(int bytes_downloaded) { - NOTREACHED(); -} - } // namespace content diff --git a/chromium/content/browser/loader/stream_resource_handler.h b/chromium/content/browser/loader/stream_resource_handler.h index cb8a2d58d6b..b6f51bba48c 100644 --- a/chromium/content/browser/loader/stream_resource_handler.h +++ b/chromium/content/browser/loader/stream_resource_handler.h @@ -59,8 +59,6 @@ class StreamResourceHandler : public ResourceHandler { const net::URLRequestStatus& status, std::unique_ptr<ResourceController> controller) override; - void OnDataDownloaded(int bytes_downloaded) override; - Stream* stream() { return writer_.stream(); } private: diff --git a/chromium/content/browser/loader/temporary_file_stream.cc b/chromium/content/browser/loader/temporary_file_stream.cc deleted file mode 100644 index 2f1c784f5e0..00000000000 --- a/chromium/content/browser/loader/temporary_file_stream.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/temporary_file_stream.h" - -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/files/file_proxy.h" -#include "base/memory/ref_counted.h" -#include "base/sequenced_task_runner.h" -#include "base/task_scheduler/post_task.h" -#include "content/public/browser/browser_thread.h" -#include "net/base/file_stream.h" -#include "storage/browser/blob/shareable_file_reference.h" - -using storage::ShareableFileReference; - -namespace content { - -namespace { - -void DidCreateTemporaryFile( - const CreateTemporaryFileStreamCallback& callback, - std::unique_ptr<base::FileProxy> file_proxy, - scoped_refptr<base::SequencedTaskRunner> task_runner, - base::File::Error error_code, - const base::FilePath& file_path) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - if (!file_proxy->IsValid()) { - callback.Run(error_code, std::unique_ptr<net::FileStream>(), NULL); - return; - } - - // Cancelled or not, create the deletable_file so the temporary is cleaned up. - scoped_refptr<ShareableFileReference> deletable_file = - ShareableFileReference::GetOrCreate( - file_path, - ShareableFileReference::DELETE_ON_FINAL_RELEASE, - task_runner.get()); - - std::unique_ptr<net::FileStream> file_stream( - new net::FileStream(file_proxy->TakeFile(), task_runner)); - - callback.Run(error_code, std::move(file_stream), deletable_file.get()); -} - -} // namespace - -void CreateTemporaryFileStream( - const CreateTemporaryFileStreamCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - scoped_refptr<base::SequencedTaskRunner> task_runner = - base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE, - base::TaskShutdownBehavior::BLOCK_SHUTDOWN}); - - std::unique_ptr<base::FileProxy> file_proxy( - new base::FileProxy(task_runner.get())); - base::FileProxy* proxy = file_proxy.get(); - proxy->CreateTemporary( - base::File::FLAG_ASYNC, - base::BindOnce(&DidCreateTemporaryFile, callback, std::move(file_proxy), - std::move(task_runner))); -} - -} // namespace content diff --git a/chromium/content/browser/loader/temporary_file_stream.h b/chromium/content/browser/loader/temporary_file_stream.h deleted file mode 100644 index 406fcc445ee..00000000000 --- a/chromium/content/browser/loader/temporary_file_stream.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_TEMPORARY_FILE_STREAM_H_ -#define CONTENT_BROWSER_LOADER_TEMPORARY_FILE_STREAM_H_ - -#include <memory> - -#include "base/callback_forward.h" -#include "base/files/file.h" -#include "content/common/content_export.h" - -namespace net { -class FileStream; -} - -namespace storage { -class ShareableFileReference; -} - -namespace content { - -typedef base::Callback<void(base::File::Error, - std::unique_ptr<net::FileStream>, - storage::ShareableFileReference*)> - CreateTemporaryFileStreamCallback; - -// Creates a temporary file and asynchronously calls |callback| with a -// net::FileStream and storage::ShareableFileReference. The file is deleted -// when the storage::ShareableFileReference is deleted. Note it is the -// consumer's responsibility to ensure the storage::ShareableFileReference -// stays in scope until net::FileStream has finished closing the file. On error, -// |callback| is called with an error in the first parameter. -// -// This function may only be called on the IO thread. -// -// TODO(davidben): Juggling the net::FileStream and -// storage::ShareableFileReference lifetimes is a nuisance. The two should -// be tied together so the consumer need not deal with it. -CONTENT_EXPORT void CreateTemporaryFileStream( - const CreateTemporaryFileStreamCallback& callback); - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_TEMPORARY_FILE_STREAM_H_ diff --git a/chromium/content/browser/loader/temporary_file_stream_unittest.cc b/chromium/content/browser/loader/temporary_file_stream_unittest.cc deleted file mode 100644 index e32ae5b2a6b..00000000000 --- a/chromium/content/browser/loader/temporary_file_stream_unittest.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/temporary_file_stream.h" - -#include <string.h> -#include <string> -#include <utility> - -#include "base/bind.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "content/public/test/test_utils.h" -#include "net/base/file_stream.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/test_completion_callback.h" -#include "storage/browser/blob/shareable_file_reference.h" -#include "testing/gtest/include/gtest/gtest.h" - -using storage::ShareableFileReference; - -namespace content { - -namespace { - -const char kTestData[] = "0123456789"; -const int kTestDataSize = arraysize(kTestData) - 1; - -class WaitForFileStream { - public: - base::File::Error error() const { return error_; } - net::FileStream* file_stream() const { return file_stream_.get(); } - ShareableFileReference* deletable_file() const { - return deletable_file_.get(); - } - - void OnFileStreamCreated(base::File::Error error, - std::unique_ptr<net::FileStream> file_stream, - ShareableFileReference* deletable_file) { - error_ = error; - file_stream_ = std::move(file_stream); - deletable_file_ = deletable_file; - loop_.Quit(); - } - - void Wait() { - loop_.Run(); - } - - void Release() { - file_stream_.reset(nullptr); - deletable_file_ = nullptr; - } - private: - base::RunLoop loop_; - base::File::Error error_; - std::unique_ptr<net::FileStream> file_stream_; - scoped_refptr<ShareableFileReference> deletable_file_; -}; - -} // namespace - -TEST(TemporaryFileStreamTest, Basic) { - TestBrowserThreadBundle thread_bundle(TestBrowserThreadBundle::IO_MAINLOOP); - - // Create a temporary. - WaitForFileStream file_stream_waiter; - CreateTemporaryFileStream(base::Bind(&WaitForFileStream::OnFileStreamCreated, - base::Unretained(&file_stream_waiter))); - file_stream_waiter.Wait(); - - // The temporary should exist. - EXPECT_EQ(base::File::FILE_OK, file_stream_waiter.error()); - base::FilePath file_path = file_stream_waiter.deletable_file()->path(); - EXPECT_TRUE(base::PathExists(file_path)); - - // Write some data to the temporary. - int bytes_written = 0; - scoped_refptr<net::IOBufferWithSize> buf = - new net::IOBufferWithSize(kTestDataSize); - memcpy(buf->data(), kTestData, kTestDataSize); - scoped_refptr<net::DrainableIOBuffer> drainable = - new net::DrainableIOBuffer(buf.get(), buf->size()); - while (bytes_written != kTestDataSize) { - net::TestCompletionCallback write_callback; - int rv = file_stream_waiter.file_stream()->Write( - drainable.get(), drainable->BytesRemaining(), - write_callback.callback()); - if (rv == net::ERR_IO_PENDING) - rv = write_callback.WaitForResult(); - ASSERT_LT(0, rv); - drainable->DidConsume(rv); - bytes_written += rv; - } - - // Verify the data matches. - std::string contents; - ASSERT_TRUE(base::ReadFileToString(file_path, &contents)); - EXPECT_EQ(kTestData, contents); - - // Close the file. - net::TestCompletionCallback close_callback; - int rv = file_stream_waiter.file_stream()->Close(close_callback.callback()); - if (rv == net::ERR_IO_PENDING) - rv = close_callback.WaitForResult(); - EXPECT_EQ(net::OK, rv); - - // Release everything. The file should be gone now. - file_stream_waiter.Release(); - content::RunAllTasksUntilIdle(); - - // The temporary should be gone now. - EXPECT_FALSE(base::PathExists(file_path)); -} - -} // content diff --git a/chromium/content/browser/loader/test_resource_handler.cc b/chromium/content/browser/loader/test_resource_handler.cc index b4c9f869b52..0fbeb2706a9 100644 --- a/chromium/content/browser/loader/test_resource_handler.cc +++ b/chromium/content/browser/loader/test_resource_handler.cc @@ -147,7 +147,6 @@ void TestResourceHandler::OnWillRead( int* buf_size, std::unique_ptr<ResourceController> controller) { EXPECT_FALSE(canceled_); - EXPECT_FALSE(expect_on_data_downloaded_); EXPECT_EQ(0, on_response_completed_called_); // Only create a ScopedCallDepthTracker if not called re-entrantly, as // OnWillRead may be called synchronously in response to a Resume(), but @@ -182,7 +181,6 @@ void TestResourceHandler::OnReadCompleted( int bytes_read, std::unique_ptr<ResourceController> controller) { EXPECT_FALSE(canceled_); - EXPECT_FALSE(expect_on_data_downloaded_); EXPECT_EQ(1, on_will_start_called_); EXPECT_EQ(1, on_response_started_called_); EXPECT_EQ(0, on_response_completed_called_); @@ -225,7 +223,7 @@ void TestResourceHandler::OnResponseCompleted( parent_read_buffer_size_ = nullptr; EXPECT_EQ(0, on_response_completed_called_); - if (status.is_success() && !expect_on_data_downloaded_ && expect_eof_read_) + if (status.is_success() && expect_eof_read_) EXPECT_EQ(1, on_read_eof_called_); ++on_response_completed_called_; @@ -248,15 +246,6 @@ void TestResourceHandler::OnResponseCompleted( controller->Resume(); } -void TestResourceHandler::OnDataDownloaded(int bytes_downloaded) { - EXPECT_TRUE(expect_on_data_downloaded_); - EXPECT_EQ(1, on_will_start_called_); - EXPECT_EQ(1, on_response_started_called_); - EXPECT_EQ(0, on_response_completed_called_); - - total_bytes_downloaded_ += bytes_downloaded; -} - void TestResourceHandler::Resume() { ScopedCallDepthTracker call_depth_tracker(&call_depth_); diff --git a/chromium/content/browser/loader/test_resource_handler.h b/chromium/content/browser/loader/test_resource_handler.h index f109fce1a6d..d99196a1128 100644 --- a/chromium/content/browser/loader/test_resource_handler.h +++ b/chromium/content/browser/loader/test_resource_handler.h @@ -63,7 +63,6 @@ class TestResourceHandler : public ResourceHandler { void OnResponseCompleted( const net::URLRequestStatus& status, std::unique_ptr<ResourceController> controller) override; - void OnDataDownloaded(int bytes_downloaded) override; void Resume(); void CancelWithError(net::Error error_code); @@ -122,12 +121,6 @@ class TestResourceHandler : public ResourceHandler { defer_on_response_completed_ = defer_on_response_completed; } - // Set if OnDataDownloaded calls are expected instead of - // OnWillRead/OnReadCompleted. - void set_expect_on_data_downloaded(bool expect_on_data_downloaded) { - expect_on_data_downloaded_ = expect_on_data_downloaded; - } - // Sets whether to expect a final 0-byte read on success. Defaults to true. void set_expect_eof_read(bool expect_eof_read) { expect_eof_read_ = expect_eof_read; @@ -154,8 +147,6 @@ class TestResourceHandler : public ResourceHandler { return resource_response_.get(); }; - int total_bytes_downloaded() const { return total_bytes_downloaded_; } - const std::string& body() const { return body_; } net::URLRequestStatus final_status() const { return final_status_; } @@ -197,8 +188,6 @@ class TestResourceHandler : public ResourceHandler { bool defer_on_read_eof_ = false; bool defer_on_response_completed_ = false; - bool expect_on_data_downloaded_ = false; - bool expect_eof_read_ = true; int on_will_start_called_ = 0; @@ -211,7 +200,6 @@ class TestResourceHandler : public ResourceHandler { GURL start_url_; scoped_refptr<network::ResourceResponse> resource_response_; - int total_bytes_downloaded_ = 0; std::string body_; net::URLRequestStatus final_status_ = net::URLRequestStatus::FromError(net::ERR_UNEXPECTED); diff --git a/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc b/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc index e6ede61511d..9385955b5c5 100644 --- a/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc +++ b/chromium/content/browser/loader/url_loader_factory_impl_unittest.cc @@ -203,8 +203,8 @@ TEST_P(URLLoaderFactoryImplTest, GetResponse) { client.completion_status().encoded_data_length); EXPECT_EQ(static_cast<int64_t>(expected.size()), client.completion_status().encoded_body_length); - // OnTransferSizeUpdated is not dispatched as report_raw_headers is not set. - EXPECT_EQ(0, client.body_transfer_size()); + EXPECT_EQ(static_cast<int64_t>(expected.size()), client.body_transfer_size()); + EXPECT_GT(client.body_transfer_size(), 0); EXPECT_GT(client.response_head().encoded_data_length, 0); EXPECT_GT(client.completion_status().encoded_data_length, 0); } @@ -322,133 +322,6 @@ TEST_P(URLLoaderFactoryImplTest, ShouldNotRequestURL) { EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); } -TEST_P(URLLoaderFactoryImplTest, DownloadToFile) { - constexpr int32_t kRoutingId = 1; - constexpr int32_t kRequestId = 2; - - network::mojom::URLLoaderPtr loader; - base::FilePath root; - base::PathService::Get(DIR_TEST_DATA, &root); - net::URLRequestMockHTTPJob::AddUrlHandlers(root); - - network::ResourceRequest request; - network::TestURLLoaderClient client; - request.url = net::URLRequestMockHTTPJob::GetMockUrl("hello.html"); - request.method = "GET"; - request.resource_type = RESOURCE_TYPE_XHR; - request.download_to_file = true; - request.request_initiator = url::Origin::Create(request.url); - factory_->CreateLoaderAndStart( - mojo::MakeRequest(&loader), kRoutingId, kRequestId, 0, request, - client.CreateInterfacePtr(), - net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); - ASSERT_FALSE(client.has_received_response()); - ASSERT_FALSE(client.has_data_downloaded()); - ASSERT_FALSE(client.has_received_completion()); - - client.RunUntilResponseReceived(); - - net::URLRequest* url_request = - rdh_.GetURLRequest(GlobalRequestID(kChildId, kRequestId)); - ASSERT_TRUE(url_request); - ResourceRequestInfoImpl* request_info = - ResourceRequestInfoImpl::ForRequest(url_request); - ASSERT_TRUE(request_info); - EXPECT_EQ(kChildId, request_info->GetChildID()); - EXPECT_EQ(kRoutingId, request_info->GetRouteID()); - EXPECT_EQ(kRequestId, request_info->GetRequestID()); - - ASSERT_FALSE(client.has_received_completion()); - - client.RunUntilComplete(); - ASSERT_TRUE(client.has_data_downloaded()); - ASSERT_TRUE(client.has_received_completion()); - - EXPECT_EQ(200, client.response_head().headers->response_code()); - std::string content_type; - client.response_head().headers->GetNormalizedHeader("content-type", - &content_type); - EXPECT_EQ("text/html", content_type); - EXPECT_EQ(0, client.completion_status().error_code); - - std::string contents; - base::ReadFileToString(client.response_head().download_file_path, &contents); - - EXPECT_EQ(static_cast<int64_t>(contents.size()), - client.download_data_length()); - EXPECT_EQ(static_cast<int64_t>(contents.size()), - client.encoded_download_data_length()); - - std::string expected; - base::ReadFileToString( - root.Append(base::FilePath(FILE_PATH_LITERAL("hello.html"))), &expected); - EXPECT_EQ(expected, contents); - EXPECT_EQ(static_cast<int64_t>(expected.size()) + - client.response_head().encoded_data_length, - client.completion_status().encoded_data_length); - EXPECT_EQ(static_cast<int64_t>(expected.size()), - client.completion_status().encoded_body_length); -} - -TEST_P(URLLoaderFactoryImplTest, DownloadToFileFailure) { - constexpr int32_t kRoutingId = 1; - constexpr int32_t kRequestId = 2; - - network::mojom::URLLoaderPtr loader; - base::FilePath root; - base::PathService::Get(DIR_TEST_DATA, &root); - net::URLRequestSlowDownloadJob::AddUrlHandler(); - - network::ResourceRequest request; - network::TestURLLoaderClient client; - request.url = GURL(net::URLRequestSlowDownloadJob::kKnownSizeUrl); - request.method = "GET"; - request.resource_type = RESOURCE_TYPE_XHR; - request.download_to_file = true; - request.request_initiator = url::Origin::Create(request.url); - factory_->CreateLoaderAndStart( - mojo::MakeRequest(&loader), kRoutingId, kRequestId, 0, request, - client.CreateInterfacePtr(), - net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); - ASSERT_FALSE(client.has_received_response()); - ASSERT_FALSE(client.has_data_downloaded()); - ASSERT_FALSE(client.has_received_completion()); - - client.RunUntilResponseReceived(); - - net::URLRequest* url_request = - rdh_.GetURLRequest(GlobalRequestID(kChildId, kRequestId)); - ASSERT_TRUE(url_request); - ResourceRequestInfoImpl* request_info = - ResourceRequestInfoImpl::ForRequest(url_request); - ASSERT_TRUE(request_info); - EXPECT_EQ(kChildId, request_info->GetChildID()); - EXPECT_EQ(kRoutingId, request_info->GetRouteID()); - EXPECT_EQ(kRequestId, request_info->GetRequestID()); - - ASSERT_FALSE(client.has_received_completion()); - - client.RunUntilDataDownloaded(); - ASSERT_TRUE(client.has_data_downloaded()); - ASSERT_FALSE(client.has_received_completion()); - EXPECT_LT(0, client.download_data_length()); - EXPECT_GE( - static_cast<int64_t>(net::URLRequestSlowDownloadJob::kFirstDownloadSize), - client.download_data_length()); - EXPECT_LT(0, client.encoded_download_data_length()); - EXPECT_GE( - static_cast<int64_t>(net::URLRequestSlowDownloadJob::kFirstDownloadSize), - client.encoded_download_data_length()); - - url_request->Cancel(); - client.RunUntilComplete(); - - ASSERT_TRUE(client.has_received_completion()); - - EXPECT_EQ(200, client.response_head().headers->response_code()); - EXPECT_EQ(net::ERR_ABORTED, client.completion_status().error_code); -} - TEST_P(URLLoaderFactoryImplTest, OnTransferSizeUpdated) { constexpr int32_t kRoutingId = 81; constexpr int32_t kRequestId = 28; diff --git a/chromium/content/browser/loader/wake_lock_resource_throttle.cc b/chromium/content/browser/loader/wake_lock_resource_throttle.cc deleted file mode 100644 index 9ba1bd0da64..00000000000 --- a/chromium/content/browser/loader/wake_lock_resource_throttle.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/loader/wake_lock_resource_throttle.h" - -#include "content/browser/service_manager/service_manager_context.h" -#include "content/public/browser/browser_thread.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "services/device/public/mojom/constants.mojom.h" -#include "services/device/public/mojom/wake_lock_provider.mojom.h" -#include "services/service_manager/public/cpp/connector.h" - -namespace content { - -namespace { - -const int kWakeLockDelaySeconds = 30; - -} // namespace - -WakeLockResourceThrottle::WakeLockResourceThrottle(const std::string& host) - : host_(host) {} - -WakeLockResourceThrottle::~WakeLockResourceThrottle() {} - -void WakeLockResourceThrottle::WillStartRequest(bool* defer) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Delay wake lock request to dismiss small requests. - timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kWakeLockDelaySeconds), - this, &WakeLockResourceThrottle::RequestWakeLock); -} - -void WakeLockResourceThrottle::WillProcessResponse(bool* defer) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - // Cancel wake lock after request finishes. - if (wake_lock_) - wake_lock_->CancelWakeLock(); - - timer_.Stop(); -} - -const char* WakeLockResourceThrottle::GetNameForLogging() const { - return "WakeLockResourceThrottle"; -} - -void WakeLockResourceThrottle::RequestWakeLock() { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(!wake_lock_); - - service_manager::Connector* connector = - ServiceManagerContext::GetConnectorForIOThread(); - // |connector| might be nullptr in some testing contexts, in which the - // service manager connection isn't initialized. - if (connector) { - device::mojom::WakeLockProviderPtr wake_lock_provider; - connector->BindInterface(device::mojom::kServiceName, - mojo::MakeRequest(&wake_lock_provider)); - wake_lock_provider->GetWakeLockWithoutContext( - device::mojom::WakeLockType::kPreventAppSuspension, - device::mojom::WakeLockReason::kOther, "Uploading data to " + host_, - mojo::MakeRequest(&wake_lock_)); - - wake_lock_->RequestWakeLock(); - } -} - -} // namespace content diff --git a/chromium/content/browser/loader/wake_lock_resource_throttle.h b/chromium/content/browser/loader/wake_lock_resource_throttle.h deleted file mode 100644 index 986b566aa86..00000000000 --- a/chromium/content/browser/loader/wake_lock_resource_throttle.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_LOADER_WAKE_LOCK_RESOURCE_THROTTLE_H_ -#define CONTENT_BROWSER_LOADER_WAKE_LOCK_RESOURCE_THROTTLE_H_ - -#include <memory> -#include <string> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/timer/timer.h" -#include "content/public/browser/resource_throttle.h" -#include "services/device/public/mojom/wake_lock.mojom.h" - -namespace content { - -// This ResourceThrottle holds wake lock until large upload request finishes. -class WakeLockResourceThrottle : public ResourceThrottle { - public: - WakeLockResourceThrottle(const std::string& host); - ~WakeLockResourceThrottle() override; - - // ResourceThrottle overrides: - void WillStartRequest(bool* defer) override; - void WillProcessResponse(bool* defer) override; - const char* GetNameForLogging() const override; - - private: - void RequestWakeLock(); - - const std::string host_; - base::OneShotTimer timer_; - - // Destruction of wake_lock_ will trigger - // WakeLock::OnConnectionError on the service side, so there is no - // need to call CancelWakeLock() in the destructor. - device::mojom::WakeLockPtr wake_lock_; - - DISALLOW_COPY_AND_ASSIGN(WakeLockResourceThrottle); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_LOADER_WAKE_LOCK_RESOURCE_THROTTLE_H_ |