Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 1 | // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "base/test/scoped_feature_list.h" |
| 6 | #include "build/build_config.h" |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 7 | #include "content/browser/frame_host/render_frame_host_impl.h" |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 8 | #include "content/browser/storage_partition_impl.h" |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 9 | #include "content/browser/url_loader_factory_getter.h" |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 10 | #include "content/public/browser/browser_context.h" |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 11 | #include "content/public/browser/network_service_instance.h" |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 12 | #include "content/public/browser/web_contents.h" |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 13 | #include "content/public/common/content_switches.h" |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 14 | #include "content/public/test/browser_test_utils.h" |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 15 | #include "content/public/test/content_browser_test.h" |
| 16 | #include "content/public/test/content_browser_test_utils.h" |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 17 | #include "content/public/test/simple_url_loader_test_helper.h" |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 18 | #include "content/shell/browser/shell.h" |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 19 | #include "net/dns/mock_host_resolver.h" |
| 20 | #include "net/test/embedded_test_server/http_request.h" |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 21 | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
Yutaka Hirano | d8789f9 | 2018-01-30 09:59:51 | [diff] [blame] | 22 | #include "services/network/public/cpp/features.h" |
John Abd-El-Malek | bdafede | 2018-02-06 15:10:36 | [diff] [blame] | 23 | #include "services/network/public/cpp/simple_url_loader.h" |
Ken Rockot | 54311e6 | 2018-02-10 19:01:52 | [diff] [blame] | 24 | #include "services/network/public/mojom/network_service.mojom.h" |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 25 | |
| 26 | namespace content { |
| 27 | |
| 28 | namespace { |
| 29 | |
John Abd-El-Malek | 53670dd | 2018-01-18 22:07:21 | [diff] [blame] | 30 | network::mojom::NetworkContextPtr CreateNetworkContext() { |
| 31 | network::mojom::NetworkContextPtr network_context; |
| 32 | network::mojom::NetworkContextParamsPtr context_params = |
| 33 | network::mojom::NetworkContextParams::New(); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 34 | GetNetworkService()->CreateNetworkContext(mojo::MakeRequest(&network_context), |
| 35 | std::move(context_params)); |
| 36 | return network_context; |
| 37 | } |
| 38 | |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 39 | int LoadBasicRequestOnIOThread( |
| 40 | URLLoaderFactoryGetter* url_loader_factory_getter, |
| 41 | const GURL& url) { |
| 42 | DCHECK_CURRENTLY_ON(BrowserThread::UI); |
John Abd-El-Malek | 1df6179 | 2018-01-12 20:40:45 | [diff] [blame] | 43 | auto request = std::make_unique<network::ResourceRequest>(); |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 44 | request->url = url; |
| 45 | |
| 46 | SimpleURLLoaderTestHelper simple_loader_helper; |
| 47 | // Wait for callback on UI thread to avoid nesting IO message loops. |
| 48 | simple_loader_helper.SetRunLoopQuitThread(BrowserThread::UI); |
| 49 | |
John Abd-El-Malek | bdafede | 2018-02-06 15:10:36 | [diff] [blame] | 50 | std::unique_ptr<network::SimpleURLLoader> simple_loader = |
| 51 | network::SimpleURLLoader::Create(std::move(request), |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 52 | TRAFFIC_ANNOTATION_FOR_TESTS); |
| 53 | |
| 54 | // |URLLoaderFactoryGetter::GetNetworkFactory()| can only be accessed on IO |
| 55 | // thread. |
| 56 | BrowserThread::PostTask( |
| 57 | BrowserThread::IO, FROM_HERE, |
| 58 | base::BindOnce( |
John Abd-El-Malek | bdafede | 2018-02-06 15:10:36 | [diff] [blame] | 59 | [](network::SimpleURLLoader* loader, |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 60 | URLLoaderFactoryGetter* factory_getter, |
John Abd-El-Malek | bdafede | 2018-02-06 15:10:36 | [diff] [blame] | 61 | network::SimpleURLLoader::BodyAsStringCallback |
| 62 | body_as_string_callback) { |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 63 | loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( |
| 64 | factory_getter->GetNetworkFactory(), |
| 65 | std::move(body_as_string_callback)); |
| 66 | }, |
| 67 | base::Unretained(simple_loader.get()), |
| 68 | base::Unretained(url_loader_factory_getter), |
| 69 | simple_loader_helper.GetCallback())); |
| 70 | |
| 71 | simple_loader_helper.WaitForCallback(); |
| 72 | return simple_loader->NetError(); |
| 73 | } |
| 74 | |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 75 | } // namespace |
| 76 | |
| 77 | // This test source has been excluded from Android as Android doesn't have |
| 78 | // out-of-process Network Service. |
| 79 | class NetworkServiceRestartBrowserTest : public ContentBrowserTest { |
| 80 | public: |
| 81 | NetworkServiceRestartBrowserTest() { |
Yutaka Hirano | d8789f9 | 2018-01-30 09:59:51 | [diff] [blame] | 82 | scoped_feature_list_.InitAndEnableFeature( |
| 83 | network::features::kNetworkService); |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 84 | } |
| 85 | |
| 86 | void SetUpOnMainThread() override { |
Chong Zhang | 5d9b3418 | 2018-01-30 16:20:57 | [diff] [blame] | 87 | embedded_test_server()->RegisterRequestMonitor( |
| 88 | base::BindRepeating(&NetworkServiceRestartBrowserTest::MonitorRequest, |
| 89 | base::Unretained(this))); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 90 | EXPECT_TRUE(embedded_test_server()->Start()); |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 91 | ContentBrowserTest::SetUpOnMainThread(); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 92 | } |
| 93 | |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 94 | GURL GetTestURL() const { |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 95 | // Use '/echoheader' instead of '/echo' to avoid a disk_cache bug. |
| 96 | // See https://crbug.com/792255. |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 97 | return embedded_test_server()->GetURL("/echoheader"); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 98 | } |
| 99 | |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 100 | BrowserContext* browser_context() { |
| 101 | return shell()->web_contents()->GetBrowserContext(); |
| 102 | } |
| 103 | |
Chong Zhang | 5d9b3418 | 2018-01-30 16:20:57 | [diff] [blame] | 104 | RenderFrameHostImpl* main_frame() { |
| 105 | return static_cast<RenderFrameHostImpl*>( |
| 106 | shell()->web_contents()->GetMainFrame()); |
| 107 | } |
| 108 | |
| 109 | bool CheckCanLoadHttp(const std::string& relative_url) { |
| 110 | GURL test_url = embedded_test_server()->GetURL(relative_url); |
| 111 | std::string script( |
| 112 | "var xhr = new XMLHttpRequest();" |
| 113 | "xhr.open('GET', '"); |
| 114 | script += test_url.spec() + |
| 115 | "', true);" |
| 116 | "xhr.onload = function (e) {" |
| 117 | " if (xhr.readyState === 4) {" |
| 118 | " window.domAutomationController.send(xhr.status === 200);" |
| 119 | " }" |
| 120 | "};" |
| 121 | "xhr.onerror = function () {" |
| 122 | " window.domAutomationController.send(false);" |
| 123 | "};" |
| 124 | "xhr.send(null)"; |
| 125 | bool xhr_result = false; |
| 126 | // The JS call will fail if disallowed because the process will be killed. |
| 127 | bool execute_result = |
| 128 | ExecuteScriptAndExtractBool(shell(), script, &xhr_result); |
| 129 | return xhr_result && execute_result; |
| 130 | } |
| 131 | |
| 132 | // Called by |embedded_test_server()|. |
| 133 | void MonitorRequest(const net::test_server::HttpRequest& request) { |
| 134 | last_request_relative_url_ = request.relative_url; |
| 135 | } |
| 136 | |
| 137 | std::string last_request_relative_url() const { |
| 138 | return last_request_relative_url_; |
| 139 | } |
| 140 | |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 141 | private: |
Chong Zhang | 5d9b3418 | 2018-01-30 16:20:57 | [diff] [blame] | 142 | std::string last_request_relative_url_; |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 143 | base::test::ScopedFeatureList scoped_feature_list_; |
| 144 | |
| 145 | DISALLOW_COPY_AND_ASSIGN(NetworkServiceRestartBrowserTest); |
| 146 | }; |
| 147 | |
| 148 | IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, |
| 149 | NetworkServiceProcessRecovery) { |
John Abd-El-Malek | 53670dd | 2018-01-18 22:07:21 | [diff] [blame] | 150 | network::mojom::NetworkContextPtr network_context = CreateNetworkContext(); |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 151 | EXPECT_EQ(net::OK, LoadBasicRequest(network_context.get(), GetTestURL())); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 152 | EXPECT_TRUE(network_context.is_bound()); |
| 153 | EXPECT_FALSE(network_context.encountered_error()); |
| 154 | |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 155 | // Crash the NetworkService process. Existing interfaces should receive error |
| 156 | // notifications at some point. |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 157 | SimulateNetworkServiceCrash(); |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 158 | // |network_context| will receive an error notification, but it's not |
| 159 | // guaranteed to have arrived at this point. Flush the pointer to make sure |
| 160 | // the notification has been received. |
| 161 | network_context.FlushForTesting(); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 162 | EXPECT_TRUE(network_context.is_bound()); |
| 163 | EXPECT_TRUE(network_context.encountered_error()); |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 164 | // Make sure we could get |net::ERR_FAILED| with an invalid |network_context|. |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 165 | EXPECT_EQ(net::ERR_FAILED, |
| 166 | LoadBasicRequest(network_context.get(), GetTestURL())); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 167 | |
| 168 | // NetworkService should restart automatically and return valid interface. |
John Abd-El-Malek | 53670dd | 2018-01-18 22:07:21 | [diff] [blame] | 169 | network::mojom::NetworkContextPtr network_context2 = CreateNetworkContext(); |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 170 | EXPECT_EQ(net::OK, LoadBasicRequest(network_context2.get(), GetTestURL())); |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 171 | EXPECT_TRUE(network_context2.is_bound()); |
| 172 | EXPECT_FALSE(network_context2.encountered_error()); |
| 173 | } |
| 174 | |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 175 | // Make sure |StoragePartitionImpl::GetNetworkContext()| returns valid interface |
| 176 | // after crash. |
| 177 | IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, |
Chong Zhang | e0dfdb6 | 2017-12-09 18:25:14 | [diff] [blame] | 178 | StoragePartitionImplGetNetworkContext) { |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 179 | StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 180 | BrowserContext::GetDefaultStoragePartition(browser_context())); |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 181 | |
John Abd-El-Malek | 53670dd | 2018-01-18 22:07:21 | [diff] [blame] | 182 | network::mojom::NetworkContext* old_network_context = |
| 183 | partition->GetNetworkContext(); |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 184 | EXPECT_EQ(net::OK, LoadBasicRequest(old_network_context, GetTestURL())); |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 185 | |
| 186 | // Crash the NetworkService process. Existing interfaces should receive error |
| 187 | // notifications at some point. |
| 188 | SimulateNetworkServiceCrash(); |
| 189 | // Flush the interface to make sure the error notification was received. |
| 190 | partition->FlushNetworkInterfaceForTesting(); |
| 191 | |
| 192 | // |partition->GetNetworkContext()| should return a valid new pointer after |
| 193 | // crash. |
| 194 | EXPECT_NE(old_network_context, partition->GetNetworkContext()); |
Chong Zhang | c40a6ce5 | 2017-12-10 03:00:28 | [diff] [blame] | 195 | EXPECT_EQ(net::OK, |
| 196 | LoadBasicRequest(partition->GetNetworkContext(), GetTestURL())); |
Chong Zhang | 5f46827 | 2017-12-07 23:59:14 | [diff] [blame] | 197 | } |
| 198 | |
Chong Zhang | d4c92364 | 2018-01-03 21:22:29 | [diff] [blame] | 199 | // Make sure |URLLoaderFactoryGetter| returns valid interface after crash. |
| 200 | IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, |
| 201 | URLLoaderFactoryGetterGetNetworkFactory) { |
| 202 | StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| 203 | BrowserContext::GetDefaultStoragePartition(browser_context())); |
| 204 | scoped_refptr<URLLoaderFactoryGetter> url_loader_factory_getter = |
| 205 | partition->url_loader_factory_getter(); |
| 206 | EXPECT_EQ(net::OK, LoadBasicRequestOnIOThread(url_loader_factory_getter.get(), |
| 207 | GetTestURL())); |
| 208 | // Crash the NetworkService process. Existing interfaces should receive error |
| 209 | // notifications at some point. |
| 210 | SimulateNetworkServiceCrash(); |
| 211 | // Flush the interface to make sure the error notification was received. |
| 212 | partition->FlushNetworkInterfaceForTesting(); |
| 213 | url_loader_factory_getter->FlushNetworkInterfaceOnIOThreadForTesting(); |
| 214 | |
| 215 | // |url_loader_factory_getter| should be able to get a valid new pointer after |
| 216 | // crash. |
| 217 | EXPECT_EQ(net::OK, LoadBasicRequestOnIOThread(url_loader_factory_getter.get(), |
| 218 | GetTestURL())); |
| 219 | } |
| 220 | |
| 221 | // Make sure basic navigation works after crash. |
| 222 | IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, |
| 223 | NavigationURLLoaderBasic) { |
| 224 | StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| 225 | BrowserContext::GetDefaultStoragePartition(browser_context())); |
| 226 | |
| 227 | EXPECT_TRUE( |
| 228 | NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html"))); |
| 229 | |
| 230 | // Crash the NetworkService process. Existing interfaces should receive error |
| 231 | // notifications at some point. |
| 232 | SimulateNetworkServiceCrash(); |
| 233 | // Flush the interface to make sure the error notification was received. |
| 234 | partition->FlushNetworkInterfaceForTesting(); |
| 235 | partition->url_loader_factory_getter() |
| 236 | ->FlushNetworkInterfaceOnIOThreadForTesting(); |
| 237 | |
| 238 | EXPECT_TRUE( |
| 239 | NavigateToURL(shell(), embedded_test_server()->GetURL("/title2.html"))); |
| 240 | } |
| 241 | |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 242 | // Make sure basic XHR works after crash. |
Chong Zhang | 5d9b3418 | 2018-01-30 16:20:57 | [diff] [blame] | 243 | IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, BasicXHR) { |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 244 | StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>( |
| 245 | BrowserContext::GetDefaultStoragePartition(browser_context())); |
| 246 | |
Chong Zhang | 5d9b3418 | 2018-01-30 16:20:57 | [diff] [blame] | 247 | EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL("/echo"))); |
Chong Zhang | 7306b0b | 2018-01-24 05:59:24 | [diff] [blame] | 248 | EXPECT_TRUE(CheckCanLoadHttp("/title1.html")); |
| 249 | EXPECT_EQ(last_request_relative_url(), "/title1.html"); |
| 250 | |
| 251 | // Crash the NetworkService process. Existing interfaces should receive error |
| 252 | // notifications at some point. |
| 253 | SimulateNetworkServiceCrash(); |
| 254 | // Flush the interface to make sure the error notification was received. |
| 255 | partition->FlushNetworkInterfaceForTesting(); |
| 256 | // Flush the interface to make sure the frame host has received error |
| 257 | // notification and the new URLLoaderFactoryBundle has been received by the |
| 258 | // frame. |
| 259 | main_frame()->FlushNetworkAndNavigationInterfacesForTesting(); |
| 260 | |
| 261 | EXPECT_TRUE(CheckCanLoadHttp("/title2.html")); |
| 262 | EXPECT_EQ(last_request_relative_url(), "/title2.html"); |
| 263 | } |
| 264 | |
Chong Zhang | c583e67 | 2017-11-08 16:34:08 | [diff] [blame] | 265 | } // namespace content |