Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: content/public/test/test_download_request_handler.cc

Issue 1203983004: Stop using SpawnedTestServer in DownloadContentTest.* (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 "base/logging.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/run_loop.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "net/base/io_buffer.h"
12 #include "net/http/http_request_headers.h"
13 #include "net/http/http_response_headers.h"
14 #include "net/url_request/url_request_filter.h"
15 #include "net/url_request/url_request_interceptor.h"
16
17 namespace content {
18
19 namespace {
20
21 using Parameters = TestDownloadRequestHandler::Parameters;
22 using InjectedError = TestDownloadRequestHandler::InjectedError;
23
24 class Interceptor : public net::URLRequestInterceptor {
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 nit, suggestion: Use a more fully qualified name s
asanka 2015/11/13 21:40:01 Done.
25 public:
26 static base::WeakPtr<Interceptor> Register(const GURL& url);
27
28 using JobFactory =
29 base::Callback<net::URLRequestJob*(net::URLRequest*,
30 net::NetworkDelegate*,
31 base::WeakPtr<Interceptor>)>;
32
33 ~Interceptor() override;
34 void Unregister();
35 void SetJobFactory(const JobFactory& factory);
36 void GetAndResetCompletedRequests(
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 General comment throughout this file: Comments as
asanka 2015/11/13 21:40:01 Done.
37 TestDownloadRequestHandler::CompletedRequests* requests);
38 void AddCompletedRequest(
39 const TestDownloadRequestHandler::CompletedRequest& request_info);
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 nit: Use parallel variable names for these two fun
asanka 2015/11/13 21:40:01 Done.
40
41 private:
42 Interceptor(const GURL& url);
43
44 // net::URLRequestInterceptor
45 net::URLRequestJob* MaybeInterceptRequest(
46 net::URLRequest* request,
47 net::NetworkDelegate* network_delegate) const override;
48
49 TestDownloadRequestHandler::CompletedRequests completed_requests_;
50 GURL url_;
51 JobFactory job_factory_;
52 mutable base::WeakPtrFactory<Interceptor> weak_ptr_factory_;
53 DISALLOW_COPY_AND_ASSIGN(Interceptor);
54 };
55
56 class PartialResponseJob : public net::URLRequestJob {
57 public:
58 static net::URLRequestJob* Factory(const Parameters& parameters,
59 net::URLRequest* request,
60 net::NetworkDelegate* delegate,
61 base::WeakPtr<Interceptor> interceptor);
62
63 // URLRequestJob
64 void Start() override;
65 void GetResponseInfo(net::HttpResponseInfo* response_info) override;
66 int64 GetTotalReceivedBytes() const override;
67 bool GetMimeType(std::string* mime_type) const override;
68 int GetResponseCode() const override;
69 bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read) override;
70
71 private:
72 PartialResponseJob(scoped_ptr<Parameters> parameters,
73 base::WeakPtr<Interceptor> interceptor,
74 net::URLRequest* url_request,
75 net::NetworkDelegate* network_delegate);
76
77 ~PartialResponseJob() override;
78 void ReportCompletedRequest(int64_t transferred_content_length);
79
80 scoped_ptr<Parameters> parameters_;
81
82 base::WeakPtr<Interceptor> interceptor_;
83 net::HttpResponseInfo response_info_;
84 int64_t offset_ = -1;
85 int64_t offset_begin_ = -1;
86 int64_t offset_end_ = -1;
87 base::WeakPtrFactory<PartialResponseJob> weak_factory_;
88
89 DISALLOW_COPY_AND_ASSIGN(PartialResponseJob);
90 };
91
92 class StaticResponseJob : public net::URLRequestJob {
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 nit: Single line comment as to purpose? (In the c
asanka 2015/11/13 21:40:02 This got removed.
93 public:
94 static net::URLRequestJob* Factory(const std::string& headers,
95 net::URLRequest* request,
96 net::NetworkDelegate* delegate,
97 base::WeakPtr<Interceptor> interceptor);
98 // URLRequestJob
99 void Start() override;
100 void GetResponseInfo(net::HttpResponseInfo* response_info) override;
101
102 private:
103 StaticResponseJob(const std::string& headers,
104 net::URLRequest* url_request,
105 net::NetworkDelegate* network_delegate);
106 ~StaticResponseJob() override;
107
108 net::HttpResponseInfo response_info_;
109 base::WeakPtrFactory<StaticResponseJob> weak_factory_;
110 DISALLOW_COPY_AND_ASSIGN(StaticResponseJob);
111 };
112
113 // static
114 net::URLRequestJob* PartialResponseJob::Factory(
115 const Parameters& parameters,
116 net::URLRequest* request,
117 net::NetworkDelegate* delegate,
118 base::WeakPtr<Interceptor> interceptor) {
119 return new PartialResponseJob(make_scoped_ptr(new Parameters(parameters)),
120 interceptor, request, delegate);
121 }
122
123 PartialResponseJob::PartialResponseJob(scoped_ptr<Parameters> parameters,
124 base::WeakPtr<Interceptor> interceptor,
125 net::URLRequest* request,
126 net::NetworkDelegate* network_delegate)
127 : net::URLRequestJob(request, network_delegate),
128 parameters_(parameters.Pass()),
129 interceptor_(interceptor),
130 weak_factory_(this) {
131 DCHECK(parameters_.get());
132 DCHECK_LT(0, parameters_->size);
133 DCHECK_NE(-1, parameters_->pattern_generator_seed);
134 }
135
136 PartialResponseJob::~PartialResponseJob() {}
137
138 void PartialResponseJob::Start() {
139 DVLOG(1) << "Starting request for " << request()->url().spec();
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 Just confirming that you wanted to land the log en
asanka 2015/11/13 21:40:01 Yeah. They are D* logs that only result in code fo
140 SetStatus(net::URLRequestStatus());
141 std::stringstream response_headers;
142
143 const net::HttpRequestHeaders extra_headers =
144 request()->extra_request_headers();
145
146 DCHECK(request()->method() == "GET") << "PartialResponseJob only "
147 "knows how to respond to GET "
148 "requests";
149 offset_begin_ = 0;
150 offset_end_ = parameters_->size - 1;
151 std::string value;
152 std::string range_header;
153 std::vector<net::HttpByteRange> byte_ranges;
154
155 if (parameters_->support_byte_ranges &&
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 I feel like a comment or two would go a long way h
asanka 2015/11/13 21:40:02 Yup. It looks a bit dense now that I look at it ag
156 extra_headers.GetHeader(net::HttpRequestHeaders::kIfRange, &value) &&
157 !value.empty() && value == parameters_->etag &&
158 extra_headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) &&
159 net::HttpUtil::ParseRangeHeader(range_header, &byte_ranges) &&
160 byte_ranges.size() > 0) {
161 if (byte_ranges[0].ComputeBounds(parameters_->size)) {
162 offset_begin_ = byte_ranges[0].first_byte_position();
163 offset_end_ = byte_ranges[0].last_byte_position();
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 Does this need to be the min of the parameters_->s
asanka 2015/11/13 21:40:02 The HttpByteRange::ComputeBounds() method takes ca
164 response_headers << "HTTP/1.1 206 Partial content\r\n";
165 response_headers << "Content-Range: bytes " << offset_begin_ << "-"
166 << offset_end_ << "/" << parameters_->size << "\r\n";
167 response_headers << "Content-Length: " << offset_end_ - offset_begin_ + 1
168 << "\r\n";
169 } else {
170 response_headers << "HTTP/1.1 416 Range not satisfiable\r\n";
171 response_headers << "Content-Range: bytes */" << parameters_->size
172 << "\r\n";
173 }
174 } else {
175 response_headers << "HTTP/1.1 200 Success\r\n";
176 response_headers << "Content-Length: " << parameters_->size << "\r\n";
177 }
178 if (!parameters_->content_type.empty())
179 response_headers << "Content-Type: " << parameters_->content_type << "\r\n";
180
181 if (parameters_->support_byte_ranges)
182 response_headers << "Accept-Ranges: bytes\r\n";
183
184 if (!parameters_->etag.empty())
185 response_headers << "ETag: " << parameters_->etag << "\r\n";
186
187 if (!parameters_->last_modified.empty())
188 response_headers << "Last-Modified: " << parameters_->last_modified
189 << "\r\n";
190
191 std::string raw_headers = response_headers.str();
192 response_info_.headers =
193 new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders(
194 raw_headers.c_str(), raw_headers.size()));
195 offset_ = offset_begin_;
196
197 while (!parameters_->injected_errors.empty() &&
198 parameters_->injected_errors.front().offset <= offset_)
199 parameters_->injected_errors.pop();
200
201 DVLOG(1) << "Notifying response headers complete with headers:\n"
202 << raw_headers;
203 base::MessageLoop::current()->PostTask(
204 FROM_HERE, base::Bind(&PartialResponseJob::NotifyHeadersComplete,
205 weak_factory_.GetWeakPtr()));
206 }
207
208 void PartialResponseJob::GetResponseInfo(net::HttpResponseInfo* response_info) {
209 *response_info = response_info_;
210 }
211
212 int64 PartialResponseJob::GetTotalReceivedBytes() const {
213 return offset_ - offset_begin_;
214 }
215
216 bool PartialResponseJob::GetMimeType(std::string* mime_type) const {
217 *mime_type = parameters_->content_type;
218 return !parameters_->content_type.empty();
219 }
220
221 int PartialResponseJob::GetResponseCode() const {
222 return response_info_.headers.get() ? response_info_.headers->response_code()
223 : 0;
224 }
225
226 bool PartialResponseJob::ReadRawData(net::IOBuffer* buf,
227 int buf_size,
228 int* bytes_read) {
229 *bytes_read = 0;
230 DVLOG(1) << "Reading " << buf_size << " bytes";
231 if (offset_ > offset_end_) {
232 ReportCompletedRequest(offset_end_ - offset_begin_ + 1);
233 DVLOG(1) << "Done reading.";
234 return true;
235 }
236
237 int64_t range_end = std::min(offset_end_, offset_ + buf_size - 1);
238 if (!parameters_->injected_errors.empty()) {
239 const InjectedError& injected_error = parameters_->injected_errors.front();
240 if (offset_ == injected_error.offset) {
241 int error = injected_error.error;
242 SetStatus(net::URLRequestStatus(net::URLRequestStatus::FAILED, error));
243 DVLOG(1) << "Returning error " << net::ErrorToString(error);
244 ReportCompletedRequest(injected_error.offset - offset_begin_);
245 parameters_->injected_errors.pop();
246 return false;
247 }
248
249 if (offset_ < injected_error.offset && injected_error.offset <= range_end)
250 range_end = injected_error.offset - 1;
251 }
252 int bytes_to_copy = (range_end - offset_) + 1;
253
254 TestDownloadRequestHandler::GetPatternBytes(
255 parameters_->pattern_generator_seed, offset_, bytes_to_copy, buf->data());
256 DVLOG(1) << "Read " << bytes_to_copy << " bytes at offset " << offset_;
257 offset_ += bytes_to_copy;
258 *bytes_read = bytes_to_copy;
259 return true;
260 }
261
262 void PartialResponseJob::ReportCompletedRequest(
263 int64_t transferred_content_length) {
264 if (interceptor_.get()) {
265 TestDownloadRequestHandler::CompletedRequest request_info;
266 request_info.transferred_content_length = transferred_content_length;
267 request_info.request_headers = request()->extra_request_headers();
268 interceptor_->AddCompletedRequest(request_info);
269 }
270 }
271
272 // static
273 net::URLRequestJob* StaticResponseJob::Factory(
274 const std::string& headers,
275 net::URLRequest* request,
276 net::NetworkDelegate* delegate,
277 base::WeakPtr<Interceptor> interceptor) {
278 return new StaticResponseJob(headers, request, delegate);
279 }
280
281 StaticResponseJob::StaticResponseJob(const std::string& headers,
282 net::URLRequest* url_request,
283 net::NetworkDelegate* network_delegate)
284 : URLRequestJob(url_request, network_delegate), weak_factory_(this) {
285 response_info_.headers = new net::HttpResponseHeaders(
286 net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
287 }
288
289 StaticResponseJob::~StaticResponseJob() {}
290
291 void StaticResponseJob::Start() {
292 base::MessageLoop::current()->PostTask(
293 FROM_HERE, base::Bind(&StaticResponseJob::NotifyHeadersComplete,
294 weak_factory_.GetWeakPtr()));
295 return;
296 }
297
298 void StaticResponseJob::GetResponseInfo(net::HttpResponseInfo* response_info) {
299 *response_info = response_info_;
300 }
301
302 template <class T>
303 void StoreValueAndInvokeClosure(const base::Closure& quit_closure,
304 T* value_receiver,
305 T value) {
306 *value_receiver = value;
307 quit_closure.Run();
308 }
309
310 // Xorshift* PRNG from https://en.wikipedia.org/wiki/Xorshift
311 uint64_t XorShift64StarWithIndex(uint64_t seed, uint64_t index) {
312 const uint64_t kMultiplier = UINT64_C(2685821657736338717);
313 uint64_t x = seed * kMultiplier + index;
314 x ^= x >> 12;
315 x ^= x << 25;
316 x ^= x >> 27;
317 return x * kMultiplier;
318 }
319
320 // static
321 base::WeakPtr<Interceptor> Interceptor::Register(const GURL& url) {
322 DCHECK(url.is_valid());
323 scoped_ptr<Interceptor> interceptor(new Interceptor(url));
324 base::WeakPtr<Interceptor> weak_reference =
325 interceptor->weak_ptr_factory_.GetWeakPtr();
326 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
327 filter->AddUrlInterceptor(url, interceptor.Pass());
328 return weak_reference;
329 }
330
331 void Interceptor::Unregister() {
332 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance();
333 filter->RemoveUrlHandler(url_);
334 }
335
336 void Interceptor::SetJobFactory(const JobFactory& job_factory) {
337 job_factory_ = job_factory;
338 }
339
340 void Interceptor::GetAndResetCompletedRequests(
341 TestDownloadRequestHandler::CompletedRequests* requests) {
342 requests->clear();
343 completed_requests_.swap(*requests);
344 }
345
346 void Interceptor::AddCompletedRequest(
347 const TestDownloadRequestHandler::CompletedRequest& request_info) {
348 completed_requests_.push_back(request_info);
349 }
350
351 Interceptor::Interceptor(const GURL& url)
352 : url_(url), weak_ptr_factory_(this) {}
353
354 Interceptor::~Interceptor() {}
355
356 net::URLRequestJob* Interceptor::MaybeInterceptRequest(
357 net::URLRequest* request,
358 net::NetworkDelegate* network_delegate) const {
359 DVLOG(1) << "Intercepting request for " << request->url()
360 << " with headers:\n" << request->extra_request_headers().ToString();
361 if (job_factory_.is_null())
362 return nullptr;
363 return job_factory_.Run(request, network_delegate,
364 weak_ptr_factory_.GetWeakPtr());
365 }
366
367 } // namespace
368
369 struct TestDownloadRequestHandler::InterceptorProxy {
370 base::WeakPtr<Interceptor> interceptor_;
371 };
372
373 TestDownloadRequestHandler::InjectedError::InjectedError(int64_t offset,
374 net::Error error)
375 : offset(offset), error(error) {}
376
377 // static
378 Parameters TestDownloadRequestHandler::Parameters::WithSingleInterruption() {
379 Parameters parameters;
380 parameters.injected_errors.push(
381 InjectedError(parameters.size / 2, net::ERR_CONNECTION_RESET));
382 return parameters;
383 }
384
385 TestDownloadRequestHandler::Parameters::Parameters(const Parameters& other)
386 : etag(other.etag),
387 last_modified(other.last_modified),
388 content_type(other.content_type),
389 size(other.size),
390 pattern_generator_seed(other.pattern_generator_seed),
391 support_byte_ranges(other.support_byte_ranges),
392 injected_errors(other.injected_errors) {}
393
394 TestDownloadRequestHandler::Parameters::Parameters()
395 : etag("abcd"),
396 last_modified("Tue, 15 Nov 1994 12:45:26 GMT"),
397 content_type("application/octet-stream"),
398 size(102400),
399 pattern_generator_seed(1),
400 support_byte_ranges(true) {}
401
402 TestDownloadRequestHandler::Parameters::Parameters(
403 const std::string& etag,
404 const std::string& last_modified,
405 const std::string& content_type,
406 int64_t size,
407 int pattern_generator_seed,
408 bool support_byte_ranges)
409 : etag(etag),
410 last_modified(last_modified),
411 content_type(content_type),
412 size(size),
413 pattern_generator_seed(pattern_generator_seed),
414 support_byte_ranges(support_byte_ranges) {}
415
416 TestDownloadRequestHandler::Parameters::~Parameters() {}
417
418 void TestDownloadRequestHandler::Parameters::ClearInjectedErrors() {
419 std::queue<InjectedError> empty_error_list;
420 injected_errors.swap(empty_error_list);
421 }
422
423 TestDownloadRequestHandler::TestDownloadRequestHandler()
424 : TestDownloadRequestHandler(GURL("http://example.com/download")) {}
425
426 TestDownloadRequestHandler::TestDownloadRequestHandler(const GURL& url)
427 : url_(url) {
428 interceptor_proxy_.reset(new InterceptorProxy);
429 base::RunLoop run_loop;
430 BrowserThread::PostTaskAndReplyWithResult(
431 BrowserThread::IO, FROM_HERE, base::Bind(&Interceptor::Register, url_),
432 base::Bind(&StoreValueAndInvokeClosure<base::WeakPtr<Interceptor>>,
433 run_loop.QuitClosure(), &interceptor_proxy_->interceptor_));
434 run_loop.Run();
435 }
436
437 void TestDownloadRequestHandler::StartServing(const Parameters& parameters) {
438 Interceptor::JobFactory job_factory =
439 base::Bind(&PartialResponseJob::Factory, parameters);
440 BrowserThread::PostTask(
Randy Smith (Not in Mondays) 2015/11/12 00:57:57 Comment about why a simple call to a setter starts
asanka 2015/11/13 21:40:01 It's a bit unclear, but StartServing(const Paramet
441 BrowserThread::IO, FROM_HERE,
442 base::Bind(&Interceptor::SetJobFactory, interceptor_proxy_->interceptor_,
443 job_factory));
444 }
445
446 void TestDownloadRequestHandler::StartServingStaticResponse(
447 const base::StringPiece& headers) {
448 Interceptor::JobFactory job_factory =
449 base::Bind(&StaticResponseJob::Factory, headers.as_string());
450 BrowserThread::PostTask(
451 BrowserThread::IO, FROM_HERE,
452 base::Bind(&Interceptor::SetJobFactory, interceptor_proxy_->interceptor_,
453 job_factory));
454 }
455
456 // static
457 void TestDownloadRequestHandler::GetPatternBytes(int seed,
458 int64_t starting_offset,
459 int length,
460 char* buffer) {
461 int64_t seed_offset = starting_offset / sizeof(int64_t);
462 int64_t first_byte_position = starting_offset % sizeof(int64_t);
463 while (length > 0) {
464 uint64_t data = XorShift64StarWithIndex(seed, seed_offset);
465 int length_to_copy =
466 std::min(length, static_cast<int>(sizeof(data) - first_byte_position));
467 memcpy(buffer, reinterpret_cast<char*>(&data) + first_byte_position,
468 length_to_copy);
469 buffer += length_to_copy;
470 length -= length_to_copy;
471 ++seed_offset;
472 first_byte_position = 0;
473 }
474 }
475
476 TestDownloadRequestHandler::~TestDownloadRequestHandler() {
477 BrowserThread::PostTask(
478 BrowserThread::IO, FROM_HERE,
479 base::Bind(&Interceptor::Unregister, interceptor_proxy_->interceptor_));
480 }
481
482 void TestDownloadRequestHandler::GetCompletedRequestInfo(
483 TestDownloadRequestHandler::CompletedRequests* requests) {
484 base::RunLoop run_loop;
485 BrowserThread::PostTaskAndReply(
486 BrowserThread::IO, FROM_HERE,
487 base::Bind(&Interceptor::GetAndResetCompletedRequests,
488 interceptor_proxy_->interceptor_, requests),
489 run_loop.QuitClosure());
490 run_loop.Run();
491 }
492
493 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698