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