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 "content/public/test/test_download_http_response.h"
6
7 #include <inttypes.h>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/numerics/ranges.h"
14 #include "base/run_loop.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/lock.h"
19 #include "base/threading/thread_task_runner_handle.h"
20 #include "content/public/browser/browser_task_traits.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "net/http/http_request_headers.h"
23 #include "net/http/http_util.h"
24
25 namespace content {
26
27 namespace {
28
29 // Lock object for protecting |g_parameters_map|.
30 base::LazyInstance<base::Lock>::Leaky g_lock = LAZY_INSTANCE_INITIALIZER;
31
32 using ParametersMap = std::map<GURL, TestDownloadHttpResponse::Parameters>;
33 // Maps url to Parameters so that requests for the same URL will get the same
34 // parameters.
35 base::LazyInstance<ParametersMap>::Leaky g_parameters_map =
36 LAZY_INSTANCE_INITIALIZER;
37
38 const char* kTestDownloadPath = "/download/";
39
40 // The size of buffer to send the entity body. The header will always be sent in
41 // one buffer.
42 const int64_t kBufferSize = 64 * 1024;
43
44 // Xorshift* PRNG from https://en.wikipedia.org/wiki/Xorshift
XorShift64StarWithIndex(uint64_t seed,uint64_t index)45 uint64_t XorShift64StarWithIndex(uint64_t seed, uint64_t index) {
46 const uint64_t kMultiplier = UINT64_C(2685821657736338717);
47 uint64_t x = seed * kMultiplier + index;
48 x ^= x >> 12;
49 x ^= x << 25;
50 x ^= x >> 27;
51 return x * kMultiplier;
52 }
53
54 // Called to resume the response.
OnResume(scoped_refptr<base::SingleThreadTaskRunner> task_runner,base::OnceClosure resume_callback)55 void OnResume(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
56 base::OnceClosure resume_callback) {
57 task_runner->PostTask(FROM_HERE, std::move(resume_callback));
58 }
59
OnResponseSentOnServerIOThread(TestDownloadHttpResponse::OnResponseSentCallback callback,std::unique_ptr<TestDownloadHttpResponse::CompletedRequest> request)60 void OnResponseSentOnServerIOThread(
61 TestDownloadHttpResponse::OnResponseSentCallback callback,
62 std::unique_ptr<TestDownloadHttpResponse::CompletedRequest> request) {
63 GetUIThreadTaskRunner({})->PostTask(
64 FROM_HERE, base::BindOnce(std::move(callback), std::move(request)));
65 }
66
GetURLFromRequest(const net::test_server::HttpRequest & request)67 GURL GetURLFromRequest(const net::test_server::HttpRequest& request) {
68 return GURL(base::StringPrintf(
69 "http://%s%s", request.headers.at(net::HttpRequestHeaders::kHost).c_str(),
70 request.relative_url.c_str()));
71 }
72 // The shim response object used by embedded_test_server. After this object is
73 // deleted, we may continue to send data with cached SendBytesCallback to
74 // support pause/resume behaviors.
75 class HttpResponse : public net::test_server::HttpResponse {
76 public:
HttpResponse(base::WeakPtr<TestDownloadHttpResponse> owner)77 explicit HttpResponse(base::WeakPtr<TestDownloadHttpResponse> owner)
78 : owner_(owner) {}
79 ~HttpResponse() override = default;
80
81 private:
82 // net::test_server::HttpResponse implementations.
SendResponse(const net::test_server::SendBytesCallback & send,net::test_server::SendCompleteCallback done)83 void SendResponse(const net::test_server::SendBytesCallback& send,
84 net::test_server::SendCompleteCallback done) override {
85 if (owner_)
86 owner_->SendResponse(send, std::move(done));
87 }
88
89 base::WeakPtr<TestDownloadHttpResponse> owner_;
90 DISALLOW_COPY_AND_ASSIGN(HttpResponse);
91 };
92
93 } // namespace
94
95 const char TestDownloadHttpResponse::kTestDownloadHostName[] =
96 "*.default.example.com";
97
98 // static
GetNextURLForDownload()99 GURL TestDownloadHttpResponse::GetNextURLForDownload() {
100 static int index = 0;
101 std::string url_string = base::StringPrintf("http://%d.default.example.com%s",
102 ++index, kTestDownloadPath);
103 return GURL(url_string);
104 }
105
HttpResponseData(int64_t min_offset,int64_t max_offset,const std::string & response,bool is_transient,bool delay_response)106 TestDownloadHttpResponse::HttpResponseData::HttpResponseData(
107 int64_t min_offset,
108 int64_t max_offset,
109 const std::string& response,
110 bool is_transient,
111 bool delay_response)
112 : min_offset(min_offset),
113 max_offset(max_offset),
114 response(response),
115 is_transient(is_transient),
116 delay_response(delay_response) {}
117
118 // static
119 TestDownloadHttpResponse::Parameters
WithSingleInterruption(const TestDownloadHttpResponse::InjectErrorCallback & inject_error_cb)120 TestDownloadHttpResponse::Parameters::WithSingleInterruption(
121 const TestDownloadHttpResponse::InjectErrorCallback& inject_error_cb) {
122 Parameters parameters;
123 parameters.injected_errors.push(parameters.size / 2);
124 parameters.inject_error_cb = inject_error_cb;
125 return parameters;
126 }
127
Parameters()128 TestDownloadHttpResponse::Parameters::Parameters()
129 : etag("abcd"),
130 last_modified("Tue, 15 Nov 1994 12:45:26 GMT"),
131 content_type("application/octet-stream"),
132 size(102400),
133 pattern_generator_seed(1),
134 support_byte_ranges(true),
135 support_partial_response(true),
136 connection_type(
137 net::HttpResponseInfo::ConnectionInfo::CONNECTION_INFO_UNKNOWN) {}
138
139 TestDownloadHttpResponse::Parameters::Parameters(const Parameters& that) =
140 default;
141 TestDownloadHttpResponse::Parameters& TestDownloadHttpResponse::Parameters::
142 operator=(const Parameters& that) = default;
143
144 TestDownloadHttpResponse::Parameters::~Parameters() = default;
145
ClearInjectedErrors()146 void TestDownloadHttpResponse::Parameters::ClearInjectedErrors() {
147 base::queue<int64_t> empty_error_list;
148 injected_errors.swap(empty_error_list);
149 inject_error_cb.Reset();
150 }
151
SetResponseForRangeRequest(int64_t min_offset,int64_t max_offset,const std::string & response,bool is_transient,bool delay_response)152 void TestDownloadHttpResponse::Parameters::SetResponseForRangeRequest(
153 int64_t min_offset,
154 int64_t max_offset,
155 const std::string& response,
156 bool is_transient,
157 bool delay_response) {
158 range_request_responses.emplace_back(HttpResponseData(
159 min_offset, max_offset, response, is_transient, delay_response));
160 }
161
CompletedRequest(const net::test_server::HttpRequest & request)162 TestDownloadHttpResponse::CompletedRequest::CompletedRequest(
163 const net::test_server::HttpRequest& request)
164 : http_request(request) {}
165
166 TestDownloadHttpResponse::CompletedRequest::~CompletedRequest() = default;
167
168 // static
StartServing(const TestDownloadHttpResponse::Parameters & parameters,const GURL & url)169 void TestDownloadHttpResponse::StartServing(
170 const TestDownloadHttpResponse::Parameters& parameters,
171 const GURL& url) {
172 base::AutoLock lock(*g_lock.Pointer());
173 auto iter = g_parameters_map.Get().find(url);
174 if (iter != g_parameters_map.Get().end())
175 g_parameters_map.Get().erase(iter);
176 g_parameters_map.Get().emplace(url, parameters);
177 }
178
179 // static
StartServingStaticResponse(const std::string & response,const GURL & url)180 void TestDownloadHttpResponse::StartServingStaticResponse(
181 const std::string& response,
182 const GURL& url) {
183 TestDownloadHttpResponse::Parameters parameters;
184 parameters.static_response = response;
185 StartServing(std::move(parameters), url);
186 }
187
188 std::unique_ptr<net::test_server::HttpResponse>
CreateResponseForTestServer()189 TestDownloadHttpResponse::CreateResponseForTestServer() {
190 return std::make_unique<HttpResponse>(weak_ptr_factory_.GetWeakPtr());
191 }
192
193 // static
GetPatternBytes(int seed,int64_t starting_offset,int length)194 std::string TestDownloadHttpResponse::GetPatternBytes(int seed,
195 int64_t starting_offset,
196 int length) {
197 int64_t seed_offset = starting_offset / sizeof(int64_t);
198 int64_t first_byte_position = starting_offset % sizeof(int64_t);
199 std::string output;
200 while (length > 0) {
201 uint64_t data = XorShift64StarWithIndex(seed, seed_offset);
202 int length_to_copy =
203 std::min(length, static_cast<int>(sizeof(data) - first_byte_position));
204 char* start_pos = reinterpret_cast<char*>(&data) + first_byte_position;
205 std::string string_to_append(start_pos, start_pos + length_to_copy);
206 output.append(string_to_append);
207 length -= length_to_copy;
208 ++seed_offset;
209 first_byte_position = 0;
210 }
211 return output;
212 }
213
TestDownloadHttpResponse(const net::test_server::HttpRequest & request,const Parameters & parameters,OnResponseSentCallback on_response_sent_callback)214 TestDownloadHttpResponse::TestDownloadHttpResponse(
215 const net::test_server::HttpRequest& request,
216 const Parameters& parameters,
217 OnResponseSentCallback on_response_sent_callback)
218 : range_(net::HttpByteRange::Bounded(0, parameters.size - 1)),
219 response_sent_offset_(0u),
220 parameters_(parameters),
221 request_(request),
222 transferred_bytes_(0u),
223 on_response_sent_callback_(std::move(on_response_sent_callback)) {
224 DCHECK_GT(parameters.size, 0) << "File size need to be greater than 0.";
225 ParseRequestHeader();
226 }
227
228 TestDownloadHttpResponse::~TestDownloadHttpResponse() = default;
229
SendResponse(const net::test_server::SendBytesCallback & send,net::test_server::SendCompleteCallback done)230 void TestDownloadHttpResponse::SendResponse(
231 const net::test_server::SendBytesCallback& send,
232 net::test_server::SendCompleteCallback done) {
233 bytes_sender_ = send;
234 done_callback_ = std::move(done);
235
236 // Throw error before sending headers.
237 if (ShouldAbortImmediately()) {
238 bytes_sender_.Run(std::string(), GenerateResultClosure());
239 return;
240 }
241
242 // Call inject error callback to UI thread.
243 if (!parameters_.injected_errors.empty() &&
244 parameters_.injected_errors.front() <= range_.last_byte_position() &&
245 parameters_.injected_errors.front() >= range_.first_byte_position() &&
246 !parameters_.inject_error_cb.is_null()) {
247 GetUIThreadTaskRunner({})->PostTask(
248 FROM_HERE, base::BindOnce(parameters_.inject_error_cb,
249 range_.first_byte_position(),
250 parameters_.injected_errors.front() -
251 range_.first_byte_position()));
252 }
253
254 // Pause before sending headers.
255 if (ShouldPauseImmediately()) {
256 PauseResponsesAndWaitForResumption();
257 return;
258 }
259
260 // Start to send the response.
261 SendResponseHeaders();
262 }
263
ParseRequestHeader()264 void TestDownloadHttpResponse::ParseRequestHeader() {
265 // Parse HTTP range header from the request.
266 std::vector<net::HttpByteRange> ranges;
267 if (request_.headers.find(net::HttpRequestHeaders::kRange) ==
268 request_.headers.end()) {
269 return;
270 }
271
272 if (!net::HttpUtil::ParseRangeHeader(
273 request_.headers.at(net::HttpRequestHeaders::kRange), &ranges)) {
274 return;
275 }
276
277 if (ranges.size() > 1)
278 LOG(WARNING) << "Multiple range intervals are not supported.";
279
280 // Adjust the response range according to request range. The first byte offset
281 // of the request may be larger than entity body size.
282 request_range_ = ranges[0];
283 if (parameters_.support_partial_response)
284 range_.set_first_byte_position(request_range_.first_byte_position());
285 range_.ComputeBounds(parameters_.size);
286
287 response_sent_offset_ = range_.first_byte_position();
288 }
289
SendResponseHeaders()290 void TestDownloadHttpResponse::SendResponseHeaders() {
291 // Send static response in |parameters_| and close connection.
292 if (!parameters_.static_response.empty()) {
293 bytes_sender_.Run(parameters_.static_response, GenerateResultClosure());
294 return;
295 }
296
297 // Send static |range_request_responses| in |parameters_| and close
298 // connection.
299 std::string response;
300 bool delay_response = false;
301 if (GetResponseForRangeRequest(&response, &delay_response)) {
302 if (delay_response) {
303 delayed_response_callback_ =
304 base::BindOnce(bytes_sender_, response, GenerateResultClosure()),
305 bytes_sender_.Run(GetDefaultResponseHeaders(), base::DoNothing());
306 } else {
307 bytes_sender_.Run(response, GenerateResultClosure());
308 }
309 return;
310 }
311
312 // Send the headers and start to send the body.
313 bytes_sender_.Run(GetDefaultResponseHeaders(), SendNextBodyChunkClosure());
314 }
315
GetDefaultResponseHeaders()316 std::string TestDownloadHttpResponse::GetDefaultResponseHeaders() {
317 std::string headers;
318 // Send partial response.
319 if (parameters_.support_partial_response && parameters_.support_byte_ranges) {
320 bool has_if_range =
321 request_.headers.find(net::HttpRequestHeaders::kIfRange) !=
322 request_.headers.end();
323 if (((has_if_range &&
324 request_.headers.at(net::HttpRequestHeaders::kIfRange) ==
325 parameters_.etag) ||
326 (!has_if_range &&
327 request_.headers.find(net::HttpRequestHeaders::kRange) !=
328 request_.headers.end())) &&
329 HandleRangeAssumingValidatorMatch(headers)) {
330 return headers;
331 }
332 }
333
334 // Send precondition failed for "If-Match" request header.
335 if (parameters_.support_partial_response && parameters_.support_byte_ranges &&
336 request_.headers.find(net::HttpRequestHeaders::kIfMatch) !=
337 request_.headers.end()) {
338 if (request_.headers.at(net::HttpRequestHeaders::kIfMatch) !=
339 parameters_.etag ||
340 !HandleRangeAssumingValidatorMatch(headers)) {
341 // Unlike If-Range, If-Match returns an error if the validators don't
342 // match.
343 headers =
344 "HTTP/1.1 412 Precondition failed\r\n"
345 "Content-Length: 0\r\n"
346 "\r\n";
347 }
348 return headers;
349 }
350
351 // Send the whole file in entity body if partial response is not supported.
352 range_.set_first_byte_position(0u);
353 range_.set_last_byte_position(parameters_.size - 1);
354 response_sent_offset_ = 0;
355
356 headers.append("HTTP/1.1 200 OK\r\n");
357 if (parameters_.support_byte_ranges)
358 headers.append("Accept-Ranges: bytes\r\n");
359 headers.append(
360 base::StringPrintf("Content-Length: %" PRId64 "\r\n", parameters_.size));
361 headers.append(GetCommonEntityHeaders());
362 return headers;
363 }
364
GetResponseForRangeRequest(std::string * output,bool * delay_response)365 bool TestDownloadHttpResponse::GetResponseForRangeRequest(
366 std::string* output,
367 bool* delay_response) {
368 if (!range_.IsValid())
369 return false;
370
371 // Find the response for range request that starts from |requset_offset|.
372 // Use default logic to generate the response if nothing can be found.
373 int64_t requset_offset = range_.first_byte_position();
374 for (auto it = parameters_.range_request_responses.begin();
375 it != parameters_.range_request_responses.end(); ++it) {
376 if (it->min_offset == -1 && it->max_offset == -1)
377 continue;
378
379 if (requset_offset < it->min_offset)
380 continue;
381
382 if (it->max_offset == -1 || requset_offset <= it->max_offset) {
383 *output = it->response;
384 *delay_response = it->delay_response;
385 // Update the global parameter for transient response, so the
386 // next response will be different.
387 if (it->is_transient) {
388 parameters_.range_request_responses.erase(it);
389 base::AutoLock lock(*g_lock.Pointer());
390 GURL url = GetURLFromRequest(request_);
391 auto iter = g_parameters_map.Get().find(url);
392 if (iter != g_parameters_map.Get().end())
393 g_parameters_map.Get().erase(iter);
394 g_parameters_map.Get().emplace(url, std::move(parameters_));
395 }
396
397 return true;
398 }
399 }
400
401 return false;
402 }
403
HandleRangeAssumingValidatorMatch(std::string & response)404 bool TestDownloadHttpResponse::HandleRangeAssumingValidatorMatch(
405 std::string& response) {
406 // The request may have specified a range that's out of bounds.
407 if (request_range_.first_byte_position() >= parameters_.size) {
408 response = base::StringPrintf(
409 "HTTP/1.1 416 Range not satisfiable\r\n"
410 "Content-Range: bytes */%" PRId64
411 "\r\n"
412 "Content-Length: 0\r\n",
413 parameters_.size);
414 return true;
415 }
416
417 response.append("HTTP/1.1 206 Partial content\r\n");
418 response.append(base::StringPrintf(
419 "Content-Range: bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n",
420 range_.first_byte_position(), range_.last_byte_position(),
421 parameters_.size));
422 response.append(base::StringPrintf(
423 "Content-Length: %" PRId64 "\r\n",
424 (range_.last_byte_position() - range_.first_byte_position()) + 1));
425 response.append(GetCommonEntityHeaders());
426 return true;
427 }
428
GetCommonEntityHeaders()429 std::string TestDownloadHttpResponse::GetCommonEntityHeaders() {
430 std::string headers;
431 if (!parameters_.content_type.empty()) {
432 headers.append(base::StringPrintf("Content-Type: %s\r\n",
433 parameters_.content_type.c_str()));
434 }
435
436 if (!parameters_.etag.empty()) {
437 headers.append(
438 base::StringPrintf("ETag: %s\r\n", parameters_.etag.c_str()));
439 }
440
441 if (!parameters_.last_modified.empty()) {
442 headers.append(base::StringPrintf("Last-Modified: %s\r\n",
443 parameters_.last_modified.c_str()));
444 }
445 headers.append("\r\n");
446 return headers;
447 }
448
GetResponseChunk(const net::HttpByteRange & buffer_range)449 std::string TestDownloadHttpResponse::GetResponseChunk(
450 const net::HttpByteRange& buffer_range) {
451 DCHECK(buffer_range.IsValid());
452 DCHECK(buffer_range.HasLastBytePosition());
453
454 int64_t length = buffer_range.last_byte_position() -
455 buffer_range.first_byte_position() + 1;
456 return GetPatternBytes(parameters_.pattern_generator_seed,
457 buffer_range.first_byte_position(), length);
458 }
459
ShouldAbortImmediately() const460 bool TestDownloadHttpResponse::ShouldAbortImmediately() const {
461 return !parameters_.injected_errors.empty() &&
462 parameters_.injected_errors.front() == -1 &&
463 !parameters_.inject_error_cb.is_null();
464 }
465
ShouldPauseImmediately() const466 bool TestDownloadHttpResponse::ShouldPauseImmediately() const {
467 return parameters_.pause_offset.has_value() &&
468 parameters_.pause_offset.value() == -1 && parameters_.on_pause_handler;
469 }
470
HandlePause(const net::HttpByteRange & buffer_range)471 bool TestDownloadHttpResponse::HandlePause(
472 const net::HttpByteRange& buffer_range) {
473 if (!parameters_.on_pause_handler || !parameters_.pause_offset.has_value())
474 return false;
475
476 int64_t pause_offset = parameters_.pause_offset.value();
477 if (pause_offset < request_range_.first_byte_position())
478 return false;
479
480 if (pause_offset > buffer_range.last_byte_position() ||
481 pause_offset < buffer_range.first_byte_position()) {
482 return false;
483 }
484
485 // Send the bytes before the pause offset.
486 net::HttpByteRange range = buffer_range;
487 if (range.last_byte_position() > pause_offset) {
488 range.set_last_byte_position(pause_offset - 1);
489 response_sent_offset_ = pause_offset;
490 SendBodyChunkInternal(range, base::DoNothing());
491 }
492
493 // Pause now. Don't close the connection to wait for resumption.
494 PauseResponsesAndWaitForResumption();
495 return true;
496 }
497
HandleInjectedError(const net::HttpByteRange & buffer_range)498 bool TestDownloadHttpResponse::HandleInjectedError(
499 const net::HttpByteRange& buffer_range) {
500 if (parameters_.injected_errors.empty())
501 return false;
502
503 // Clear all errors before first byte of |range|.
504 while (!parameters_.injected_errors.empty() &&
505 parameters_.injected_errors.front() <
506 buffer_range.first_byte_position()) {
507 parameters_.injected_errors.pop();
508 }
509
510 int64_t error_offset = parameters_.injected_errors.front();
511 if (error_offset > buffer_range.last_byte_position())
512 return false;
513
514 // Send the bytes before the error offset, then close the connection.
515 net::HttpByteRange range = buffer_range;
516 if (error_offset > buffer_range.first_byte_position()) {
517 range.set_last_byte_position(error_offset - 1);
518 DCHECK(range.IsValid());
519 response_sent_offset_ = error_offset;
520 SendBodyChunkInternal(range, GenerateResultClosure());
521 }
522
523 return true;
524 }
525
ShouldPause(const net::HttpByteRange & buffer_range) const526 bool TestDownloadHttpResponse::ShouldPause(
527 const net::HttpByteRange& buffer_range) const {
528 if (!parameters_.on_pause_handler)
529 return false;
530
531 return parameters_.pause_offset >= buffer_range.first_byte_position() &&
532 parameters_.pause_offset <= buffer_range.last_byte_position();
533 }
534
PauseResponsesAndWaitForResumption()535 void TestDownloadHttpResponse::PauseResponsesAndWaitForResumption() {
536 // Clean up the on_pause_handler so response will not be paused again.
537 base::OnceCallback<OnPauseHandler::RunType> pause_callback =
538 std::move(parameters_.on_pause_handler);
539
540 base::OnceClosure continue_closure = SendNextBodyChunkClosure();
541
542 // We may pause before sending the headers.
543 if (parameters_.pause_offset == -1) {
544 continue_closure = base::BindOnce(
545 &TestDownloadHttpResponse::SendResponseHeaders, base::Unretained(this));
546 }
547
548 // Continue to send data after resumption.
549 // TODO(xingliu): Unwind thread hopping callbacks here.
550 GetUIThreadTaskRunner({})->PostTask(
551 FROM_HERE,
552 base::BindOnce(
553 std::move(pause_callback),
554 base::BindOnce(OnResume, base::ThreadTaskRunnerHandle::Get(),
555 std::move(continue_closure))));
556 }
557
SendResponseBodyChunk()558 void TestDownloadHttpResponse::SendResponseBodyChunk() {
559 // Close the connection when reaching the end.
560 if (response_sent_offset_ > range_.last_byte_position()) {
561 GenerateResult();
562 return;
563 }
564
565 int64_t upper_bound = base::ClampToRange(response_sent_offset_ + kBufferSize,
566 range_.first_byte_position(),
567 range_.last_byte_position());
568 auto buffer_range =
569 net::HttpByteRange::Bounded(response_sent_offset_, upper_bound);
570
571 // Handle pause if needed.
572 if (HandlePause(buffer_range))
573 return;
574
575 // Handle injected error if needed.
576 if (HandleInjectedError(buffer_range))
577 return;
578
579 // Send the data buffer by buffer without throwing errors.
580 response_sent_offset_ = buffer_range.last_byte_position() + 1;
581 SendBodyChunkInternal(buffer_range, SendNextBodyChunkClosure());
582 return;
583 }
584
SendBodyChunkInternal(const net::HttpByteRange & buffer_range,base::OnceClosure next)585 void TestDownloadHttpResponse::SendBodyChunkInternal(
586 const net::HttpByteRange& buffer_range,
587 base::OnceClosure next) {
588 std::string response_chunk = GetResponseChunk(buffer_range);
589 transferred_bytes_ += static_cast<int64_t>(response_chunk.size());
590 bytes_sender_.Run(response_chunk, std::move(next));
591 }
592
593 net::test_server::SendCompleteCallback
SendNextBodyChunkClosure()594 TestDownloadHttpResponse::SendNextBodyChunkClosure() {
595 return base::BindOnce(&TestDownloadHttpResponse::SendResponseBodyChunk,
596 base::Unretained(this));
597 }
598
SendDelayedResponse()599 void TestDownloadHttpResponse::SendDelayedResponse() {
600 if (delayed_response_callback_)
601 std::move(delayed_response_callback_).Run();
602 }
603
GenerateResult()604 void TestDownloadHttpResponse::GenerateResult() {
605 auto completed_request = std::make_unique<CompletedRequest>(request_);
606 // Transferred bytes in [range_.first_byte_position(), response_sent_offset_).
607 completed_request->transferred_byte_count = transferred_bytes_;
608 OnResponseSentOnServerIOThread(std::move(on_response_sent_callback_),
609 std::move(completed_request));
610
611 // Close the HTTP connection.
612 std::move(done_callback_).Run();
613 }
614
615 net::test_server::SendCompleteCallback
GenerateResultClosure()616 TestDownloadHttpResponse::GenerateResultClosure() {
617 return base::BindOnce(&TestDownloadHttpResponse::GenerateResult,
618 base::Unretained(this));
619 }
620
621 std::unique_ptr<net::test_server::HttpResponse>
HandleTestDownloadRequest(TestDownloadHttpResponse::OnResponseSentCallback callback,const net::test_server::HttpRequest & request)622 TestDownloadResponseHandler::HandleTestDownloadRequest(
623 TestDownloadHttpResponse::OnResponseSentCallback callback,
624 const net::test_server::HttpRequest& request) {
625 server_task_runner_ = base::ThreadTaskRunnerHandle::Get();
626
627 if (request.headers.find(net::HttpRequestHeaders::kHost) ==
628 request.headers.end()) {
629 return nullptr;
630 }
631
632 base::AutoLock lock(*g_lock.Pointer());
633 GURL url = GetURLFromRequest(request);
634 auto iter = g_parameters_map.Get().find(url);
635 if (iter != g_parameters_map.Get().end()) {
636 auto test_response = std::make_unique<TestDownloadHttpResponse>(
637 request, std::move(iter->second), std::move(callback));
638 auto response = test_response->CreateResponseForTestServer();
639 responses_.emplace_back(std::move(test_response));
640 return response;
641 }
642 return nullptr;
643 }
644
645 TestDownloadResponseHandler::TestDownloadResponseHandler() = default;
646
~TestDownloadResponseHandler()647 TestDownloadResponseHandler::~TestDownloadResponseHandler() {
648 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
649 for (auto& response : responses_)
650 server_task_runner_->DeleteSoon(FROM_HERE, response.release());
651 }
652
RegisterToTestServer(net::test_server::EmbeddedTestServer * server)653 void TestDownloadResponseHandler::RegisterToTestServer(
654 net::test_server::EmbeddedTestServer* server) {
655 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
656 DCHECK(!server->Started())
657 << "Register request handler before starting the server";
658 server->RegisterRequestHandler(base::BindRepeating(
659 &content::TestDownloadResponseHandler::HandleTestDownloadRequest,
660 base::Unretained(this),
661 base::BindRepeating(
662 &content::TestDownloadResponseHandler::OnRequestCompleted,
663 base::Unretained(this))));
664 }
665
OnRequestCompleted(std::unique_ptr<TestDownloadHttpResponse::CompletedRequest> request)666 void TestDownloadResponseHandler::OnRequestCompleted(
667 std::unique_ptr<TestDownloadHttpResponse::CompletedRequest> request) {
668 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
669 completed_requests_.push_back(std::move(request));
670
671 if (run_loop_ && run_loop_->running() &&
672 completed_requests().size() >= request_count_) {
673 run_loop_->Quit();
674 }
675 }
676
WaitUntilCompletion(size_t request_count)677 void TestDownloadResponseHandler::WaitUntilCompletion(size_t request_count) {
678 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
679 request_count_ = request_count;
680
681 if ((run_loop_ && run_loop_->running()) ||
682 completed_requests().size() >= request_count_) {
683 return;
684 }
685
686 run_loop_ = std::make_unique<base::RunLoop>();
687 run_loop_->Run();
688 }
689
DispatchDelayedResponses()690 void TestDownloadResponseHandler::DispatchDelayedResponses() {
691 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
692 for (auto& response : responses_) {
693 server_task_runner_->PostTask(
694 FROM_HERE,
695 base::BindOnce(&TestDownloadHttpResponse::SendDelayedResponse,
696 base::Unretained(response.get())));
697 }
698 }
699
700 } // namespace content
701