blob: 2905662abec0e1db0ce8be0afd1b64d289e1a975 [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"
23#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"
27#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,
61 nullptr)) {
62 ResourceRequestInfo::AllocateForTesting(request_.get(),
63 RESOURCE_TYPE_MAIN_FRAME,
64 nullptr, // context
65 0, // render_process_id
66 0, // render_view_id
67 0, // render_frame_id
68 true, // is_main_frame
69 false, // parent_is_main_frame
70 true, // allow_download
71 true, // is_async
72 PREVIEWS_OFF); // previews_state
73
74 std::unique_ptr<TestResourceHandler> test_handler;
75 if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
76 test_handler = base::MakeUnique<TestResourceHandler>();
77 test_handler_ = test_handler->GetWeakPtr();
78 }
79 // TODO(mmenke): This file currently has no timeout tests. Should it?
80 detachable_handler_ = base::MakeUnique<DetachableResourceHandler>(
81 request_.get(), base::TimeDelta::FromMinutes(30),
82 std::move(test_handler));
83 mock_loader_ =
84 base::MakeUnique<MockResourceLoader>(detachable_handler_.get());
85 }
86
87 // If the DetachableResourceHandler is supposed to detach the next handler at
88 // |phase|, attempts to detach the request.
89 void MaybeSyncDetachAtPhase(DetachPhase phase) {
90 if (GetParam() == phase) {
91 detachable_handler_->Detach();
92 EXPECT_FALSE(test_handler_);
93 }
94 }
95
96 // Returns true if the DetachableResourceHandler should have detached the next
97 // handler at or before the specified phase. Also checks that |test_handler_|
98 // is nullptr iff the request should have been detached by the specified
99 // phase.
100 bool WasDetachedBy(DetachPhase phase) {
101 if (GetParam() <= phase) {
102 EXPECT_FALSE(test_handler_);
103 return true;
104 }
105 EXPECT_TRUE(test_handler_);
106 return false;
107 }
108
109 // If the DetachableResourceHandler is supposed to detach the next handler at
110 // |phase|, attempts to detach the request. Expected to be called in sync
111 // tests after the specified phase has started. Performs additional sanity
112 // checks based on that assumption.
113 void MaybeAsyncDetachAt(DetachPhase phase) {
114 if (GetParam() < phase) {
115 EXPECT_FALSE(test_handler_);
116 EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
117 return;
118 }
119
120 EXPECT_EQ(MockResourceLoader::Status::CALLBACK_PENDING,
121 mock_loader_->status());
122
123 if (GetParam() == phase) {
124 detachable_handler_->Detach();
125 EXPECT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->status());
126 EXPECT_FALSE(test_handler_);
127 return;
128 }
129
130 test_handler_->Resume();
131 }
132
133 protected:
134 TestBrowserThreadBundle thread_bundle_;
135 net::TestURLRequestContext context_;
136 std::unique_ptr<net::URLRequest> request_;
137
138 base::WeakPtr<TestResourceHandler> test_handler_;
139
140 std::unique_ptr<DetachableResourceHandler> detachable_handler_;
141 std::unique_ptr<MockResourceLoader> mock_loader_;
142};
143
144// Tests where ResourceHandler completes synchronously. Handler is detached
145// just before the phase indicated by the DetachPhase parameter.
146TEST_P(DetachableResourceHandlerTest, Sync) {
147 MaybeSyncDetachAtPhase(DetachPhase::ON_WILL_START);
148 ASSERT_EQ(MockResourceLoader::Status::IDLE,
149 mock_loader_->OnWillStart(request_->url()));
150 if (!WasDetachedBy(DetachPhase::ON_WILL_START)) {
151 EXPECT_EQ(1, test_handler_->on_will_start_called());
152 EXPECT_EQ(0, test_handler_->on_request_redirected_called());
153 }
154
155 MaybeSyncDetachAtPhase(DetachPhase::REQUEST_REDIRECTED);
156 ASSERT_EQ(
157 MockResourceLoader::Status::IDLE,
158 mock_loader_->OnRequestRedirected(
159 net::RedirectInfo(), make_scoped_refptr(new ResourceResponse())));
160 if (!WasDetachedBy(DetachPhase::REQUEST_REDIRECTED)) {
161 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
162 EXPECT_EQ(0, test_handler_->on_response_started_called());
163 }
164
165 MaybeSyncDetachAtPhase(DetachPhase::ON_RESPONSE_STARTED);
166 ASSERT_EQ(MockResourceLoader::Status::IDLE,
167 mock_loader_->OnResponseStarted(
168 make_scoped_refptr(new ResourceResponse())));
169 if (!WasDetachedBy(DetachPhase::ON_RESPONSE_STARTED)) {
170 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
171 EXPECT_EQ(1, test_handler_->on_response_started_called());
172 EXPECT_EQ(0, test_handler_->on_will_read_called());
173 }
174
175 MaybeSyncDetachAtPhase(DetachPhase::FIRST_ON_WILL_READ);
176 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
177 if (!WasDetachedBy(DetachPhase::FIRST_ON_WILL_READ)) {
178 EXPECT_EQ(1, test_handler_->on_will_read_called());
179 EXPECT_EQ(0, test_handler_->on_read_completed_called());
180 }
181
182 MaybeSyncDetachAtPhase(DetachPhase::FIRST_ON_READ_COMPLETED);
183 ASSERT_EQ(MockResourceLoader::Status::IDLE,
184 mock_loader_->OnReadCompleted(kFirstBodyRead));
185 if (!WasDetachedBy(DetachPhase::FIRST_ON_READ_COMPLETED)) {
186 EXPECT_EQ(1, test_handler_->on_read_completed_called());
187 EXPECT_EQ(kFirstBodyRead, test_handler_->body());
188 }
189
190 MaybeSyncDetachAtPhase(DetachPhase::SECOND_ON_WILL_READ);
191 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
192 if (!WasDetachedBy(DetachPhase::SECOND_ON_WILL_READ)) {
193 EXPECT_EQ(2, test_handler_->on_will_read_called());
194 EXPECT_EQ(1, test_handler_->on_read_completed_called());
195 }
196
197 MaybeSyncDetachAtPhase(DetachPhase::SECOND_ON_READ_COMPLETED);
198 ASSERT_EQ(MockResourceLoader::Status::IDLE,
199 mock_loader_->OnReadCompleted(kSecondBodyRead));
200 if (!WasDetachedBy(DetachPhase::SECOND_ON_READ_COMPLETED)) {
201 EXPECT_EQ(2, test_handler_->on_will_read_called());
202 EXPECT_EQ(2, test_handler_->on_read_completed_called());
203 EXPECT_EQ(kResponseBody, test_handler_->body());
204 }
205
206 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
207 if (!WasDetachedBy(DetachPhase::SECOND_ON_READ_COMPLETED)) {
208 EXPECT_EQ(3, test_handler_->on_will_read_called());
209 EXPECT_EQ(2, test_handler_->on_read_completed_called());
210 EXPECT_EQ(0, test_handler_->on_response_completed_called());
211 }
212
213 MaybeSyncDetachAtPhase(DetachPhase::ON_READ_EOF);
214 ASSERT_EQ(MockResourceLoader::Status::IDLE,
215 mock_loader_->OnReadCompleted(""));
216 if (!WasDetachedBy(DetachPhase::ON_READ_EOF)) {
217 EXPECT_EQ(3, test_handler_->on_read_completed_called());
218 EXPECT_EQ(1, test_handler_->on_read_eof_called());
219 EXPECT_EQ(0, test_handler_->on_response_completed_called());
220 }
221
222 MaybeSyncDetachAtPhase(DetachPhase::ON_RESPONSE_COMPLETED);
223 ASSERT_EQ(MockResourceLoader::Status::IDLE,
224 mock_loader_->OnResponseCompleted(
225 net::URLRequestStatus::FromError(net::OK)));
226 if (!WasDetachedBy(DetachPhase::ON_RESPONSE_COMPLETED)) {
227 EXPECT_EQ(1, test_handler_->on_response_completed_called());
228 EXPECT_EQ(kResponseBody, test_handler_->body());
229 }
230}
231
232// Tests where ResourceHandler completes asynchronously. Handler is detached
233// during the phase indicated by the DetachPhase parameter. Async cases where
234// the handler is detached between phases are similar enough to the sync tests
235// that they wouldn't provide meaningfully better test coverage.
236//
237// Before the handler is detached, all calls complete asynchronously.
238// Afterwards, they all complete synchronously.
239TEST_P(DetachableResourceHandlerTest, Async) {
240 if (GetParam() != DetachPhase::DETACHED_FROM_CREATION) {
241 test_handler_->set_defer_on_will_start(true);
242 test_handler_->set_defer_on_request_redirected(true);
243 test_handler_->set_defer_on_response_started(true);
244 test_handler_->set_defer_on_will_read(true);
245 test_handler_->set_defer_on_read_completed(true);
246 test_handler_->set_defer_on_read_eof(true);
247 // Note: Can't set |defer_on_response_completed|, since the
248 // DetachableResourceHandler DCHECKs when the next handler tries to defer
249 // the ERR_ABORTED message it sends downstream.
250 }
251
252 mock_loader_->OnWillStart(request_->url());
253 if (test_handler_) {
254 EXPECT_EQ(1, test_handler_->on_will_start_called());
255 EXPECT_EQ(0, test_handler_->on_request_redirected_called());
256 }
257 MaybeAsyncDetachAt(DetachPhase::ON_WILL_START);
258
259 mock_loader_->OnRequestRedirected(net::RedirectInfo(),
260 make_scoped_refptr(new ResourceResponse()));
261 if (test_handler_) {
262 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
263 EXPECT_EQ(0, test_handler_->on_response_started_called());
264 }
265 MaybeAsyncDetachAt(DetachPhase::REQUEST_REDIRECTED);
266
267 mock_loader_->OnResponseStarted(make_scoped_refptr(new ResourceResponse()));
268 if (test_handler_) {
269 EXPECT_EQ(1, test_handler_->on_request_redirected_called());
270 EXPECT_EQ(1, test_handler_->on_response_started_called());
271 EXPECT_EQ(0, test_handler_->on_will_read_called());
272 }
273 MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_STARTED);
274
275 mock_loader_->OnWillRead();
276 if (test_handler_) {
277 EXPECT_EQ(1, test_handler_->on_will_read_called());
278 EXPECT_EQ(0, test_handler_->on_read_completed_called());
279 }
280 MaybeAsyncDetachAt(DetachPhase::FIRST_ON_WILL_READ);
281
282 mock_loader_->OnReadCompleted(kFirstBodyRead);
283 if (test_handler_) {
284 EXPECT_EQ(1, test_handler_->on_read_completed_called());
285 EXPECT_EQ(kFirstBodyRead, test_handler_->body());
286 }
287 MaybeAsyncDetachAt(DetachPhase::FIRST_ON_READ_COMPLETED);
288
289 if (test_handler_)
290 test_handler_->set_defer_on_will_read(true);
291 mock_loader_->OnWillRead();
292 if (test_handler_) {
293 EXPECT_EQ(2, test_handler_->on_will_read_called());
294 EXPECT_EQ(1, test_handler_->on_read_completed_called());
295 }
296 MaybeAsyncDetachAt(DetachPhase::SECOND_ON_WILL_READ);
297
298 if (test_handler_)
299 test_handler_->set_defer_on_read_completed(true);
300 mock_loader_->OnReadCompleted(kSecondBodyRead);
301 if (test_handler_) {
302 EXPECT_EQ(2, test_handler_->on_will_read_called());
303 EXPECT_EQ(2, test_handler_->on_read_completed_called());
304 EXPECT_EQ(kResponseBody, test_handler_->body());
305 }
306 MaybeAsyncDetachAt(DetachPhase::SECOND_ON_READ_COMPLETED);
307
308 // Test doesn't check detaching on the third OnWillRead call.
309 ASSERT_EQ(MockResourceLoader::Status::IDLE, mock_loader_->OnWillRead());
310 if (GetParam() > DetachPhase::SECOND_ON_READ_COMPLETED) {
311 EXPECT_EQ(3, test_handler_->on_will_read_called());
312 EXPECT_EQ(2, test_handler_->on_read_completed_called());
313 EXPECT_EQ(0, test_handler_->on_response_completed_called());
314 } else {
315 EXPECT_FALSE(test_handler_);
316 }
317
318 if (test_handler_)
319 test_handler_->set_defer_on_read_completed(true);
320 mock_loader_->OnReadCompleted("");
321 if (test_handler_) {
322 EXPECT_EQ(3, test_handler_->on_read_completed_called());
323 EXPECT_EQ(1, test_handler_->on_read_eof_called());
324 EXPECT_EQ(0, test_handler_->on_response_completed_called());
325 }
326 MaybeAsyncDetachAt(DetachPhase::ON_READ_EOF);
327
328 if (test_handler_)
329 test_handler_->set_defer_on_response_completed(true);
330 mock_loader_->OnResponseCompleted(net::URLRequestStatus::FromError(net::OK));
331 if (test_handler_) {
332 EXPECT_EQ(1, test_handler_->on_response_completed_called());
333 EXPECT_EQ(kResponseBody, test_handler_->body());
334 }
335 MaybeAsyncDetachAt(DetachPhase::ON_RESPONSE_COMPLETED);
336}
337
338INSTANTIATE_TEST_CASE_P(/* No prefix needed*/,
339 DetachableResourceHandlerTest,
340 testing::Values(DetachPhase::DETACHED_FROM_CREATION,
341 DetachPhase::ON_WILL_START,
342 DetachPhase::REQUEST_REDIRECTED,
343 DetachPhase::ON_RESPONSE_STARTED,
344 DetachPhase::FIRST_ON_WILL_READ,
345 DetachPhase::FIRST_ON_READ_COMPLETED,
346 DetachPhase::SECOND_ON_WILL_READ,
347 DetachPhase::SECOND_ON_READ_COMPLETED,
348 DetachPhase::ON_READ_EOF,
349 DetachPhase::ON_RESPONSE_COMPLETED,
350 DetachPhase::NEVER_DETACH));
351
352} // namespace
353
354} // namespace content