OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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_request_handler.h" | |
6 | |
7 #include <inttypes.h> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/memory/weak_ptr.h" | |
11 #include "base/run_loop.h" | |
12 #include "base/sequenced_task_runner.h" | |
13 #include "base/strings/stringprintf.h" | |
14 #include "base/threading/sequenced_task_runner_handle.h" | |
15 #include "content/public/browser/browser_thread.h" | |
16 #include "net/base/io_buffer.h" | |
17 #include "net/http/http_request_headers.h" | |
18 #include "net/http/http_response_headers.h" | |
19 #include "net/url_request/url_request_filter.h" | |
20 #include "net/url_request/url_request_interceptor.h" | |
21 | |
22 namespace content { | |
23 | |
24 // Intercepts URLRequests on behalf of TestDownloadRequestHandler. Necessarily | |
25 // lives on the IO thread since that's where net::URLRequestFilter invokes the | |
26 // URLRequestInterceptor. | |
27 class TestDownloadRequestHandler::Interceptor | |
28 : public net::URLRequestInterceptor { | |
29 public: | |
30 // Invoked on the IO thread to register a URLRequestInterceptor for |url|. | |
31 // Returns an IO-thread bound weak pointer for interacting with the | |
32 // interceptor. | |
33 static base::WeakPtr<Interceptor> Register( | |
34 const GURL& url, | |
35 scoped_refptr<base::SequencedTaskRunner> client_task_runner); | |
36 | |
37 using JobFactory = | |
38 base::Callback<net::URLRequestJob*(net::URLRequest*, | |
39 net::NetworkDelegate*, | |
40 base::WeakPtr<Interceptor>)>; | |
41 | |
42 ~Interceptor() override; | |
43 | |
44 // Unregisters the URLRequestInterceptor. In reality it unregisters whatever | |
45 // was registered to intercept |url_|. Since |this| is owned by | |
46 // net::URLRequestFilter, unregistering the interceptor deletes |this| as a | |
47 // side-effect. | |
48 void Unregister(); | |
49 | |
50 // Change the URLRequestJob factory. Can be called multiple times. | |
51 void SetJobFactory(const JobFactory& factory); | |
52 | |
53 // Sets |requests| to the vector of completed requests and clears the internal | |
54 // list. The returned requests are stored in the order in which they were | |
55 // reported as being complete (not necessarily the order in which they were | |
56 // received). | |
57 void GetAndResetCompletedRequests( | |
58 TestDownloadRequestHandler::CompletedRequests* requests); | |
59 | |
60 // Can be called by a URLRequestJob to notify this interceptor of a completed | |
61 // request. | |
62 void AddCompletedRequest( | |
63 const TestDownloadRequestHandler::CompletedRequest& request); | |
64 | |
65 // Returns the task runner that should be used for invoking any client | |
66 // supplied callbacks. | |
67 scoped_refptr<base::SequencedTaskRunner> GetClientTaskRunner(); | |
68 | |
69 private: | |
70 Interceptor(const GURL& url, | |
71 scoped_refptr<base::SequencedTaskRunner> client_task_runner); | |
72 | |
73 // net::URLRequestInterceptor | |
74 net::URLRequestJob* MaybeInterceptRequest( | |
75 net::URLRequest* request, | |
76 net::NetworkDelegate* network_delegate) const override; | |
77 | |
78 TestDownloadRequestHandler::CompletedRequests completed_requests_; | |
79 GURL url_; | |
80 JobFactory job_factory_; | |
81 scoped_refptr<base::SequencedTaskRunner> client_task_runner_; | |
82 | |
83 // mutable because MaybeInterceptRequest() is inexplicably const. | |
84 mutable base::WeakPtrFactory<Interceptor> weak_ptr_factory_; | |
85 DISALLOW_COPY_AND_ASSIGN(Interceptor); | |
86 }; | |
87 | |
88 // A URLRequestJob that constructs a response to a URLRequest based on the | |
89 // contents of a Parameters object. It can handle partial (i.e. byte range | |
90 // requests). Created on and lives on the IO thread. | |
91 class TestDownloadRequestHandler::PartialResponseJob | |
92 : public net::URLRequestJob { | |
93 public: | |
94 static net::URLRequestJob* Factory(const Parameters& parameters, | |
95 net::URLRequest* request, | |
96 net::NetworkDelegate* delegate, | |
97 base::WeakPtr<Interceptor> interceptor); | |
98 | |
99 // URLRequestJob | |
100 void Start() override; | |
101 void GetResponseInfo(net::HttpResponseInfo* response_info) override; | |
102 int64 GetTotalReceivedBytes() const override; | |
103 bool GetMimeType(std::string* mime_type) const override; | |
104 int GetResponseCode() const override; | |
105 bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read) override; | |
106 | |
107 private: | |
108 PartialResponseJob(scoped_ptr<Parameters> parameters, | |
109 base::WeakPtr<Interceptor> interceptor, | |
110 net::URLRequest* url_request, | |
111 net::NetworkDelegate* network_delegate); | |
112 | |
113 ~PartialResponseJob() override; | |
114 void ReportCompletedRequest(int64_t transferred_byte_count); | |
115 static void OnStartResponseCallbackOnPossiblyIncorrectThread( | |
116 base::WeakPtr<PartialResponseJob> job, | |
117 const std::string& headers, | |
118 net::Error error); | |
119 void OnStartResponseCallback(const std::string& headers, net::Error error); | |
120 | |
121 // In general, the Parameters object can specify an explicit OnStart handler. | |
122 // In its absence or if the explicit OnStart handler requests the default | |
123 // behavior, this method can be invoked to respond to the request based on the | |
124 // remaining Parameters fields (as if there was no OnStart handler). | |
125 void HandleOnStartDefault(); | |
126 | |
127 // Respond Start() assuming that any If-Match or If-Range headers have been | |
128 // successfully validated. This handler assumes that there *must* be a Range | |
129 // header even though the spec doesn't strictly require it for If-Match. | |
130 bool HandleRangeAssumingValidatorMatch(); | |
131 | |
132 // Adds headers that describe the entity (Content-Type, ETag, Last-Modified). | |
133 // It also adds an 'Accept-Ranges' header if appropriate. | |
134 void AddCommonEntityHeaders(); | |
135 | |
136 // Schedules NotifyHeadersComplete() to be called and sets | |
137 // offset_of_next_read_ to begin reading. Since this interceptor is avoiding | |
138 // network requests, it schedules the NotifyHeadersComplete() call | |
139 // asynchronously in order to avoid unexpected re-entrancy. | |
140 void NotifyHeadersCompleteAndPrepareToRead(); | |
141 | |
142 scoped_ptr<Parameters> parameters_; | |
143 | |
144 base::WeakPtr<Interceptor> interceptor_; | |
145 net::HttpResponseInfo response_info_; | |
146 int64_t offset_of_next_read_ = -1; | |
147 int64_t requested_range_begin_ = -1; | |
148 int64_t requested_range_end_ = -1; | |
149 base::WeakPtrFactory<PartialResponseJob> weak_factory_; | |
150 | |
151 DISALLOW_COPY_AND_ASSIGN(PartialResponseJob); | |
152 }; | |
153 | |
154 namespace { | |
155 | |
156 template <class T> | |
157 void StoreValueAndInvokeClosure(const base::Closure& closure, | |
158 T* value_receiver, | |
159 T value) { | |
160 *value_receiver = value; | |
161 closure.Run(); | |
162 } | |
163 | |
164 // Xorshift* PRNG from https://en.wikipedia.org/wiki/Xorshift | |
165 uint64_t XorShift64StarWithIndex(uint64_t seed, uint64_t index) { | |
166 const uint64_t kMultiplier = UINT64_C(2685821657736338717); | |
167 uint64_t x = seed * kMultiplier + index; | |
168 x ^= x >> 12; | |
169 x ^= x << 25; | |
170 x ^= x >> 27; | |
171 return x * kMultiplier; | |
172 } | |
173 | |
174 void RespondToOnStartedCallbackWithStaticHeaders( | |
175 const std::string& headers, | |
176 const net::HttpRequestHeaders&, | |
177 const TestDownloadRequestHandler::OnStartResponseCallback& callback) { | |
178 callback.Run(headers, net::OK); | |
179 } | |
180 | |
181 GURL GetNextURLForDownloadInterceptor() { | |
182 static int index = 0; | |
183 std::string url_string = | |
184 base::StringPrintf("https://%d.default.example.com/download/", ++index); | |
185 return GURL(url_string); | |
186 } | |
187 | |
188 scoped_refptr<net::HttpResponseHeaders> HeadersFromString( | |
189 const std::string& headers_string) { | |
190 scoped_refptr<net::HttpResponseHeaders> headers = | |
191 new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( | |
192 headers_string.c_str(), headers_string.size())); | |
193 return headers; | |
194 } | |
195 | |
196 } // namespace | |
197 | |
198 // static | |
199 net::URLRequestJob* TestDownloadRequestHandler::PartialResponseJob::Factory( | |
200 const Parameters& parameters, | |
201 net::URLRequest* request, | |
202 net::NetworkDelegate* delegate, | |
203 base::WeakPtr<Interceptor> interceptor) { | |
204 return new PartialResponseJob(make_scoped_ptr(new Parameters(parameters)), | |
205 interceptor, request, delegate); | |
206 } | |
207 | |
208 TestDownloadRequestHandler::PartialResponseJob::PartialResponseJob( | |
209 scoped_ptr<Parameters> parameters, | |
210 base::WeakPtr<Interceptor> interceptor, | |
211 net::URLRequest* request, | |
212 net::NetworkDelegate* network_delegate) | |
213 : net::URLRequestJob(request, network_delegate), | |
214 parameters_(parameters.Pass()), | |
215 interceptor_(interceptor), | |
216 weak_factory_(this) { | |
217 DCHECK(parameters_.get()); | |
218 DCHECK_LT(0, parameters_->size); | |
219 DCHECK_NE(-1, parameters_->pattern_generator_seed); | |
220 } | |
221 | |
222 TestDownloadRequestHandler::PartialResponseJob::~PartialResponseJob() {} | |
223 | |
224 void TestDownloadRequestHandler::PartialResponseJob::Start() { | |
225 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
226 DVLOG(1) << "Starting request for " << request()->url().spec(); | |
227 | |
228 if (parameters_->on_start_handler.is_null() || !interceptor_.get()) { | |
229 HandleOnStartDefault(); | |
230 return; | |
231 } | |
232 | |
233 DVLOG(1) << "Invoking custom OnStart handler."; | |
234 interceptor_->GetClientTaskRunner()->PostTask( | |
Randy Smith (Not in Mondays)
2015/11/28 00:38:19
Comment somewhere (here or at declaration of inter
asanka
2015/11/30 18:00:10
It's not safe since they are not lifetime-linked.
Randy Smith (Not in Mondays)
2015/11/30 18:38:24
Ah, whoops, missed that. Thanks.
| |
235 FROM_HERE, | |
236 base::Bind( | |
237 parameters_->on_start_handler, request()->extra_request_headers(), | |
238 base::Bind(&PartialResponseJob:: | |
239 OnStartResponseCallbackOnPossiblyIncorrectThread, | |
240 weak_factory_.GetWeakPtr()))); | |
241 } | |
242 | |
243 void TestDownloadRequestHandler::PartialResponseJob::GetResponseInfo( | |
244 net::HttpResponseInfo* response_info) { | |
245 *response_info = response_info_; | |
246 } | |
247 | |
248 int64 TestDownloadRequestHandler::PartialResponseJob::GetTotalReceivedBytes() | |
249 const { | |
250 return offset_of_next_read_ - requested_range_begin_; | |
251 } | |
252 | |
253 bool TestDownloadRequestHandler::PartialResponseJob::GetMimeType( | |
254 std::string* mime_type) const { | |
255 *mime_type = parameters_->content_type; | |
256 return !parameters_->content_type.empty(); | |
257 } | |
258 | |
259 int TestDownloadRequestHandler::PartialResponseJob::GetResponseCode() const { | |
260 return response_info_.headers.get() ? response_info_.headers->response_code() | |
261 : 0; | |
262 } | |
263 | |
264 bool TestDownloadRequestHandler::PartialResponseJob::ReadRawData( | |
265 net::IOBuffer* buf, | |
266 int buf_size, | |
267 int* bytes_read) { | |
268 *bytes_read = 0; | |
269 DVLOG(1) << "Preparing to read " << buf_size << " bytes"; | |
270 | |
271 // requested_range_begin_ == -1 implies that the body was empty. | |
272 if (offset_of_next_read_ > requested_range_end_ || | |
273 requested_range_begin_ == -1) { | |
274 ReportCompletedRequest(requested_range_end_ - requested_range_begin_ + 1); | |
275 DVLOG(1) << "Done reading."; | |
276 return true; | |
277 } | |
278 | |
279 int64_t range_end = | |
280 std::min(requested_range_end_, offset_of_next_read_ + buf_size - 1); | |
281 | |
282 if (!parameters_->injected_errors.empty()) { | |
283 const InjectedError& injected_error = parameters_->injected_errors.front(); | |
284 | |
285 if (offset_of_next_read_ == injected_error.offset) { | |
286 int error = injected_error.error; | |
287 SetStatus(net::URLRequestStatus(net::URLRequestStatus::FAILED, error)); | |
288 DVLOG(1) << "Returning error " << net::ErrorToString(error); | |
289 ReportCompletedRequest(injected_error.offset - requested_range_begin_); | |
290 parameters_->injected_errors.pop(); | |
291 return false; | |
292 } | |
293 | |
294 if (offset_of_next_read_ < injected_error.offset && | |
295 injected_error.offset <= range_end) | |
296 range_end = injected_error.offset - 1; | |
297 } | |
298 int bytes_to_copy = (range_end - offset_of_next_read_) + 1; | |
299 | |
300 TestDownloadRequestHandler::GetPatternBytes( | |
301 parameters_->pattern_generator_seed, offset_of_next_read_, bytes_to_copy, | |
302 buf->data()); | |
303 DVLOG(1) << "Read " << bytes_to_copy << " bytes at offset " | |
304 << offset_of_next_read_; | |
305 offset_of_next_read_ += bytes_to_copy; | |
306 *bytes_read = bytes_to_copy; | |
307 return true; | |
308 } | |
309 | |
310 void TestDownloadRequestHandler::PartialResponseJob::ReportCompletedRequest( | |
311 int64_t transferred_byte_count) { | |
312 if (interceptor_.get()) { | |
313 TestDownloadRequestHandler::CompletedRequest completed_request; | |
314 completed_request.transferred_byte_count = transferred_byte_count; | |
315 completed_request.request_headers = request()->extra_request_headers(); | |
316 interceptor_->AddCompletedRequest(completed_request); | |
317 } | |
318 } | |
319 | |
320 // static | |
321 void TestDownloadRequestHandler::PartialResponseJob:: | |
322 OnStartResponseCallbackOnPossiblyIncorrectThread( | |
323 base::WeakPtr<PartialResponseJob> job, | |
324 const std::string& headers, | |
325 net::Error error) { | |
326 BrowserThread::PostTask( | |
327 BrowserThread::IO, FROM_HERE, | |
328 base::Bind(&PartialResponseJob::OnStartResponseCallback, job, headers, | |
329 error)); | |
330 } | |
331 | |
332 void TestDownloadRequestHandler::PartialResponseJob::OnStartResponseCallback( | |
333 const std::string& headers, | |
334 net::Error error) { | |
335 DVLOG(1) << "OnStartResponse invoked with error:" << error | |
336 << " and headers:" << std::endl | |
337 << headers; | |
338 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
339 if (headers.empty() && error == net::OK) { | |
340 HandleOnStartDefault(); | |
341 return; | |
342 } | |
343 | |
344 if (error != net::OK) { | |
345 NotifyStartError(net::URLRequestStatus::FromError(error)); | |
346 return; | |
347 } | |
348 | |
349 response_info_.headers = new net::HttpResponseHeaders( | |
350 net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size())); | |
351 NotifyHeadersCompleteAndPrepareToRead(); | |
352 } | |
353 | |
354 void TestDownloadRequestHandler::PartialResponseJob::HandleOnStartDefault() { | |
355 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
356 SetStatus(net::URLRequestStatus()); | |
357 | |
358 const net::HttpRequestHeaders& extra_headers = | |
359 request()->extra_request_headers(); | |
360 | |
361 DCHECK(request()->method() == "GET") << "PartialResponseJob only " | |
362 "knows how to respond to GET " | |
363 "requests"; | |
364 std::string value; | |
365 | |
366 // If the request contains an 'If-Range' header and the value matches our | |
367 // ETag, then try to handle the range request. | |
368 if (parameters_->support_byte_ranges && | |
369 extra_headers.GetHeader(net::HttpRequestHeaders::kIfRange, &value) && | |
370 value == parameters_->etag && HandleRangeAssumingValidatorMatch()) | |
371 return; | |
372 | |
373 if (parameters_->support_byte_ranges && | |
374 extra_headers.GetHeader("If-Match", &value)) { | |
375 if (value == parameters_->etag && HandleRangeAssumingValidatorMatch()) | |
376 return; | |
377 | |
378 // Unlike If-Range, If-Match returns an error if the validators don't match. | |
379 response_info_.headers = HeadersFromString( | |
380 "HTTP/1.1 412 Precondition failed\r\n" | |
381 "Content-Length: 0\r\n"); | |
382 requested_range_begin_ = requested_range_end_ = -1; | |
383 NotifyHeadersCompleteAndPrepareToRead(); | |
384 return; | |
385 } | |
386 | |
387 requested_range_begin_ = 0; | |
388 requested_range_end_ = parameters_->size - 1; | |
389 response_info_.headers = | |
390 HeadersFromString(base::StringPrintf("HTTP/1.1 200 Success\r\n" | |
391 "Content-Length: %" PRId64 "\r\n", | |
392 parameters_->size)); | |
393 AddCommonEntityHeaders(); | |
394 NotifyHeadersCompleteAndPrepareToRead(); | |
395 return; | |
396 } | |
397 | |
398 bool TestDownloadRequestHandler::PartialResponseJob:: | |
399 HandleRangeAssumingValidatorMatch() { | |
400 const net::HttpRequestHeaders& extra_headers = | |
401 request()->extra_request_headers(); | |
402 | |
403 std::string range_header; | |
404 std::vector<net::HttpByteRange> byte_ranges; | |
405 | |
406 // There needs to be a 'Range' header and it should have exactly one range. | |
407 // This server is not going to deal with multiple ranges. | |
408 if (!extra_headers.GetHeader(net::HttpRequestHeaders::kRange, | |
409 &range_header) || | |
410 !net::HttpUtil::ParseRangeHeader(range_header, &byte_ranges) || | |
411 byte_ranges.size() != 1) | |
412 return false; | |
413 | |
414 // The request may have specified a range that's out of bounds. | |
415 if (!byte_ranges[0].ComputeBounds(parameters_->size)) { | |
416 response_info_.headers = HeadersFromString( | |
417 base::StringPrintf("HTTP/1.1 416 Range not satisfiable\r\n" | |
418 "Content-Range: bytes */%" PRId64 "\r\n" | |
419 "Content-Length: 0\r\n", | |
420 parameters_->size)); | |
421 requested_range_begin_ = requested_range_end_ = -1; | |
422 NotifyHeadersCompleteAndPrepareToRead(); | |
423 return true; | |
424 } | |
425 | |
426 requested_range_begin_ = byte_ranges[0].first_byte_position(); | |
427 requested_range_end_ = byte_ranges[0].last_byte_position(); | |
428 response_info_.headers = | |
429 HeadersFromString(base::StringPrintf( | |
430 "HTTP/1.1 206 Partial content\r\n" | |
431 "Content-Range: bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n" | |
432 "Content-Length: %" PRId64 "\r\n", | |
433 requested_range_begin_, requested_range_end_, parameters_->size, | |
434 (requested_range_end_ - requested_range_begin_) + 1)); | |
435 AddCommonEntityHeaders(); | |
436 NotifyHeadersCompleteAndPrepareToRead(); | |
437 return true; | |
438 } | |
439 | |
440 void TestDownloadRequestHandler::PartialResponseJob::AddCommonEntityHeaders() { | |
441 if (parameters_->support_byte_ranges) | |
442 response_info_.headers->AddHeader("Accept-Ranges: bytes"); | |
443 | |
444 if (!parameters_->content_type.empty()) | |
445 response_info_.headers->AddHeader(base::StringPrintf( | |
446 "Content-Type: %s", parameters_->content_type.c_str())); | |
447 | |
448 if (!parameters_->etag.empty()) | |
449 response_info_.headers->AddHeader( | |
450 base::StringPrintf("ETag: %s", parameters_->etag.c_str())); | |
451 | |
452 if (!parameters_->last_modified.empty()) | |
453 response_info_.headers->AddHeader(base::StringPrintf( | |
454 "Last-Modified: %s", parameters_->last_modified.c_str())); | |
455 } | |
456 | |
457 void TestDownloadRequestHandler::PartialResponseJob:: | |
458 NotifyHeadersCompleteAndPrepareToRead() { | |
459 std::string normalized_headers; | |
460 response_info_.headers->GetNormalizedHeaders(&normalized_headers); | |
461 DVLOG(1) << "Notify ready with headers:\n" << normalized_headers; | |
462 | |
463 offset_of_next_read_ = requested_range_begin_; | |
464 | |
465 // Flush out injected_errors that no longer apply. We are going to skip over | |
466 // ones where the |offset| == |requested_range_begin_| as well. While it | |
467 // prevents injecting an error at offset 0, it makes it much easier to set up | |
468 // parameter sets for download resumption. It means that when a request is | |
469 // interrupted at offset O, a subsequent request for the range O-<end> won't | |
470 // immediately interrupt as well. If we don't exclude interruptions at | |
471 // relative offset 0, then test writers would need to reset the parameter | |
472 // prior to each resumption rather than once at the beginning of the test. | |
473 while (!parameters_->injected_errors.empty() && | |
474 parameters_->injected_errors.front().offset <= requested_range_begin_) | |
475 parameters_->injected_errors.pop(); | |
476 | |
477 base::MessageLoop::current()->PostTask( | |
478 FROM_HERE, base::Bind(&PartialResponseJob::NotifyHeadersComplete, | |
479 weak_factory_.GetWeakPtr())); | |
480 } | |
481 | |
482 // static | |
483 base::WeakPtr<TestDownloadRequestHandler::Interceptor> | |
484 TestDownloadRequestHandler::Interceptor::Register( | |
485 const GURL& url, | |
486 scoped_refptr<base::SequencedTaskRunner> client_task_runner) { | |
487 DCHECK(url.is_valid()); | |
488 scoped_ptr<Interceptor> interceptor(new Interceptor(url, client_task_runner)); | |
489 base::WeakPtr<Interceptor> weak_reference = | |
490 interceptor->weak_ptr_factory_.GetWeakPtr(); | |
491 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); | |
492 filter->AddUrlInterceptor(url, interceptor.Pass()); | |
493 return weak_reference; | |
494 } | |
495 | |
496 void TestDownloadRequestHandler::Interceptor::Unregister() { | |
497 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); | |
498 filter->RemoveUrlHandler(url_); | |
499 // We are deleted now since the filter owned |this|. | |
500 } | |
501 | |
502 void TestDownloadRequestHandler::Interceptor::SetJobFactory( | |
503 const JobFactory& job_factory) { | |
504 job_factory_ = job_factory; | |
505 } | |
506 | |
507 void TestDownloadRequestHandler::Interceptor::GetAndResetCompletedRequests( | |
508 TestDownloadRequestHandler::CompletedRequests* requests) { | |
509 requests->clear(); | |
510 completed_requests_.swap(*requests); | |
511 } | |
512 | |
513 void TestDownloadRequestHandler::Interceptor::AddCompletedRequest( | |
514 const TestDownloadRequestHandler::CompletedRequest& request) { | |
515 completed_requests_.push_back(request); | |
516 } | |
517 | |
518 scoped_refptr<base::SequencedTaskRunner> | |
519 TestDownloadRequestHandler::Interceptor::GetClientTaskRunner() { | |
520 return client_task_runner_; | |
521 } | |
522 | |
523 TestDownloadRequestHandler::Interceptor::Interceptor( | |
524 const GURL& url, | |
525 scoped_refptr<base::SequencedTaskRunner> client_task_runner) | |
526 : url_(url), | |
527 client_task_runner_(client_task_runner), | |
528 weak_ptr_factory_(this) {} | |
529 | |
530 TestDownloadRequestHandler::Interceptor::~Interceptor() {} | |
531 | |
532 net::URLRequestJob* | |
533 TestDownloadRequestHandler::Interceptor::MaybeInterceptRequest( | |
534 net::URLRequest* request, | |
535 net::NetworkDelegate* network_delegate) const { | |
536 DVLOG(1) << "Intercepting request for " << request->url() | |
537 << " with headers:\n" << request->extra_request_headers().ToString(); | |
538 if (job_factory_.is_null()) | |
539 return nullptr; | |
540 return job_factory_.Run(request, network_delegate, | |
541 weak_ptr_factory_.GetWeakPtr()); | |
542 } | |
543 | |
544 TestDownloadRequestHandler::InjectedError::InjectedError(int64_t offset, | |
545 net::Error error) | |
546 : offset(offset), error(error) {} | |
547 | |
548 // static | |
549 TestDownloadRequestHandler::Parameters | |
550 TestDownloadRequestHandler::Parameters::WithSingleInterruption() { | |
551 Parameters parameters; | |
552 parameters.injected_errors.push( | |
553 InjectedError(parameters.size / 2, net::ERR_CONNECTION_RESET)); | |
554 return parameters; | |
555 } | |
556 | |
557 TestDownloadRequestHandler::Parameters::Parameters() | |
558 : etag("abcd"), | |
559 last_modified("Tue, 15 Nov 1994 12:45:26 GMT"), | |
560 content_type("application/octet-stream"), | |
561 size(102400), | |
562 pattern_generator_seed(1), | |
563 support_byte_ranges(true) {} | |
564 | |
565 // Copy and move constructors / assignment operators are all defaults. | |
566 TestDownloadRequestHandler::Parameters::Parameters(Parameters&&) = default; | |
567 TestDownloadRequestHandler::Parameters::Parameters(const Parameters&) = default; | |
568 TestDownloadRequestHandler::Parameters& TestDownloadRequestHandler::Parameters:: | |
569 operator=(Parameters&&) = default; | |
570 TestDownloadRequestHandler::Parameters& TestDownloadRequestHandler::Parameters:: | |
571 operator=(const Parameters&) = default; | |
572 | |
573 TestDownloadRequestHandler::Parameters::~Parameters() {} | |
574 | |
575 void TestDownloadRequestHandler::Parameters::ClearInjectedErrors() { | |
576 std::queue<InjectedError> empty_error_list; | |
577 injected_errors.swap(empty_error_list); | |
578 } | |
579 | |
580 TestDownloadRequestHandler::TestDownloadRequestHandler() | |
581 : TestDownloadRequestHandler(GetNextURLForDownloadInterceptor()) {} | |
582 | |
583 TestDownloadRequestHandler::TestDownloadRequestHandler(const GURL& url) | |
584 : url_(url) { | |
585 DCHECK(base::SequencedTaskRunnerHandle::IsSet()); | |
586 base::RunLoop run_loop; | |
587 BrowserThread::PostTaskAndReplyWithResult( | |
588 BrowserThread::IO, FROM_HERE, | |
589 base::Bind(&Interceptor::Register, url_, | |
590 base::SequencedTaskRunnerHandle::Get()), | |
591 base::Bind(&StoreValueAndInvokeClosure<base::WeakPtr<Interceptor>>, | |
592 run_loop.QuitClosure(), &interceptor_)); | |
593 run_loop.Run(); | |
594 } | |
595 | |
596 void TestDownloadRequestHandler::StartServing(const Parameters& parameters) { | |
597 DCHECK(CalledOnValidThread()); | |
598 Interceptor::JobFactory job_factory = | |
599 base::Bind(&PartialResponseJob::Factory, parameters); | |
600 // Interceptor, if valid, is already registered and serving requests. We just | |
601 // need to set the correct job factory for it to start serving using the new | |
602 // parameters. | |
603 BrowserThread::PostTask( | |
604 BrowserThread::IO, FROM_HERE, | |
605 base::Bind(&Interceptor::SetJobFactory, interceptor_, job_factory)); | |
606 } | |
607 | |
608 void TestDownloadRequestHandler::StartServingStaticResponse( | |
609 const base::StringPiece& headers) { | |
610 DCHECK(CalledOnValidThread()); | |
611 Parameters parameters; | |
612 parameters.on_start_handler = base::Bind( | |
613 &RespondToOnStartedCallbackWithStaticHeaders, headers.as_string()); | |
614 StartServing(parameters); | |
615 } | |
616 | |
617 // static | |
618 void TestDownloadRequestHandler::GetPatternBytes(int seed, | |
619 int64_t starting_offset, | |
620 int length, | |
621 char* buffer) { | |
622 int64_t seed_offset = starting_offset / sizeof(int64_t); | |
623 int64_t first_byte_position = starting_offset % sizeof(int64_t); | |
624 while (length > 0) { | |
625 uint64_t data = XorShift64StarWithIndex(seed, seed_offset); | |
626 int length_to_copy = | |
627 std::min(length, static_cast<int>(sizeof(data) - first_byte_position)); | |
628 memcpy(buffer, reinterpret_cast<char*>(&data) + first_byte_position, | |
629 length_to_copy); | |
630 buffer += length_to_copy; | |
631 length -= length_to_copy; | |
632 ++seed_offset; | |
633 first_byte_position = 0; | |
634 } | |
635 } | |
636 | |
637 TestDownloadRequestHandler::~TestDownloadRequestHandler() { | |
638 DCHECK(CalledOnValidThread()); | |
639 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
640 base::Bind(&Interceptor::Unregister, interceptor_)); | |
641 } | |
642 | |
643 void TestDownloadRequestHandler::GetCompletedRequestInfo( | |
644 TestDownloadRequestHandler::CompletedRequests* requests) { | |
645 DCHECK(CalledOnValidThread()); | |
646 base::RunLoop run_loop; | |
647 BrowserThread::PostTaskAndReply( | |
648 BrowserThread::IO, FROM_HERE, | |
649 base::Bind(&Interceptor::GetAndResetCompletedRequests, interceptor_, | |
650 requests), | |
651 run_loop.QuitClosure()); | |
652 run_loop.Run(); | |
653 } | |
654 | |
655 } // namespace content | |
OLD | NEW |