OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "net/url_request/url_fetcher_core.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/logging.h" | |
11 #include "base/metrics/histogram.h" | |
12 #include "base/profiler/scoped_tracker.h" | |
13 #include "base/sequenced_task_runner.h" | |
14 #include "base/single_thread_task_runner.h" | |
15 #include "base/stl_util.h" | |
16 #include "base/thread_task_runner_handle.h" | |
17 #include "base/tracked_objects.h" | |
18 #include "net/base/elements_upload_data_stream.h" | |
19 #include "net/base/io_buffer.h" | |
20 #include "net/base/load_flags.h" | |
21 #include "net/base/net_errors.h" | |
22 #include "net/base/request_priority.h" | |
23 #include "net/base/upload_bytes_element_reader.h" | |
24 #include "net/base/upload_data_stream.h" | |
25 #include "net/base/upload_file_element_reader.h" | |
26 #include "net/http/http_response_headers.h" | |
27 #include "net/url_request/redirect_info.h" | |
28 #include "net/url_request/url_fetcher_delegate.h" | |
29 #include "net/url_request/url_fetcher_response_writer.h" | |
30 #include "net/url_request/url_request_context.h" | |
31 #include "net/url_request/url_request_context_getter.h" | |
32 #include "net/url_request/url_request_throttler_manager.h" | |
33 | |
34 namespace { | |
35 | |
36 const int kBufferSize = 4096; | |
37 const int kUploadProgressTimerInterval = 100; | |
38 bool g_ignore_certificate_requests = false; | |
39 | |
40 void EmptyCompletionCallback(int result) {} | |
41 | |
42 } // namespace | |
43 | |
44 namespace net { | |
45 | |
46 // URLFetcherCore::Registry --------------------------------------------------- | |
47 | |
48 URLFetcherCore::Registry::Registry() {} | |
49 URLFetcherCore::Registry::~Registry() {} | |
50 | |
51 void URLFetcherCore::Registry::AddURLFetcherCore(URLFetcherCore* core) { | |
52 DCHECK(!ContainsKey(fetchers_, core)); | |
53 fetchers_.insert(core); | |
54 } | |
55 | |
56 void URLFetcherCore::Registry::RemoveURLFetcherCore(URLFetcherCore* core) { | |
57 DCHECK(ContainsKey(fetchers_, core)); | |
58 fetchers_.erase(core); | |
59 } | |
60 | |
61 void URLFetcherCore::Registry::CancelAll() { | |
62 while (!fetchers_.empty()) | |
63 (*fetchers_.begin())->CancelURLRequest(ERR_ABORTED); | |
64 } | |
65 | |
66 // URLFetcherCore ------------------------------------------------------------- | |
67 | |
68 // static | |
69 base::LazyInstance<URLFetcherCore::Registry> | |
70 URLFetcherCore::g_registry = LAZY_INSTANCE_INITIALIZER; | |
71 | |
72 URLFetcherCore::URLFetcherCore(URLFetcher* fetcher, | |
73 const GURL& original_url, | |
74 URLFetcher::RequestType request_type, | |
75 URLFetcherDelegate* d) | |
76 : fetcher_(fetcher), | |
77 original_url_(original_url), | |
78 request_type_(request_type), | |
79 delegate_(d), | |
80 delegate_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
81 load_flags_(LOAD_NORMAL), | |
82 response_code_(URLFetcher::RESPONSE_CODE_INVALID), | |
83 buffer_(new IOBuffer(kBufferSize)), | |
84 url_request_data_key_(NULL), | |
85 was_fetched_via_proxy_(false), | |
86 upload_content_set_(false), | |
87 upload_range_offset_(0), | |
88 upload_range_length_(0), | |
89 referrer_policy_( | |
90 URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE), | |
91 is_chunked_upload_(false), | |
92 was_cancelled_(false), | |
93 stop_on_redirect_(false), | |
94 stopped_on_redirect_(false), | |
95 automatically_retry_on_5xx_(true), | |
96 num_retries_on_5xx_(0), | |
97 max_retries_on_5xx_(0), | |
98 num_retries_on_network_changes_(0), | |
99 max_retries_on_network_changes_(0), | |
100 current_upload_bytes_(-1), | |
101 current_response_bytes_(0), | |
102 total_response_bytes_(-1) { | |
103 CHECK(original_url_.is_valid()); | |
104 } | |
105 | |
106 void URLFetcherCore::Start() { | |
107 DCHECK(delegate_task_runner_.get()); | |
108 DCHECK(request_context_getter_.get()) << "We need an URLRequestContext!"; | |
109 if (network_task_runner_.get()) { | |
110 DCHECK_EQ(network_task_runner_, | |
111 request_context_getter_->GetNetworkTaskRunner()); | |
112 } else { | |
113 network_task_runner_ = request_context_getter_->GetNetworkTaskRunner(); | |
114 } | |
115 DCHECK(network_task_runner_.get()) << "We need an IO task runner"; | |
116 | |
117 network_task_runner_->PostTask( | |
118 FROM_HERE, base::Bind(&URLFetcherCore::StartOnIOThread, this)); | |
119 } | |
120 | |
121 void URLFetcherCore::Stop() { | |
122 if (delegate_task_runner_.get()) // May be NULL in tests. | |
123 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
124 | |
125 delegate_ = NULL; | |
126 fetcher_ = NULL; | |
127 if (!network_task_runner_.get()) | |
128 return; | |
129 if (network_task_runner_->RunsTasksOnCurrentThread()) { | |
130 CancelURLRequest(ERR_ABORTED); | |
131 } else { | |
132 network_task_runner_->PostTask( | |
133 FROM_HERE, | |
134 base::Bind(&URLFetcherCore::CancelURLRequest, this, ERR_ABORTED)); | |
135 } | |
136 } | |
137 | |
138 void URLFetcherCore::SetUploadData(const std::string& upload_content_type, | |
139 const std::string& upload_content) { | |
140 AssertHasNoUploadData(); | |
141 DCHECK(!is_chunked_upload_); | |
142 DCHECK(upload_content_type_.empty()); | |
143 | |
144 // Empty |upload_content_type| is allowed iff the |upload_content| is empty. | |
145 DCHECK(upload_content.empty() || !upload_content_type.empty()); | |
146 | |
147 upload_content_type_ = upload_content_type; | |
148 upload_content_ = upload_content; | |
149 upload_content_set_ = true; | |
150 } | |
151 | |
152 void URLFetcherCore::SetUploadFilePath( | |
153 const std::string& upload_content_type, | |
154 const base::FilePath& file_path, | |
155 uint64 range_offset, | |
156 uint64 range_length, | |
157 scoped_refptr<base::TaskRunner> file_task_runner) { | |
158 AssertHasNoUploadData(); | |
159 DCHECK(!is_chunked_upload_); | |
160 DCHECK_EQ(upload_range_offset_, 0ULL); | |
161 DCHECK_EQ(upload_range_length_, 0ULL); | |
162 DCHECK(upload_content_type_.empty()); | |
163 DCHECK(!upload_content_type.empty()); | |
164 | |
165 upload_content_type_ = upload_content_type; | |
166 upload_file_path_ = file_path; | |
167 upload_range_offset_ = range_offset; | |
168 upload_range_length_ = range_length; | |
169 upload_file_task_runner_ = file_task_runner; | |
170 upload_content_set_ = true; | |
171 } | |
172 | |
173 void URLFetcherCore::SetUploadStreamFactory( | |
174 const std::string& upload_content_type, | |
175 const URLFetcher::CreateUploadStreamCallback& factory) { | |
176 AssertHasNoUploadData(); | |
177 DCHECK(!is_chunked_upload_); | |
178 DCHECK(upload_content_type_.empty()); | |
179 | |
180 upload_content_type_ = upload_content_type; | |
181 upload_stream_factory_ = factory; | |
182 upload_content_set_ = true; | |
183 } | |
184 | |
185 void URLFetcherCore::SetChunkedUpload(const std::string& content_type) { | |
186 if (!is_chunked_upload_) { | |
187 AssertHasNoUploadData(); | |
188 DCHECK(upload_content_type_.empty()); | |
189 } | |
190 | |
191 // Empty |content_type| is not allowed here, because it is impossible | |
192 // to ensure non-empty upload content as it is not yet supplied. | |
193 DCHECK(!content_type.empty()); | |
194 | |
195 upload_content_type_ = content_type; | |
196 upload_content_.clear(); | |
197 is_chunked_upload_ = true; | |
198 } | |
199 | |
200 void URLFetcherCore::AppendChunkToUpload(const std::string& content, | |
201 bool is_last_chunk) { | |
202 DCHECK(delegate_task_runner_.get()); | |
203 DCHECK(network_task_runner_.get()); | |
204 network_task_runner_->PostTask( | |
205 FROM_HERE, | |
206 base::Bind(&URLFetcherCore::CompleteAddingUploadDataChunk, this, content, | |
207 is_last_chunk)); | |
208 } | |
209 | |
210 void URLFetcherCore::SetLoadFlags(int load_flags) { | |
211 load_flags_ = load_flags; | |
212 } | |
213 | |
214 int URLFetcherCore::GetLoadFlags() const { | |
215 return load_flags_; | |
216 } | |
217 | |
218 void URLFetcherCore::SetReferrer(const std::string& referrer) { | |
219 referrer_ = referrer; | |
220 } | |
221 | |
222 void URLFetcherCore::SetReferrerPolicy( | |
223 URLRequest::ReferrerPolicy referrer_policy) { | |
224 referrer_policy_ = referrer_policy; | |
225 } | |
226 | |
227 void URLFetcherCore::SetExtraRequestHeaders( | |
228 const std::string& extra_request_headers) { | |
229 extra_request_headers_.Clear(); | |
230 extra_request_headers_.AddHeadersFromString(extra_request_headers); | |
231 } | |
232 | |
233 void URLFetcherCore::AddExtraRequestHeader(const std::string& header_line) { | |
234 extra_request_headers_.AddHeaderFromString(header_line); | |
235 } | |
236 | |
237 void URLFetcherCore::SetRequestContext( | |
238 URLRequestContextGetter* request_context_getter) { | |
239 DCHECK(!request_context_getter_.get()); | |
240 DCHECK(request_context_getter); | |
241 request_context_getter_ = request_context_getter; | |
242 } | |
243 | |
244 void URLFetcherCore::SetFirstPartyForCookies( | |
245 const GURL& first_party_for_cookies) { | |
246 DCHECK(first_party_for_cookies_.is_empty()); | |
247 first_party_for_cookies_ = first_party_for_cookies; | |
248 } | |
249 | |
250 void URLFetcherCore::SetURLRequestUserData( | |
251 const void* key, | |
252 const URLFetcher::CreateDataCallback& create_data_callback) { | |
253 DCHECK(key); | |
254 DCHECK(!create_data_callback.is_null()); | |
255 url_request_data_key_ = key; | |
256 url_request_create_data_callback_ = create_data_callback; | |
257 } | |
258 | |
259 void URLFetcherCore::SetStopOnRedirect(bool stop_on_redirect) { | |
260 stop_on_redirect_ = stop_on_redirect; | |
261 } | |
262 | |
263 void URLFetcherCore::SetAutomaticallyRetryOn5xx(bool retry) { | |
264 automatically_retry_on_5xx_ = retry; | |
265 } | |
266 | |
267 void URLFetcherCore::SetMaxRetriesOn5xx(int max_retries) { | |
268 max_retries_on_5xx_ = max_retries; | |
269 } | |
270 | |
271 int URLFetcherCore::GetMaxRetriesOn5xx() const { | |
272 return max_retries_on_5xx_; | |
273 } | |
274 | |
275 base::TimeDelta URLFetcherCore::GetBackoffDelay() const { | |
276 return backoff_delay_; | |
277 } | |
278 | |
279 void URLFetcherCore::SetAutomaticallyRetryOnNetworkChanges(int max_retries) { | |
280 max_retries_on_network_changes_ = max_retries; | |
281 } | |
282 | |
283 void URLFetcherCore::SaveResponseToFileAtPath( | |
284 const base::FilePath& file_path, | |
285 scoped_refptr<base::SequencedTaskRunner> file_task_runner) { | |
286 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
287 SaveResponseWithWriter(scoped_ptr<URLFetcherResponseWriter>( | |
288 new URLFetcherFileWriter(file_task_runner, file_path))); | |
289 } | |
290 | |
291 void URLFetcherCore::SaveResponseToTemporaryFile( | |
292 scoped_refptr<base::SequencedTaskRunner> file_task_runner) { | |
293 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
294 SaveResponseWithWriter(scoped_ptr<URLFetcherResponseWriter>( | |
295 new URLFetcherFileWriter(file_task_runner, base::FilePath()))); | |
296 } | |
297 | |
298 void URLFetcherCore::SaveResponseWithWriter( | |
299 scoped_ptr<URLFetcherResponseWriter> response_writer) { | |
300 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
301 response_writer_ = response_writer.Pass(); | |
302 } | |
303 | |
304 HttpResponseHeaders* URLFetcherCore::GetResponseHeaders() const { | |
305 return response_headers_.get(); | |
306 } | |
307 | |
308 // TODO(panayiotis): socket_address_ is written in the IO thread, | |
309 // if this is accessed in the UI thread, this could result in a race. | |
310 // Same for response_headers_ above and was_fetched_via_proxy_ below. | |
311 HostPortPair URLFetcherCore::GetSocketAddress() const { | |
312 return socket_address_; | |
313 } | |
314 | |
315 bool URLFetcherCore::WasFetchedViaProxy() const { | |
316 return was_fetched_via_proxy_; | |
317 } | |
318 | |
319 const GURL& URLFetcherCore::GetOriginalURL() const { | |
320 return original_url_; | |
321 } | |
322 | |
323 const GURL& URLFetcherCore::GetURL() const { | |
324 return url_; | |
325 } | |
326 | |
327 const URLRequestStatus& URLFetcherCore::GetStatus() const { | |
328 return status_; | |
329 } | |
330 | |
331 int URLFetcherCore::GetResponseCode() const { | |
332 return response_code_; | |
333 } | |
334 | |
335 const ResponseCookies& URLFetcherCore::GetCookies() const { | |
336 return cookies_; | |
337 } | |
338 | |
339 void URLFetcherCore::ReceivedContentWasMalformed() { | |
340 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
341 if (network_task_runner_.get()) { | |
342 network_task_runner_->PostTask( | |
343 FROM_HERE, base::Bind(&URLFetcherCore::NotifyMalformedContent, this)); | |
344 } | |
345 } | |
346 | |
347 bool URLFetcherCore::GetResponseAsString( | |
348 std::string* out_response_string) const { | |
349 URLFetcherStringWriter* string_writer = | |
350 response_writer_ ? response_writer_->AsStringWriter() : NULL; | |
351 if (!string_writer) | |
352 return false; | |
353 | |
354 *out_response_string = string_writer->data(); | |
355 UMA_HISTOGRAM_MEMORY_KB("UrlFetcher.StringResponseSize", | |
356 (string_writer->data().length() / 1024)); | |
357 return true; | |
358 } | |
359 | |
360 bool URLFetcherCore::GetResponseAsFilePath(bool take_ownership, | |
361 base::FilePath* out_response_path) { | |
362 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
363 | |
364 URLFetcherFileWriter* file_writer = | |
365 response_writer_ ? response_writer_->AsFileWriter() : NULL; | |
366 if (!file_writer) | |
367 return false; | |
368 | |
369 *out_response_path = file_writer->file_path(); | |
370 | |
371 if (take_ownership) { | |
372 // Intentionally calling a file_writer_ method directly without posting | |
373 // the task to network_task_runner_. | |
374 // | |
375 // This is for correctly handling the case when file_writer_->DisownFile() | |
376 // is soon followed by URLFetcherCore::Stop(). We have to make sure that | |
377 // DisownFile takes effect before Stop deletes file_writer_. | |
378 // | |
379 // This direct call should be thread-safe, since DisownFile itself does no | |
380 // file operation. It just flips the state to be referred in destruction. | |
381 file_writer->DisownFile(); | |
382 } | |
383 return true; | |
384 } | |
385 | |
386 void URLFetcherCore::OnReceivedRedirect(URLRequest* request, | |
387 const RedirectInfo& redirect_info, | |
388 bool* defer_redirect) { | |
389 DCHECK_EQ(request, request_.get()); | |
390 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
391 if (stop_on_redirect_) { | |
392 stopped_on_redirect_ = true; | |
393 url_ = redirect_info.new_url; | |
394 response_code_ = request_->GetResponseCode(); | |
395 was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); | |
396 request->Cancel(); | |
397 OnReadCompleted(request, 0); | |
398 } | |
399 } | |
400 | |
401 void URLFetcherCore::OnResponseStarted(URLRequest* request) { | |
402 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
403 tracked_objects::ScopedTracker tracking_profile( | |
404 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
405 "423948 URLFetcherCore::OnResponseStarted")); | |
406 | |
407 DCHECK_EQ(request, request_.get()); | |
408 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
409 if (request_->status().is_success()) { | |
410 response_code_ = request_->GetResponseCode(); | |
411 response_headers_ = request_->response_headers(); | |
412 socket_address_ = request_->GetSocketAddress(); | |
413 was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); | |
414 total_response_bytes_ = request_->GetExpectedContentSize(); | |
415 } | |
416 | |
417 ReadResponse(); | |
418 } | |
419 | |
420 void URLFetcherCore::OnCertificateRequested( | |
421 URLRequest* request, | |
422 SSLCertRequestInfo* cert_request_info) { | |
423 DCHECK_EQ(request, request_.get()); | |
424 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
425 | |
426 if (g_ignore_certificate_requests) { | |
427 request->ContinueWithCertificate(NULL); | |
428 } else { | |
429 request->Cancel(); | |
430 } | |
431 } | |
432 | |
433 void URLFetcherCore::OnReadCompleted(URLRequest* request, | |
434 int bytes_read) { | |
435 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
436 tracked_objects::ScopedTracker tracking_profile( | |
437 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
438 "423948 URLFetcherCore::OnReadCompleted")); | |
439 | |
440 DCHECK(request == request_); | |
441 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
442 | |
443 if (!stopped_on_redirect_) | |
444 url_ = request->url(); | |
445 URLRequestThrottlerManager* throttler_manager = | |
446 request->context()->throttler_manager(); | |
447 if (throttler_manager) { | |
448 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
449 tracked_objects::ScopedTracker tracking_profile1( | |
450 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
451 "423948 URLFetcherCore::OnReadCompleted1")); | |
452 | |
453 url_throttler_entry_ = throttler_manager->RegisterRequestUrl(url_); | |
454 } | |
455 | |
456 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
457 tracked_objects::ScopedTracker tracking_profile2( | |
458 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
459 "423948 URLFetcherCore::OnReadCompleted2")); | |
460 | |
461 do { | |
462 if (!request_->status().is_success() || bytes_read <= 0) | |
463 break; | |
464 | |
465 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
466 tracked_objects::ScopedTracker tracking_profile3( | |
467 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
468 "423948 URLFetcherCore::OnReadCompleted3")); | |
469 | |
470 current_response_bytes_ += bytes_read; | |
471 InformDelegateDownloadProgress(); | |
472 | |
473 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
474 tracked_objects::ScopedTracker tracking_profile4( | |
475 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
476 "423948 URLFetcherCore::OnReadCompleted4")); | |
477 | |
478 const int result = | |
479 WriteBuffer(new DrainableIOBuffer(buffer_.get(), bytes_read)); | |
480 if (result < 0) { | |
481 // Write failed or waiting for write completion. | |
482 return; | |
483 } | |
484 } while (request_->Read(buffer_.get(), kBufferSize, &bytes_read)); | |
485 | |
486 const URLRequestStatus status = request_->status(); | |
487 | |
488 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
489 tracked_objects::ScopedTracker tracking_profile5( | |
490 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
491 "423948 URLFetcherCore::OnReadCompleted5")); | |
492 | |
493 if (status.is_success()) | |
494 request_->GetResponseCookies(&cookies_); | |
495 | |
496 // See comments re: HEAD requests in ReadResponse(). | |
497 if (!status.is_io_pending() || request_type_ == URLFetcher::HEAD) { | |
498 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
499 tracked_objects::ScopedTracker tracking_profile6( | |
500 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
501 "423948 URLFetcherCore::OnReadCompleted6")); | |
502 | |
503 status_ = status; | |
504 ReleaseRequest(); | |
505 | |
506 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
507 tracked_objects::ScopedTracker tracking_profile7( | |
508 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
509 "423948 URLFetcherCore::OnReadCompleted7")); | |
510 | |
511 // No more data to write. | |
512 const int result = response_writer_->Finish( | |
513 base::Bind(&URLFetcherCore::DidFinishWriting, this)); | |
514 | |
515 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
516 tracked_objects::ScopedTracker tracking_profile8( | |
517 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
518 "423948 URLFetcherCore::OnReadCompleted8")); | |
519 | |
520 if (result != ERR_IO_PENDING) | |
521 DidFinishWriting(result); | |
522 } | |
523 } | |
524 | |
525 void URLFetcherCore::CancelAll() { | |
526 g_registry.Get().CancelAll(); | |
527 } | |
528 | |
529 int URLFetcherCore::GetNumFetcherCores() { | |
530 return g_registry.Get().size(); | |
531 } | |
532 | |
533 void URLFetcherCore::SetIgnoreCertificateRequests(bool ignored) { | |
534 g_ignore_certificate_requests = ignored; | |
535 } | |
536 | |
537 URLFetcherCore::~URLFetcherCore() { | |
538 // |request_| should be NULL. If not, it's unsafe to delete it here since we | |
539 // may not be on the IO thread. | |
540 DCHECK(!request_.get()); | |
541 } | |
542 | |
543 void URLFetcherCore::StartOnIOThread() { | |
544 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed. | |
545 tracked_objects::ScopedTracker tracking_profile( | |
546 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
547 "456327 URLFetcherCore::StartOnIOThread")); | |
548 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
549 | |
550 if (!response_writer_) | |
551 response_writer_.reset(new URLFetcherStringWriter); | |
552 | |
553 const int result = response_writer_->Initialize( | |
554 base::Bind(&URLFetcherCore::DidInitializeWriter, this)); | |
555 if (result != ERR_IO_PENDING) | |
556 DidInitializeWriter(result); | |
557 } | |
558 | |
559 void URLFetcherCore::StartURLRequest() { | |
560 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is fixed. | |
561 tracked_objects::ScopedTracker tracking_profile( | |
562 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
563 "456327 URLFetcherCore::StartURLRequest")); | |
564 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
565 | |
566 if (was_cancelled_) { | |
567 // Since StartURLRequest() is posted as a *delayed* task, it may | |
568 // run after the URLFetcher was already stopped. | |
569 return; | |
570 } | |
571 | |
572 DCHECK(request_context_getter_.get()); | |
573 DCHECK(!request_.get()); | |
574 | |
575 g_registry.Get().AddURLFetcherCore(this); | |
576 current_response_bytes_ = 0; | |
577 request_ = request_context_getter_->GetURLRequestContext()->CreateRequest( | |
578 original_url_, DEFAULT_PRIORITY, this, NULL); | |
579 request_->set_stack_trace(stack_trace_); | |
580 int flags = request_->load_flags() | load_flags_; | |
581 | |
582 if (is_chunked_upload_) | |
583 request_->EnableChunkedUpload(); | |
584 request_->SetLoadFlags(flags); | |
585 request_->SetReferrer(referrer_); | |
586 request_->set_referrer_policy(referrer_policy_); | |
587 request_->set_first_party_for_cookies(first_party_for_cookies_.is_empty() ? | |
588 original_url_ : first_party_for_cookies_); | |
589 if (url_request_data_key_ && !url_request_create_data_callback_.is_null()) { | |
590 request_->SetUserData(url_request_data_key_, | |
591 url_request_create_data_callback_.Run()); | |
592 } | |
593 | |
594 switch (request_type_) { | |
595 case URLFetcher::GET: | |
596 break; | |
597 | |
598 case URLFetcher::POST: | |
599 case URLFetcher::PUT: | |
600 case URLFetcher::PATCH: | |
601 // Upload content must be set. | |
602 DCHECK(is_chunked_upload_ || upload_content_set_); | |
603 | |
604 request_->set_method( | |
605 request_type_ == URLFetcher::POST ? "POST" : | |
606 request_type_ == URLFetcher::PUT ? "PUT" : "PATCH"); | |
607 if (!upload_content_type_.empty()) { | |
608 extra_request_headers_.SetHeader(HttpRequestHeaders::kContentType, | |
609 upload_content_type_); | |
610 } | |
611 if (!upload_content_.empty()) { | |
612 scoped_ptr<UploadElementReader> reader(new UploadBytesElementReader( | |
613 upload_content_.data(), upload_content_.size())); | |
614 request_->set_upload( | |
615 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); | |
616 } else if (!upload_file_path_.empty()) { | |
617 scoped_ptr<UploadElementReader> reader( | |
618 new UploadFileElementReader(upload_file_task_runner_.get(), | |
619 upload_file_path_, | |
620 upload_range_offset_, | |
621 upload_range_length_, | |
622 base::Time())); | |
623 request_->set_upload( | |
624 ElementsUploadDataStream::CreateWithReader(reader.Pass(), 0)); | |
625 } else if (!upload_stream_factory_.is_null()) { | |
626 scoped_ptr<UploadDataStream> stream = upload_stream_factory_.Run(); | |
627 DCHECK(stream); | |
628 request_->set_upload(stream.Pass()); | |
629 } | |
630 | |
631 current_upload_bytes_ = -1; | |
632 // TODO(kinaba): http://crbug.com/118103. Implement upload callback in the | |
633 // layer and avoid using timer here. | |
634 upload_progress_checker_timer_.reset( | |
635 new base::RepeatingTimer<URLFetcherCore>()); | |
636 upload_progress_checker_timer_->Start( | |
637 FROM_HERE, | |
638 base::TimeDelta::FromMilliseconds(kUploadProgressTimerInterval), | |
639 this, | |
640 &URLFetcherCore::InformDelegateUploadProgress); | |
641 break; | |
642 | |
643 case URLFetcher::HEAD: | |
644 request_->set_method("HEAD"); | |
645 break; | |
646 | |
647 case URLFetcher::DELETE_REQUEST: | |
648 request_->set_method("DELETE"); | |
649 break; | |
650 | |
651 default: | |
652 NOTREACHED(); | |
653 } | |
654 | |
655 if (!extra_request_headers_.IsEmpty()) | |
656 request_->SetExtraRequestHeaders(extra_request_headers_); | |
657 | |
658 request_->Start(); | |
659 } | |
660 | |
661 void URLFetcherCore::DidInitializeWriter(int result) { | |
662 if (result != OK) { | |
663 CancelURLRequest(result); | |
664 delegate_task_runner_->PostTask( | |
665 FROM_HERE, | |
666 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, this)); | |
667 return; | |
668 } | |
669 StartURLRequestWhenAppropriate(); | |
670 } | |
671 | |
672 void URLFetcherCore::StartURLRequestWhenAppropriate() { | |
673 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
674 | |
675 if (was_cancelled_) | |
676 return; | |
677 | |
678 DCHECK(request_context_getter_.get()); | |
679 | |
680 int64 delay = 0; | |
681 if (!original_url_throttler_entry_.get()) { | |
682 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is | |
683 // fixed. | |
684 tracked_objects::ScopedTracker tracking_profile1( | |
685 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
686 "456327 URLFetcherCore::StartURLRequestWhenAppropriate1")); | |
687 URLRequestThrottlerManager* manager = | |
688 request_context_getter_->GetURLRequestContext()->throttler_manager(); | |
689 if (manager) { | |
690 original_url_throttler_entry_ = | |
691 manager->RegisterRequestUrl(original_url_); | |
692 } | |
693 } | |
694 if (original_url_throttler_entry_.get()) { | |
695 // TODO(pkasting): Remove ScopedTracker below once crbug.com/456327 is | |
696 // fixed. | |
697 tracked_objects::ScopedTracker tracking_profile2( | |
698 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
699 "456327 URLFetcherCore::StartURLRequestWhenAppropriate2")); | |
700 delay = original_url_throttler_entry_->ReserveSendingTimeForNextRequest( | |
701 GetBackoffReleaseTime()); | |
702 } | |
703 | |
704 if (delay == 0) { | |
705 StartURLRequest(); | |
706 } else { | |
707 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
708 FROM_HERE, base::Bind(&URLFetcherCore::StartURLRequest, this), | |
709 base::TimeDelta::FromMilliseconds(delay)); | |
710 } | |
711 } | |
712 | |
713 void URLFetcherCore::CancelURLRequest(int error) { | |
714 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
715 | |
716 if (request_.get()) { | |
717 request_->CancelWithError(error); | |
718 ReleaseRequest(); | |
719 } | |
720 | |
721 // Set the error manually. | |
722 // Normally, calling URLRequest::CancelWithError() results in calling | |
723 // OnReadCompleted() with bytes_read = -1 via an asynchronous task posted by | |
724 // URLRequestJob::NotifyDone(). But, because the request was released | |
725 // immediately after being canceled, the request could not call | |
726 // OnReadCompleted() which overwrites |status_| with the error status. | |
727 status_.set_status(URLRequestStatus::CANCELED); | |
728 status_.set_error(error); | |
729 | |
730 // Release the reference to the request context. There could be multiple | |
731 // references to URLFetcher::Core at this point so it may take a while to | |
732 // delete the object, but we cannot delay the destruction of the request | |
733 // context. | |
734 request_context_getter_ = NULL; | |
735 first_party_for_cookies_ = GURL(); | |
736 url_request_data_key_ = NULL; | |
737 url_request_create_data_callback_.Reset(); | |
738 was_cancelled_ = true; | |
739 } | |
740 | |
741 void URLFetcherCore::OnCompletedURLRequest( | |
742 base::TimeDelta backoff_delay) { | |
743 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
744 | |
745 // Save the status and backoff_delay so that delegates can read it. | |
746 if (delegate_) { | |
747 backoff_delay_ = backoff_delay; | |
748 InformDelegateFetchIsComplete(); | |
749 } | |
750 } | |
751 | |
752 void URLFetcherCore::InformDelegateFetchIsComplete() { | |
753 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
754 if (delegate_) | |
755 delegate_->OnURLFetchComplete(fetcher_); | |
756 } | |
757 | |
758 void URLFetcherCore::NotifyMalformedContent() { | |
759 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
760 if (url_throttler_entry_.get()) { | |
761 int status_code = response_code_; | |
762 if (status_code == URLFetcher::RESPONSE_CODE_INVALID) { | |
763 // The status code will generally be known by the time clients | |
764 // call the |ReceivedContentWasMalformed()| function (which ends up | |
765 // calling the current function) but if it's not, we need to assume | |
766 // the response was successful so that the total failure count | |
767 // used to calculate exponential back-off goes up. | |
768 status_code = 200; | |
769 } | |
770 url_throttler_entry_->ReceivedContentWasMalformed(status_code); | |
771 } | |
772 } | |
773 | |
774 void URLFetcherCore::DidFinishWriting(int result) { | |
775 if (result != OK) { | |
776 CancelURLRequest(result); | |
777 delegate_task_runner_->PostTask( | |
778 FROM_HERE, | |
779 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, this)); | |
780 return; | |
781 } | |
782 // If the file was successfully closed, then the URL request is complete. | |
783 RetryOrCompleteUrlFetch(); | |
784 } | |
785 | |
786 void URLFetcherCore::RetryOrCompleteUrlFetch() { | |
787 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
788 base::TimeDelta backoff_delay; | |
789 | |
790 // Checks the response from server. | |
791 if (response_code_ >= 500 || | |
792 status_.error() == ERR_TEMPORARILY_THROTTLED) { | |
793 // When encountering a server error, we will send the request again | |
794 // after backoff time. | |
795 ++num_retries_on_5xx_; | |
796 | |
797 // Note that backoff_delay may be 0 because (a) the | |
798 // URLRequestThrottlerManager and related code does not | |
799 // necessarily back off on the first error, (b) it only backs off | |
800 // on some of the 5xx status codes, (c) not all URLRequestContexts | |
801 // have a throttler manager. | |
802 base::TimeTicks backoff_release_time = GetBackoffReleaseTime(); | |
803 backoff_delay = backoff_release_time - base::TimeTicks::Now(); | |
804 if (backoff_delay < base::TimeDelta()) | |
805 backoff_delay = base::TimeDelta(); | |
806 | |
807 if (automatically_retry_on_5xx_ && | |
808 num_retries_on_5xx_ <= max_retries_on_5xx_) { | |
809 StartOnIOThread(); | |
810 return; | |
811 } | |
812 } else { | |
813 backoff_delay = base::TimeDelta(); | |
814 } | |
815 | |
816 // Retry if the request failed due to network changes. | |
817 if (status_.error() == ERR_NETWORK_CHANGED && | |
818 num_retries_on_network_changes_ < max_retries_on_network_changes_) { | |
819 ++num_retries_on_network_changes_; | |
820 | |
821 // Retry soon, after flushing all the current tasks which may include | |
822 // further network change observers. | |
823 network_task_runner_->PostTask( | |
824 FROM_HERE, base::Bind(&URLFetcherCore::StartOnIOThread, this)); | |
825 return; | |
826 } | |
827 | |
828 request_context_getter_ = NULL; | |
829 first_party_for_cookies_ = GURL(); | |
830 url_request_data_key_ = NULL; | |
831 url_request_create_data_callback_.Reset(); | |
832 bool posted = delegate_task_runner_->PostTask( | |
833 FROM_HERE, | |
834 base::Bind(&URLFetcherCore::OnCompletedURLRequest, this, backoff_delay)); | |
835 | |
836 // If the delegate message loop does not exist any more, then the delegate | |
837 // should be gone too. | |
838 DCHECK(posted || !delegate_); | |
839 } | |
840 | |
841 void URLFetcherCore::ReleaseRequest() { | |
842 upload_progress_checker_timer_.reset(); | |
843 request_.reset(); | |
844 g_registry.Get().RemoveURLFetcherCore(this); | |
845 } | |
846 | |
847 base::TimeTicks URLFetcherCore::GetBackoffReleaseTime() { | |
848 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
849 | |
850 if (!original_url_throttler_entry_.get()) | |
851 return base::TimeTicks(); | |
852 | |
853 base::TimeTicks original_url_backoff = | |
854 original_url_throttler_entry_->GetExponentialBackoffReleaseTime(); | |
855 base::TimeTicks destination_url_backoff; | |
856 if (url_throttler_entry_.get() && | |
857 original_url_throttler_entry_.get() != url_throttler_entry_.get()) { | |
858 destination_url_backoff = | |
859 url_throttler_entry_->GetExponentialBackoffReleaseTime(); | |
860 } | |
861 | |
862 return original_url_backoff > destination_url_backoff ? | |
863 original_url_backoff : destination_url_backoff; | |
864 } | |
865 | |
866 void URLFetcherCore::CompleteAddingUploadDataChunk( | |
867 const std::string& content, bool is_last_chunk) { | |
868 if (was_cancelled_) { | |
869 // Since CompleteAddingUploadDataChunk() is posted as a *delayed* task, it | |
870 // may run after the URLFetcher was already stopped. | |
871 return; | |
872 } | |
873 DCHECK(is_chunked_upload_); | |
874 DCHECK(request_.get()); | |
875 DCHECK(!content.empty()); | |
876 request_->AppendChunkToUpload(content.data(), | |
877 static_cast<int>(content.length()), | |
878 is_last_chunk); | |
879 } | |
880 | |
881 int URLFetcherCore::WriteBuffer(scoped_refptr<DrainableIOBuffer> data) { | |
882 while (data->BytesRemaining() > 0) { | |
883 const int result = response_writer_->Write( | |
884 data.get(), | |
885 data->BytesRemaining(), | |
886 base::Bind(&URLFetcherCore::DidWriteBuffer, this, data)); | |
887 if (result < 0) { | |
888 if (result != ERR_IO_PENDING) | |
889 DidWriteBuffer(data, result); | |
890 return result; | |
891 } | |
892 data->DidConsume(result); | |
893 } | |
894 return OK; | |
895 } | |
896 | |
897 void URLFetcherCore::DidWriteBuffer(scoped_refptr<DrainableIOBuffer> data, | |
898 int result) { | |
899 if (result < 0) { // Handle errors. | |
900 CancelURLRequest(result); | |
901 response_writer_->Finish(base::Bind(&EmptyCompletionCallback)); | |
902 delegate_task_runner_->PostTask( | |
903 FROM_HERE, | |
904 base::Bind(&URLFetcherCore::InformDelegateFetchIsComplete, this)); | |
905 return; | |
906 } | |
907 | |
908 // Continue writing. | |
909 data->DidConsume(result); | |
910 if (WriteBuffer(data) < 0) | |
911 return; | |
912 | |
913 // Finished writing buffer_. Read some more, unless the request has been | |
914 // cancelled and deleted. | |
915 DCHECK_EQ(0, data->BytesRemaining()); | |
916 if (request_.get()) | |
917 ReadResponse(); | |
918 } | |
919 | |
920 void URLFetcherCore::ReadResponse() { | |
921 // Some servers may treat HEAD requests as GET requests. To free up the | |
922 // network connection as soon as possible, signal that the request has | |
923 // completed immediately, without trying to read any data back (all we care | |
924 // about is the response code and headers, which we already have). | |
925 int bytes_read = 0; | |
926 if (request_->status().is_success() && | |
927 (request_type_ != URLFetcher::HEAD)) { | |
928 if (!request_->Read(buffer_.get(), kBufferSize, &bytes_read)) | |
929 bytes_read = -1; // Match OnReadCompleted() interface contract. | |
930 } | |
931 OnReadCompleted(request_.get(), bytes_read); | |
932 } | |
933 | |
934 void URLFetcherCore::InformDelegateUploadProgress() { | |
935 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
936 if (request_.get()) { | |
937 int64 current = request_->GetUploadProgress().position(); | |
938 if (current_upload_bytes_ != current) { | |
939 current_upload_bytes_ = current; | |
940 int64 total = -1; | |
941 if (!is_chunked_upload_) { | |
942 total = static_cast<int64>(request_->GetUploadProgress().size()); | |
943 // Total may be zero if the UploadDataStream::Init has not been called | |
944 // yet. Don't send the upload progress until the size is initialized. | |
945 if (!total) | |
946 return; | |
947 } | |
948 delegate_task_runner_->PostTask( | |
949 FROM_HERE, | |
950 base::Bind( | |
951 &URLFetcherCore::InformDelegateUploadProgressInDelegateThread, | |
952 this, current, total)); | |
953 } | |
954 } | |
955 } | |
956 | |
957 void URLFetcherCore::InformDelegateUploadProgressInDelegateThread( | |
958 int64 current, int64 total) { | |
959 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
960 if (delegate_) | |
961 delegate_->OnURLFetchUploadProgress(fetcher_, current, total); | |
962 } | |
963 | |
964 void URLFetcherCore::InformDelegateDownloadProgress() { | |
965 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
966 tracked_objects::ScopedTracker tracking_profile1( | |
967 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
968 "423948 URLFetcherCore::InformDelegateDownloadProgress1")); | |
969 | |
970 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
971 | |
972 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
973 tracked_objects::ScopedTracker tracking_profile2( | |
974 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
975 "423948 URLFetcherCore::InformDelegateDownloadProgress2")); | |
976 | |
977 delegate_task_runner_->PostTask( | |
978 FROM_HERE, | |
979 base::Bind( | |
980 &URLFetcherCore::InformDelegateDownloadProgressInDelegateThread, | |
981 this, current_response_bytes_, total_response_bytes_)); | |
982 } | |
983 | |
984 void URLFetcherCore::InformDelegateDownloadProgressInDelegateThread( | |
985 int64 current, int64 total) { | |
986 DCHECK(delegate_task_runner_->BelongsToCurrentThread()); | |
987 if (delegate_) | |
988 delegate_->OnURLFetchDownloadProgress(fetcher_, current, total); | |
989 } | |
990 | |
991 void URLFetcherCore::AssertHasNoUploadData() const { | |
992 DCHECK(!upload_content_set_); | |
993 DCHECK(upload_content_.empty()); | |
994 DCHECK(upload_file_path_.empty()); | |
995 DCHECK(upload_stream_factory_.is_null()); | |
996 } | |
997 | |
998 } // namespace net | |
OLD | NEW |