blob: 174ed5f2f1ed72e6916a90f55aa61c8174f62bd6 [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"
20#include "content/public/common/resource_response.h"
21#include "content/public/test/test_browser_thread_bundle.h"
22#include "net/base/net_errors.h"
rhalavatia20efdbc2017-04-20 12:28:2723#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
mmenke3c1d10c2017-03-09 16:25:4524#include "net/url_request/redirect_info.h"
25#include "net/url_request/url_request_context.h"
26#include "net/url_request/url_request_status.h"
27#include "net/url_request/url_request_test_util.h"
28#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,
66 nullptr, // context
67 0, // render_process_id
68 0, // render_view_id
69 0, // render_frame_id
70 true, // is_main_frame
71 false, // parent_is_main_frame
72 true, // allow_download
73 true, // is_async
74 PREVIEWS_OFF); // previews_state
75
76 std::unique_ptr<TestResourceHandler> test_handler;
77 if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
78 test_handler = base::MakeUnique<TestResourceHandler>();
79 test_handler_ = test_handler->GetWeakPtr();
80 }
81 // TODO(mmenke): This file currently has no timeout tests. Should it?
82 detachable_handler_ = base::MakeUnique<DetachableResourceHandler>(
83 request_.get(), base::TimeDelta::FromMinutes(30),
84 std::move(test_handler));
85 mock_loader_ =
86 base::MakeUnique<MockResourceLoader>(detachable_handler_.get());
87 }
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(
160 net::RedirectInfo(), base::MakeRefCounted<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(
kylechar96f3eba2017-09-25 20:23:56169 base::MakeRefCounted<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
260 mock_loader_->OnRequestRedirected(net::RedirectInfo(),
kylechar96f3eba2017-09-25 20:23:56261 base::MakeRefCounted<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
kylechar96f3eba2017-09-25 20:23:56268 mock_loader_->OnResponseStarted(base::MakeRefCounted<ResourceResponse>());
mmenke3c1d10c2017-03-09 16:25:45269 if (test_handler_) {
270 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
271 EXPECT_EQ(1, test_handler_->on_response_started_called());
272 EXPECT_EQ(0, test_handler_->on_will_read_called());
273 }
274 MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_STARTED);
275
276 mock_loader_->OnWillRead();
277 if (test_handler_) {
278 EXPECT_EQ(1, test_handler_->on_will_read_called());
279 EXPECT_EQ(0, test_handler_->on_read_completed_called());
280 }
281 MaybeAsyncDetachAt(DetachPhase::FIRST_ON_WILL_READ);
282
283 mock_loader_->OnReadCompleted(kFirstBodyRead);
284 if (test_handler_) {
285 EXPECT_EQ(1, test_handler_->on_read_completed_called());
286 EXPECT_EQ(kFirstBodyRead, test_handler_->body());
287 }
288 MaybeAsyncDetachAt(DetachPhase::FIRST_ON_READ_COMPLETED);
289
290 if (test_handler_)
291 test_handler_->set_defer_on_will_read(true);
292 mock_loader_->OnWillRead();
293 if (test_handler_) {
294 EXPECT_EQ(2, test_handler_->on_will_read_called());
295 EXPECT_EQ(1, test_handler_->on_read_completed_called());
296 }
297 MaybeAsyncDetachAt(DetachPhase::SECOND_ON_WILL_READ);
298
299 if (test_handler_)
300 test_handler_->set_defer_on_read_completed(true);
301 mock_loader_->OnReadCompleted(kSecondBodyRead);
302 if (test_handler_) {
303 EXPECT_EQ(2, test_handler_->on_will_read_called());
304 EXPECT_EQ(2, test_handler_->on_read_completed_called());
305 EXPECT_EQ(kResponseBody, test_handler_->body());
306 }
307 MaybeAsyncDetachAt(DetachPhase::SECOND_ON_READ_COMPLETED);
308
309 // Test doesn't check detaching on the third OnWillRead call.
310 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
311 if (GetParam() > DetachPhase::SECOND_ON_READ_COMPLETED) {
312 EXPECT_EQ(3, test_handler_->on_will_read_called());
313 EXPECT_EQ(2, test_handler_->on_read_completed_called());
314 EXPECT_EQ(0, test_handler_->on_response_completed_called());
315 } else {
316 EXPECT_FALSE(test_handler_);
317 }
318
319 if (test_handler_)
320 test_handler_->set_defer_on_read_completed(true);
321 mock_loader_->OnReadCompleted("");
322 if (test_handler_) {
323 EXPECT_EQ(3, test_handler_->on_read_completed_called());
324 EXPECT_EQ(1, test_handler_->on_read_eof_called());
325 EXPECT_EQ(0, test_handler_->on_response_completed_called());
326 }
327 MaybeAsyncDetachAt(DetachPhase::ON_READ_EOF);
328
329 if (test_handler_)
330 test_handler_->set_defer_on_response_completed(true);
331 mock_loader_->OnResponseCompleted(net::URLRequestStatus::FromError(net::OK));
332 if (test_handler_) {
333 EXPECT_EQ(1, test_handler_->on_response_completed_called());
334 EXPECT_EQ(kResponseBody, test_handler_->body());
335 }
336 MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_COMPLETED);
337}
338
339INSTANTIATE_TEST_CASE_P(/* No prefix needed*/,
340 DetachableResourceHandlerTest,
341 testing::Values(DetachPhase::DETACHED_FROM_CREATION,
342 DetachPhase::ON_WILL_START,
343 DetachPhase::REQUEST_REDIRECTED,
344 DetachPhase::ON_RESPONSE_STARTED,
345 DetachPhase::FIRST_ON_WILL_READ,
346 DetachPhase::FIRST_ON_READ_COMPLETED,
347 DetachPhase::SECOND_ON_WILL_READ,
348 DetachPhase::SECOND_ON_READ_COMPLETED,
349 DetachPhase::ON_READ_EOF,
350 DetachPhase::ON_RESPONSE_COMPLETED,
351 DetachPhase::NEVER_DETACH));
352
353} // namespace
354
355} // namespace content