blob: a477ded464199e33ebdeb6ca9c35b9f3b228487a [file] [log] [blame]
mmenke3c1d10c2017-03-09 16:25:451// Copyright 2017 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/loader/detachable_resource_handler.h"
6
7#include <string>
8
9#include "base/logging.h"
10#include "base/macros.h"
mmenke3c1d10c2017-03-09 16:25:4511#include "base/memory/weak_ptr.h"
12#include "base/single_thread_task_runner.h"
13#include "base/threading/thread_task_runner_handle.h"
14#include "base/time/time.h"
15#include "content/browser/loader/mock_resource_loader.h"
16#include "content/browser/loader/resource_controller.h"
17#include "content/browser/loader/test_resource_handler.h"
18#include "content/public/browser/resource_request_info.h"
mmenke3c1d10c2017-03-09 16:25:4519#include "content/public/test/test_browser_thread_bundle.h"
20#include "net/base/net_errors.h"
rhalavatia20efdbc2017-04-20 12:28:2721#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
mmenke3c1d10c2017-03-09 16:25:4522#include "net/url_request/redirect_info.h"
23#include "net/url_request/url_request_context.h"
24#include "net/url_request/url_request_status.h"
25#include "net/url_request/url_request_test_util.h"
John Abd-El-Malek46248032018-01-17 19:11:2326#include "services/network/public/cpp/resource_response.h"
mmenke3c1d10c2017-03-09 16:25:4527#include "testing/gtest/include/gtest/gtest.h"
28#include "url/gurl.h"
29
30namespace content {
31
32namespace {
33
34// Full response body.
35const char kResponseBody[] = "Nifty response body.";
36// Two separate reads allow for testing cancellation in the middle of one read,
37// and between reads.
38const char kFirstBodyRead[] = "Nifty";
39const char kSecondBodyRead[] = " response body.";
40
41enum class DetachPhase {
42 DETACHED_FROM_CREATION,
43 ON_WILL_START,
44 REQUEST_REDIRECTED,
45 ON_RESPONSE_STARTED,
46 FIRST_ON_WILL_READ,
47 FIRST_ON_READ_COMPLETED,
48 SECOND_ON_WILL_READ,
49 SECOND_ON_READ_COMPLETED,
50 ON_READ_EOF,
51 ON_RESPONSE_COMPLETED,
52 NEVER_DETACH,
53};
54
55class DetachableResourceHandlerTest
56 : public testing::TestWithParam<DetachPhase> {
57 public:
58 DetachableResourceHandlerTest()
59 : request_(context_.CreateRequest(GURL("http://foo/"),
60 net::DEFAULT_PRIORITY,
rhalavatia20efdbc2017-04-20 12:28:2761 nullptr,
62 TRAFFIC_ANNOTATION_FOR_TESTS)) {
mmenke3c1d10c2017-03-09 16:25:4563 ResourceRequestInfo::AllocateForTesting(request_.get(),
64 RESOURCE_TYPE_MAIN_FRAME,
Jian Li18173422017-11-08 03:00:0265 nullptr, // context
66 0, // render_process_id
67 0, // render_view_id
68 0, // render_frame_id
69 true, // is_main_frame
70 true, // allow_download
71 true, // is_async
72 PREVIEWS_OFF, // previews_state
73 nullptr); // navigation_ui_data
mmenke3c1d10c2017-03-09 16:25:4574
75 std::unique_ptr<TestResourceHandler> test_handler;
76 if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
Jeremy Roman04f27c372017-10-27 15:20:5577 test_handler = std::make_unique<TestResourceHandler>();
mmenke3c1d10c2017-03-09 16:25:4578 test_handler_ = test_handler->GetWeakPtr();
79 }
80 // TODO(mmenke): This file currently has no timeout tests. Should it?
Jeremy Roman04f27c372017-10-27 15:20:5581 detachable_handler_ = std::make_unique<DetachableResourceHandler>(
mmenke3c1d10c2017-03-09 16:25:4582 request_.get(), base::TimeDelta::FromMinutes(30),
83 std::move(test_handler));
84 mock_loader_ =
Jeremy Roman04f27c372017-10-27 15:20:5585 std::make_unique<MockResourceLoader>(detachable_handler_.get());
mmenke3c1d10c2017-03-09 16:25:4586 }
87
88 // If the DetachableResourceHandler is supposed to detach the next handler at
89 // |phase|, attempts to detach the request.
90 void MaybeSyncDetachAtPhase(DetachPhase phase) {
91 if (GetParam() == phase) {
92 detachable_handler_->Detach();
93 EXPECT_FALSE(test_handler_);
94 }
95 }
96
97 // Returns true if the DetachableResourceHandler should have detached the next
98 // handler at or before the specified phase. Also checks that |test_handler_|
99 // is nullptr iff the request should have been detached by the specified
100 // phase.
101 bool WasDetachedBy(DetachPhase phase) {
102 if (GetParam() <= phase) {
103 EXPECT_FALSE(test_handler_);
104 return true;
105 }
106 EXPECT_TRUE(test_handler_);
107 return false;
108 }
109
110 // If the DetachableResourceHandler is supposed to detach the next handler at
111 // |phase|, attempts to detach the request. Expected to be called in sync
112 // tests after the specified phase has started. Performs additional sanity
113 // checks based on that assumption.
114 void MaybeAsyncDetachAt(DetachPhase phase) {
115 if (GetParam() < phase) {
116 EXPECT_FALSE(test_handler_);
117 EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
118 return;
119 }
120
121 EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
122 mock_loader_->status());
123
124 if (GetParam() == phase) {
125 detachable_handler_->Detach();
126 EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
127 EXPECT_FALSE(test_handler_);
128 return;
129 }
130
131 test_handler_->Resume();
132 }
133
134 protected:
135 TestBrowserThreadBundle thread_bundle_;
136 net::TestURLRequestContext context_;
137 std::unique_ptr<net::URLRequest> request_;
138
139 base::WeakPtr<TestResourceHandler> test_handler_;
140
141 std::unique_ptr<DetachableResourceHandler> detachable_handler_;
142 std::unique_ptr<MockResourceLoader> mock_loader_;
143};
144
145// Tests where ResourceHandler completes synchronously. Handler is detached
146// just before the phase indicated by the DetachPhase parameter.
147TEST_P(DetachableResourceHandlerTest, Sync) {
148 MaybeSyncDetachAtPhase(DetachPhase::ON_WILL_START);
149 ASSERT_EQ(MockResourceLoader::Status::IDLE,
150 mock_loader_->OnWillStart(request_->url()));
151 if (!WasDetachedBy(DetachPhase::ON_WILL_START)) {
152 EXPECT_EQ(1, test_handler_->on_will_start_called());
153 EXPECT_EQ(0, test_handler_->on_request_redirected_called());
154 }
155
156 MaybeSyncDetachAtPhase(DetachPhase::REQUEST_REDIRECTED);
kylechar96f3eba2017-09-25 20:23:56157 ASSERT_EQ(MockResourceLoader::Status::IDLE,
158 mock_loader_->OnRequestRedirected(
John Abd-El-Malek46248032018-01-17 19:11:23159 net::RedirectInfo(),
160 base::MakeRefCounted<network::ResourceResponse>()));
mmenke3c1d10c2017-03-09 16:25:45161 if (!WasDetachedBy(DetachPhase::REQUEST_REDIRECTED)) {
162 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
163 EXPECT_EQ(0, test_handler_->on_response_started_called());
164 }
165
166 MaybeSyncDetachAtPhase(DetachPhase::ON_RESPONSE_STARTED);
167 ASSERT_EQ(MockResourceLoader::Status::IDLE,
168 mock_loader_->OnResponseStarted(
John Abd-El-Malek46248032018-01-17 19:11:23169 base::MakeRefCounted<network::ResourceResponse>()));
mmenke3c1d10c2017-03-09 16:25:45170 if (!WasDetachedBy(DetachPhase::ON_RESPONSE_STARTED)) {
171 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
172 EXPECT_EQ(1, test_handler_->on_response_started_called());
173 EXPECT_EQ(0, test_handler_->on_will_read_called());
174 }
175
176 MaybeSyncDetachAtPhase(DetachPhase::FIRST_ON_WILL_READ);
177 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
178 if (!WasDetachedBy(DetachPhase::FIRST_ON_WILL_READ)) {
179 EXPECT_EQ(1, test_handler_->on_will_read_called());
180 EXPECT_EQ(0, test_handler_->on_read_completed_called());
181 }
182
183 MaybeSyncDetachAtPhase(DetachPhase::FIRST_ON_READ_COMPLETED);
184 ASSERT_EQ(MockResourceLoader::Status::IDLE,
185 mock_loader_->OnReadCompleted(kFirstBodyRead));
186 if (!WasDetachedBy(DetachPhase::FIRST_ON_READ_COMPLETED)) {
187 EXPECT_EQ(1, test_handler_->on_read_completed_called());
188 EXPECT_EQ(kFirstBodyRead, test_handler_->body());
189 }
190
191 MaybeSyncDetachAtPhase(DetachPhase::SECOND_ON_WILL_READ);
192 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
193 if (!WasDetachedBy(DetachPhase::SECOND_ON_WILL_READ)) {
194 EXPECT_EQ(2, test_handler_->on_will_read_called());
195 EXPECT_EQ(1, test_handler_->on_read_completed_called());
196 }
197
198 MaybeSyncDetachAtPhase(DetachPhase::SECOND_ON_READ_COMPLETED);
199 ASSERT_EQ(MockResourceLoader::Status::IDLE,
200 mock_loader_->OnReadCompleted(kSecondBodyRead));
201 if (!WasDetachedBy(DetachPhase::SECOND_ON_READ_COMPLETED)) {
202 EXPECT_EQ(2, test_handler_->on_will_read_called());
203 EXPECT_EQ(2, test_handler_->on_read_completed_called());
204 EXPECT_EQ(kResponseBody, test_handler_->body());
205 }
206
207 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
208 if (!WasDetachedBy(DetachPhase::SECOND_ON_READ_COMPLETED)) {
209 EXPECT_EQ(3, test_handler_->on_will_read_called());
210 EXPECT_EQ(2, test_handler_->on_read_completed_called());
211 EXPECT_EQ(0, test_handler_->on_response_completed_called());
212 }
213
214 MaybeSyncDetachAtPhase(DetachPhase::ON_READ_EOF);
215 ASSERT_EQ(MockResourceLoader::Status::IDLE,
216 mock_loader_->OnReadCompleted(""));
217 if (!WasDetachedBy(DetachPhase::ON_READ_EOF)) {
218 EXPECT_EQ(3, test_handler_->on_read_completed_called());
219 EXPECT_EQ(1, test_handler_->on_read_eof_called());
220 EXPECT_EQ(0, test_handler_->on_response_completed_called());
221 }
222
223 MaybeSyncDetachAtPhase(DetachPhase::ON_RESPONSE_COMPLETED);
224 ASSERT_EQ(MockResourceLoader::Status::IDLE,
225 mock_loader_->OnResponseCompleted(
226 net::URLRequestStatus::FromError(net::OK)));
227 if (!WasDetachedBy(DetachPhase::ON_RESPONSE_COMPLETED)) {
228 EXPECT_EQ(1, test_handler_->on_response_completed_called());
229 EXPECT_EQ(kResponseBody, test_handler_->body());
230 }
231}
232
233// Tests where ResourceHandler completes asynchronously. Handler is detached
234// during the phase indicated by the DetachPhase parameter. Async cases where
235// the handler is detached between phases are similar enough to the sync tests
236// that they wouldn't provide meaningfully better test coverage.
237//
238// Before the handler is detached, all calls complete asynchronously.
239// Afterwards, they all complete synchronously.
240TEST_P(DetachableResourceHandlerTest, Async) {
241 if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
242 test_handler_->set_defer_on_will_start(true);
243 test_handler_->set_defer_on_request_redirected(true);
244 test_handler_->set_defer_on_response_started(true);
245 test_handler_->set_defer_on_will_read(true);
246 test_handler_->set_defer_on_read_completed(true);
247 test_handler_->set_defer_on_read_eof(true);
248 // Note: Can't set |defer_on_response_completed|, since the
249 // DetachableResourceHandler DCHECKs when the next handler tries to defer
250 // the ERR_ABORTED message it sends downstream.
251 }
252
253 mock_loader_->OnWillStart(request_->url());
254 if (test_handler_) {
255 EXPECT_EQ(1, test_handler_->on_will_start_called());
256 EXPECT_EQ(0, test_handler_->on_request_redirected_called());
257 }
258 MaybeAsyncDetachAt(DetachPhase::ON_WILL_START);
259
John Abd-El-Malek46248032018-01-17 19:11:23260 mock_loader_->OnRequestRedirected(
261 net::RedirectInfo(), base::MakeRefCounted<network::ResourceResponse>());
mmenke3c1d10c2017-03-09 16:25:45262 if (test_handler_) {
263 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
264 EXPECT_EQ(0, test_handler_->on_response_started_called());
265 }
266 MaybeAsyncDetachAt(DetachPhase::REQUEST_REDIRECTED);
267
John Abd-El-Malek46248032018-01-17 19:11:23268 mock_loader_->OnResponseStarted(
269 base::MakeRefCounted<network::ResourceResponse>());
mmenke3c1d10c2017-03-09 16:25:45270 if (test_handler_) {
271 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
272 EXPECT_EQ(1, test_handler_->on_response_started_called());
273 EXPECT_EQ(0, test_handler_->on_will_read_called());
274 }
275 MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_STARTED);
276
277 mock_loader_->OnWillRead();
278 if (test_handler_) {
279 EXPECT_EQ(1, test_handler_->on_will_read_called());
280 EXPECT_EQ(0, test_handler_->on_read_completed_called());
281 }
282 MaybeAsyncDetachAt(DetachPhase::FIRST_ON_WILL_READ);
283
284 mock_loader_->OnReadCompleted(kFirstBodyRead);
285 if (test_handler_) {
286 EXPECT_EQ(1, test_handler_->on_read_completed_called());
287 EXPECT_EQ(kFirstBodyRead, test_handler_->body());
288 }
289 MaybeAsyncDetachAt(DetachPhase::FIRST_ON_READ_COMPLETED);
290
291 if (test_handler_)
292 test_handler_->set_defer_on_will_read(true);
293 mock_loader_->OnWillRead();
294 if (test_handler_) {
295 EXPECT_EQ(2, test_handler_->on_will_read_called());
296 EXPECT_EQ(1, test_handler_->on_read_completed_called());
297 }
298 MaybeAsyncDetachAt(DetachPhase::SECOND_ON_WILL_READ);
299
300 if (test_handler_)
301 test_handler_->set_defer_on_read_completed(true);
302 mock_loader_->OnReadCompleted(kSecondBodyRead);
303 if (test_handler_) {
304 EXPECT_EQ(2, test_handler_->on_will_read_called());
305 EXPECT_EQ(2, test_handler_->on_read_completed_called());
306 EXPECT_EQ(kResponseBody, test_handler_->body());
307 }
308 MaybeAsyncDetachAt(DetachPhase::SECOND_ON_READ_COMPLETED);
309
310 // Test doesn't check detaching on the third OnWillRead call.
311 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
312 if (GetParam() > DetachPhase::SECOND_ON_READ_COMPLETED) {
313 EXPECT_EQ(3, test_handler_->on_will_read_called());
314 EXPECT_EQ(2, test_handler_->on_read_completed_called());
315 EXPECT_EQ(0, test_handler_->on_response_completed_called());
316 } else {
317 EXPECT_FALSE(test_handler_);
318 }
319
320 if (test_handler_)
321 test_handler_->set_defer_on_read_completed(true);
322 mock_loader_->OnReadCompleted("");
323 if (test_handler_) {
324 EXPECT_EQ(3, test_handler_->on_read_completed_called());
325 EXPECT_EQ(1, test_handler_->on_read_eof_called());
326 EXPECT_EQ(0, test_handler_->on_response_completed_called());
327 }
328 MaybeAsyncDetachAt(DetachPhase::ON_READ_EOF);
329
330 if (test_handler_)
331 test_handler_->set_defer_on_response_completed(true);
332 mock_loader_->OnResponseCompleted(net::URLRequestStatus::FromError(net::OK));
333 if (test_handler_) {
334 EXPECT_EQ(1, test_handler_->on_response_completed_called());
335 EXPECT_EQ(kResponseBody, test_handler_->body());
336 }
337 MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_COMPLETED);
338}
339
340INSTANTIATE_TEST_CASE_P(/* No prefix needed*/,
341 DetachableResourceHandlerTest,
342 testing::Values(DetachPhase::DETACHED_FROM_CREATION,
343 DetachPhase::ON_WILL_START,
344 DetachPhase::REQUEST_REDIRECTED,
345 DetachPhase::ON_RESPONSE_STARTED,
346 DetachPhase::FIRST_ON_WILL_READ,
347 DetachPhase::FIRST_ON_READ_COMPLETED,
348 DetachPhase::SECOND_ON_WILL_READ,
349 DetachPhase::SECOND_ON_READ_COMPLETED,
350 DetachPhase::ON_READ_EOF,
351 DetachPhase::ON_RESPONSE_COMPLETED,
352 DetachPhase::NEVER_DETACH));
353
354} // namespace
355
356} // namespace content