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_request_job.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/compiler_specific.h" | |
9 #include "base/message_loop/message_loop.h" | |
10 #include "base/power_monitor/power_monitor.h" | |
11 #include "base/profiler/scoped_tracker.h" | |
12 #include "base/strings/string_number_conversions.h" | |
13 #include "base/strings/string_util.h" | |
14 #include "base/values.h" | |
15 #include "net/base/auth.h" | |
16 #include "net/base/host_port_pair.h" | |
17 #include "net/base/io_buffer.h" | |
18 #include "net/base/load_states.h" | |
19 #include "net/base/net_errors.h" | |
20 #include "net/base/network_delegate.h" | |
21 #include "net/filter/filter.h" | |
22 #include "net/http/http_response_headers.h" | |
23 | |
24 namespace net { | |
25 | |
26 namespace { | |
27 | |
28 // Callback for TYPE_URL_REQUEST_FILTERS_SET net-internals event. | |
29 base::Value* FiltersSetCallback(Filter* filter, | |
30 NetLog::LogLevel /* log_level */) { | |
31 base::DictionaryValue* event_params = new base::DictionaryValue(); | |
32 event_params->SetString("filters", filter->OrderedFilterList()); | |
33 return event_params; | |
34 } | |
35 | |
36 std::string ComputeMethodForRedirect(const std::string& method, | |
37 int http_status_code) { | |
38 // For 303 redirects, all request methods except HEAD are converted to GET, | |
39 // as per the latest httpbis draft. The draft also allows POST requests to | |
40 // be converted to GETs when following 301/302 redirects, for historical | |
41 // reasons. Most major browsers do this and so shall we. Both RFC 2616 and | |
42 // the httpbis draft say to prompt the user to confirm the generation of new | |
43 // requests, other than GET and HEAD requests, but IE omits these prompts and | |
44 // so shall we. | |
45 // See: | |
46 // https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-17#section-7.3 | |
47 if ((http_status_code == 303 && method != "HEAD") || | |
48 ((http_status_code == 301 || http_status_code == 302) && | |
49 method == "POST")) { | |
50 return "GET"; | |
51 } | |
52 return method; | |
53 } | |
54 | |
55 } // namespace | |
56 | |
57 URLRequestJob::URLRequestJob(URLRequest* request, | |
58 NetworkDelegate* network_delegate) | |
59 : request_(request), | |
60 done_(false), | |
61 prefilter_bytes_read_(0), | |
62 postfilter_bytes_read_(0), | |
63 filter_input_byte_count_(0), | |
64 filter_needs_more_output_space_(false), | |
65 filtered_read_buffer_len_(0), | |
66 has_handled_response_(false), | |
67 expected_content_size_(-1), | |
68 network_delegate_(network_delegate), | |
69 weak_factory_(this) { | |
70 base::PowerMonitor* power_monitor = base::PowerMonitor::Get(); | |
71 if (power_monitor) | |
72 power_monitor->AddObserver(this); | |
73 } | |
74 | |
75 void URLRequestJob::SetUpload(UploadDataStream* upload) { | |
76 } | |
77 | |
78 void URLRequestJob::SetExtraRequestHeaders(const HttpRequestHeaders& headers) { | |
79 } | |
80 | |
81 void URLRequestJob::SetPriority(RequestPriority priority) { | |
82 } | |
83 | |
84 void URLRequestJob::Kill() { | |
85 weak_factory_.InvalidateWeakPtrs(); | |
86 // Make sure the request is notified that we are done. We assume that the | |
87 // request took care of setting its error status before calling Kill. | |
88 if (request_) | |
89 NotifyCanceled(); | |
90 } | |
91 | |
92 void URLRequestJob::DetachRequest() { | |
93 request_ = NULL; | |
94 } | |
95 | |
96 // This function calls ReadData to get stream data. If a filter exists, passes | |
97 // the data to the attached filter. Then returns the output from filter back to | |
98 // the caller. | |
99 bool URLRequestJob::Read(IOBuffer* buf, int buf_size, int *bytes_read) { | |
100 bool rv = false; | |
101 | |
102 DCHECK_LT(buf_size, 1000000); // Sanity check. | |
103 DCHECK(buf); | |
104 DCHECK(bytes_read); | |
105 DCHECK(filtered_read_buffer_.get() == NULL); | |
106 DCHECK_EQ(0, filtered_read_buffer_len_); | |
107 | |
108 *bytes_read = 0; | |
109 | |
110 // Skip Filter if not present. | |
111 if (!filter_.get()) { | |
112 rv = ReadRawDataHelper(buf, buf_size, bytes_read); | |
113 } else { | |
114 // Save the caller's buffers while we do IO | |
115 // in the filter's buffers. | |
116 filtered_read_buffer_ = buf; | |
117 filtered_read_buffer_len_ = buf_size; | |
118 | |
119 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
120 tracked_objects::ScopedTracker tracking_profile2( | |
121 FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 URLRequestJob::Read2")); | |
122 | |
123 if (ReadFilteredData(bytes_read)) { | |
124 rv = true; // We have data to return. | |
125 | |
126 // It is fine to call DoneReading even if ReadFilteredData receives 0 | |
127 // bytes from the net, but we avoid making that call if we know for | |
128 // sure that's the case (ReadRawDataHelper path). | |
129 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is | |
130 // fixed. | |
131 tracked_objects::ScopedTracker tracking_profile3( | |
132 FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 URLRequestJob::Read3")); | |
133 | |
134 if (*bytes_read == 0) | |
135 DoneReading(); | |
136 } else { | |
137 rv = false; // Error, or a new IO is pending. | |
138 } | |
139 } | |
140 | |
141 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
142 tracked_objects::ScopedTracker tracking_profile4( | |
143 FROM_HERE_WITH_EXPLICIT_FUNCTION("423948 URLRequestJob::Read4")); | |
144 | |
145 if (rv && *bytes_read == 0) | |
146 NotifyDone(URLRequestStatus()); | |
147 return rv; | |
148 } | |
149 | |
150 void URLRequestJob::StopCaching() { | |
151 // Nothing to do here. | |
152 } | |
153 | |
154 bool URLRequestJob::GetFullRequestHeaders(HttpRequestHeaders* headers) const { | |
155 // Most job types don't send request headers. | |
156 return false; | |
157 } | |
158 | |
159 int64 URLRequestJob::GetTotalReceivedBytes() const { | |
160 return 0; | |
161 } | |
162 | |
163 LoadState URLRequestJob::GetLoadState() const { | |
164 return LOAD_STATE_IDLE; | |
165 } | |
166 | |
167 UploadProgress URLRequestJob::GetUploadProgress() const { | |
168 return UploadProgress(); | |
169 } | |
170 | |
171 bool URLRequestJob::GetCharset(std::string* charset) { | |
172 return false; | |
173 } | |
174 | |
175 void URLRequestJob::GetResponseInfo(HttpResponseInfo* info) { | |
176 } | |
177 | |
178 void URLRequestJob::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { | |
179 // Only certain request types return more than just request start times. | |
180 } | |
181 | |
182 bool URLRequestJob::GetResponseCookies(std::vector<std::string>* cookies) { | |
183 return false; | |
184 } | |
185 | |
186 Filter* URLRequestJob::SetupFilter() const { | |
187 return NULL; | |
188 } | |
189 | |
190 bool URLRequestJob::IsRedirectResponse(GURL* location, | |
191 int* http_status_code) { | |
192 // For non-HTTP jobs, headers will be null. | |
193 HttpResponseHeaders* headers = request_->response_headers(); | |
194 if (!headers) | |
195 return false; | |
196 | |
197 std::string value; | |
198 if (!headers->IsRedirect(&value)) | |
199 return false; | |
200 | |
201 *location = request_->url().Resolve(value); | |
202 *http_status_code = headers->response_code(); | |
203 return true; | |
204 } | |
205 | |
206 bool URLRequestJob::CopyFragmentOnRedirect(const GURL& location) const { | |
207 return true; | |
208 } | |
209 | |
210 bool URLRequestJob::IsSafeRedirect(const GURL& location) { | |
211 return true; | |
212 } | |
213 | |
214 bool URLRequestJob::NeedsAuth() { | |
215 return false; | |
216 } | |
217 | |
218 void URLRequestJob::GetAuthChallengeInfo( | |
219 scoped_refptr<AuthChallengeInfo>* auth_info) { | |
220 // This will only be called if NeedsAuth() returns true, in which | |
221 // case the derived class should implement this! | |
222 NOTREACHED(); | |
223 } | |
224 | |
225 void URLRequestJob::SetAuth(const AuthCredentials& credentials) { | |
226 // This will only be called if NeedsAuth() returns true, in which | |
227 // case the derived class should implement this! | |
228 NOTREACHED(); | |
229 } | |
230 | |
231 void URLRequestJob::CancelAuth() { | |
232 // This will only be called if NeedsAuth() returns true, in which | |
233 // case the derived class should implement this! | |
234 NOTREACHED(); | |
235 } | |
236 | |
237 void URLRequestJob::ContinueWithCertificate( | |
238 X509Certificate* client_cert) { | |
239 // The derived class should implement this! | |
240 NOTREACHED(); | |
241 } | |
242 | |
243 void URLRequestJob::ContinueDespiteLastError() { | |
244 // Implementations should know how to recover from errors they generate. | |
245 // If this code was reached, we are trying to recover from an error that | |
246 // we don't know how to recover from. | |
247 NOTREACHED(); | |
248 } | |
249 | |
250 void URLRequestJob::FollowDeferredRedirect() { | |
251 DCHECK_NE(-1, deferred_redirect_info_.status_code); | |
252 | |
253 // NOTE: deferred_redirect_info_ may be invalid, and attempting to follow it | |
254 // will fail inside FollowRedirect. The DCHECK above asserts that we called | |
255 // OnReceivedRedirect. | |
256 | |
257 // It is also possible that FollowRedirect will drop the last reference to | |
258 // this job, so we need to reset our members before calling it. | |
259 | |
260 RedirectInfo redirect_info = deferred_redirect_info_; | |
261 deferred_redirect_info_ = RedirectInfo(); | |
262 FollowRedirect(redirect_info); | |
263 } | |
264 | |
265 void URLRequestJob::ResumeNetworkStart() { | |
266 // This should only be called for HTTP Jobs, and implemented in the derived | |
267 // class. | |
268 NOTREACHED(); | |
269 } | |
270 | |
271 bool URLRequestJob::GetMimeType(std::string* mime_type) const { | |
272 return false; | |
273 } | |
274 | |
275 int URLRequestJob::GetResponseCode() const { | |
276 return -1; | |
277 } | |
278 | |
279 HostPortPair URLRequestJob::GetSocketAddress() const { | |
280 return HostPortPair(); | |
281 } | |
282 | |
283 void URLRequestJob::OnSuspend() { | |
284 Kill(); | |
285 } | |
286 | |
287 void URLRequestJob::NotifyURLRequestDestroyed() { | |
288 } | |
289 | |
290 // static | |
291 GURL URLRequestJob::ComputeReferrerForRedirect( | |
292 URLRequest::ReferrerPolicy policy, | |
293 const std::string& referrer, | |
294 const GURL& redirect_destination) { | |
295 GURL original_referrer(referrer); | |
296 bool secure_referrer_but_insecure_destination = | |
297 original_referrer.SchemeIsSecure() && | |
298 !redirect_destination.SchemeIsSecure(); | |
299 bool same_origin = | |
300 original_referrer.GetOrigin() == redirect_destination.GetOrigin(); | |
301 switch (policy) { | |
302 case URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE: | |
303 return secure_referrer_but_insecure_destination ? GURL() | |
304 : original_referrer; | |
305 | |
306 case URLRequest::REDUCE_REFERRER_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN: | |
307 if (same_origin) { | |
308 return original_referrer; | |
309 } else if (secure_referrer_but_insecure_destination) { | |
310 return GURL(); | |
311 } else { | |
312 return original_referrer.GetOrigin(); | |
313 } | |
314 | |
315 case URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN: | |
316 return same_origin ? original_referrer : original_referrer.GetOrigin(); | |
317 | |
318 case URLRequest::NEVER_CLEAR_REFERRER: | |
319 return original_referrer; | |
320 } | |
321 | |
322 NOTREACHED(); | |
323 return GURL(); | |
324 } | |
325 | |
326 URLRequestJob::~URLRequestJob() { | |
327 base::PowerMonitor* power_monitor = base::PowerMonitor::Get(); | |
328 if (power_monitor) | |
329 power_monitor->RemoveObserver(this); | |
330 } | |
331 | |
332 void URLRequestJob::NotifyCertificateRequested( | |
333 SSLCertRequestInfo* cert_request_info) { | |
334 if (!request_) | |
335 return; // The request was destroyed, so there is no more work to do. | |
336 | |
337 request_->NotifyCertificateRequested(cert_request_info); | |
338 } | |
339 | |
340 void URLRequestJob::NotifySSLCertificateError(const SSLInfo& ssl_info, | |
341 bool fatal) { | |
342 if (!request_) | |
343 return; // The request was destroyed, so there is no more work to do. | |
344 | |
345 request_->NotifySSLCertificateError(ssl_info, fatal); | |
346 } | |
347 | |
348 bool URLRequestJob::CanGetCookies(const CookieList& cookie_list) const { | |
349 if (!request_) | |
350 return false; // The request was destroyed, so there is no more work to do. | |
351 | |
352 return request_->CanGetCookies(cookie_list); | |
353 } | |
354 | |
355 bool URLRequestJob::CanSetCookie(const std::string& cookie_line, | |
356 CookieOptions* options) const { | |
357 if (!request_) | |
358 return false; // The request was destroyed, so there is no more work to do. | |
359 | |
360 return request_->CanSetCookie(cookie_line, options); | |
361 } | |
362 | |
363 bool URLRequestJob::CanEnablePrivacyMode() const { | |
364 if (!request_) | |
365 return false; // The request was destroyed, so there is no more work to do. | |
366 | |
367 return request_->CanEnablePrivacyMode(); | |
368 } | |
369 | |
370 CookieStore* URLRequestJob::GetCookieStore() const { | |
371 DCHECK(request_); | |
372 | |
373 return request_->cookie_store(); | |
374 } | |
375 | |
376 void URLRequestJob::NotifyBeforeNetworkStart(bool* defer) { | |
377 if (!request_) | |
378 return; | |
379 | |
380 request_->NotifyBeforeNetworkStart(defer); | |
381 } | |
382 | |
383 void URLRequestJob::NotifyHeadersComplete() { | |
384 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
385 tracked_objects::ScopedTracker tracking_profile( | |
386 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
387 "423948 URLRequestJob::NotifyHeadersComplete")); | |
388 | |
389 if (!request_ || !request_->has_delegate()) | |
390 return; // The request was destroyed, so there is no more work to do. | |
391 | |
392 if (has_handled_response_) | |
393 return; | |
394 | |
395 DCHECK(!request_->status().is_io_pending()); | |
396 | |
397 // Initialize to the current time, and let the subclass optionally override | |
398 // the time stamps if it has that information. The default request_time is | |
399 // set by URLRequest before it calls our Start method. | |
400 request_->response_info_.response_time = base::Time::Now(); | |
401 GetResponseInfo(&request_->response_info_); | |
402 | |
403 // When notifying the delegate, the delegate can release the request | |
404 // (and thus release 'this'). After calling to the delgate, we must | |
405 // check the request pointer to see if it still exists, and return | |
406 // immediately if it has been destroyed. self_preservation ensures our | |
407 // survival until we can get out of this method. | |
408 scoped_refptr<URLRequestJob> self_preservation(this); | |
409 | |
410 if (request_) { | |
411 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
412 tracked_objects::ScopedTracker tracking_profile1( | |
413 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
414 "423948 URLRequestJob::NotifyHeadersComplete 1")); | |
415 | |
416 request_->OnHeadersComplete(); | |
417 } | |
418 | |
419 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
420 tracked_objects::ScopedTracker tracking_profile2( | |
421 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
422 "423948 URLRequestJob::NotifyHeadersComplete 2")); | |
423 | |
424 GURL new_location; | |
425 int http_status_code; | |
426 if (IsRedirectResponse(&new_location, &http_status_code)) { | |
427 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
428 tracked_objects::ScopedTracker tracking_profile3( | |
429 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
430 "423948 URLRequestJob::NotifyHeadersComplete 3")); | |
431 | |
432 // Redirect response bodies are not read. Notify the transaction | |
433 // so it does not treat being stopped as an error. | |
434 DoneReadingRedirectResponse(); | |
435 | |
436 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
437 tracked_objects::ScopedTracker tracking_profile4( | |
438 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
439 "423948 URLRequestJob::NotifyHeadersComplete 4")); | |
440 | |
441 RedirectInfo redirect_info = | |
442 ComputeRedirectInfo(new_location, http_status_code); | |
443 | |
444 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
445 tracked_objects::ScopedTracker tracking_profile5( | |
446 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
447 "423948 URLRequestJob::NotifyHeadersComplete 5")); | |
448 | |
449 bool defer_redirect = false; | |
450 request_->NotifyReceivedRedirect(redirect_info, &defer_redirect); | |
451 | |
452 // Ensure that the request wasn't detached or destroyed in | |
453 // NotifyReceivedRedirect | |
454 if (!request_ || !request_->has_delegate()) | |
455 return; | |
456 | |
457 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
458 tracked_objects::ScopedTracker tracking_profile6( | |
459 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
460 "423948 URLRequestJob::NotifyHeadersComplete 6")); | |
461 | |
462 // If we were not cancelled, then maybe follow the redirect. | |
463 if (request_->status().is_success()) { | |
464 if (defer_redirect) { | |
465 deferred_redirect_info_ = redirect_info; | |
466 } else { | |
467 FollowRedirect(redirect_info); | |
468 } | |
469 return; | |
470 } | |
471 } else if (NeedsAuth()) { | |
472 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
473 tracked_objects::ScopedTracker tracking_profile7( | |
474 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
475 "423948 URLRequestJob::NotifyHeadersComplete 7")); | |
476 | |
477 scoped_refptr<AuthChallengeInfo> auth_info; | |
478 GetAuthChallengeInfo(&auth_info); | |
479 | |
480 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
481 tracked_objects::ScopedTracker tracking_profile8( | |
482 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
483 "423948 URLRequestJob::NotifyHeadersComplete 8")); | |
484 | |
485 // Need to check for a NULL auth_info because the server may have failed | |
486 // to send a challenge with the 401 response. | |
487 if (auth_info.get()) { | |
488 request_->NotifyAuthRequired(auth_info.get()); | |
489 // Wait for SetAuth or CancelAuth to be called. | |
490 return; | |
491 } | |
492 } | |
493 | |
494 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
495 tracked_objects::ScopedTracker tracking_profile9( | |
496 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
497 "423948 URLRequestJob::NotifyHeadersComplete 9")); | |
498 | |
499 has_handled_response_ = true; | |
500 if (request_->status().is_success()) | |
501 filter_.reset(SetupFilter()); | |
502 | |
503 if (!filter_.get()) { | |
504 std::string content_length; | |
505 request_->GetResponseHeaderByName("content-length", &content_length); | |
506 if (!content_length.empty()) | |
507 base::StringToInt64(content_length, &expected_content_size_); | |
508 } else { | |
509 request_->net_log().AddEvent( | |
510 NetLog::TYPE_URL_REQUEST_FILTERS_SET, | |
511 base::Bind(&FiltersSetCallback, base::Unretained(filter_.get()))); | |
512 } | |
513 | |
514 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
515 tracked_objects::ScopedTracker tracking_profile10( | |
516 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
517 "423948 URLRequestJob::NotifyHeadersComplete 10")); | |
518 | |
519 request_->NotifyResponseStarted(); | |
520 } | |
521 | |
522 void URLRequestJob::NotifyReadComplete(int bytes_read) { | |
523 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
524 tracked_objects::ScopedTracker tracking_profile( | |
525 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
526 "423948 URLRequestJob::NotifyReadComplete")); | |
527 | |
528 if (!request_ || !request_->has_delegate()) | |
529 return; // The request was destroyed, so there is no more work to do. | |
530 | |
531 // TODO(darin): Bug 1004233. Re-enable this test once all of the chrome | |
532 // unit_tests have been fixed to not trip this. | |
533 #if 0 | |
534 DCHECK(!request_->status().is_io_pending()); | |
535 #endif | |
536 // The headers should be complete before reads complete | |
537 DCHECK(has_handled_response_); | |
538 | |
539 OnRawReadComplete(bytes_read); | |
540 | |
541 // Don't notify if we had an error. | |
542 if (!request_->status().is_success()) | |
543 return; | |
544 | |
545 // When notifying the delegate, the delegate can release the request | |
546 // (and thus release 'this'). After calling to the delegate, we must | |
547 // check the request pointer to see if it still exists, and return | |
548 // immediately if it has been destroyed. self_preservation ensures our | |
549 // survival until we can get out of this method. | |
550 scoped_refptr<URLRequestJob> self_preservation(this); | |
551 | |
552 if (filter_.get()) { | |
553 // Tell the filter that it has more data | |
554 FilteredDataRead(bytes_read); | |
555 | |
556 // Filter the data. | |
557 int filter_bytes_read = 0; | |
558 if (ReadFilteredData(&filter_bytes_read)) { | |
559 if (!filter_bytes_read) | |
560 DoneReading(); | |
561 request_->NotifyReadCompleted(filter_bytes_read); | |
562 } | |
563 } else { | |
564 request_->NotifyReadCompleted(bytes_read); | |
565 } | |
566 DVLOG(1) << __FUNCTION__ << "() " | |
567 << "\"" << (request_ ? request_->url().spec() : "???") << "\"" | |
568 << " pre bytes read = " << bytes_read | |
569 << " pre total = " << prefilter_bytes_read_ | |
570 << " post total = " << postfilter_bytes_read_; | |
571 } | |
572 | |
573 void URLRequestJob::NotifyStartError(const URLRequestStatus &status) { | |
574 DCHECK(!has_handled_response_); | |
575 has_handled_response_ = true; | |
576 if (request_) { | |
577 // There may be relevant information in the response info even in the | |
578 // error case. | |
579 GetResponseInfo(&request_->response_info_); | |
580 | |
581 request_->set_status(status); | |
582 request_->NotifyResponseStarted(); | |
583 // We may have been deleted. | |
584 } | |
585 } | |
586 | |
587 void URLRequestJob::NotifyDone(const URLRequestStatus &status) { | |
588 DCHECK(!done_) << "Job sending done notification twice"; | |
589 if (done_) | |
590 return; | |
591 done_ = true; | |
592 | |
593 // Unless there was an error, we should have at least tried to handle | |
594 // the response before getting here. | |
595 DCHECK(has_handled_response_ || !status.is_success()); | |
596 | |
597 // As with NotifyReadComplete, we need to take care to notice if we were | |
598 // destroyed during a delegate callback. | |
599 if (request_) { | |
600 request_->set_is_pending(false); | |
601 // With async IO, it's quite possible to have a few outstanding | |
602 // requests. We could receive a request to Cancel, followed shortly | |
603 // by a successful IO. For tracking the status(), once there is | |
604 // an error, we do not change the status back to success. To | |
605 // enforce this, only set the status if the job is so far | |
606 // successful. | |
607 if (request_->status().is_success()) { | |
608 if (status.status() == URLRequestStatus::FAILED) { | |
609 request_->net_log().AddEventWithNetErrorCode(NetLog::TYPE_FAILED, | |
610 status.error()); | |
611 } | |
612 request_->set_status(status); | |
613 } | |
614 } | |
615 | |
616 // Complete this notification later. This prevents us from re-entering the | |
617 // delegate if we're done because of a synchronous call. | |
618 base::MessageLoop::current()->PostTask( | |
619 FROM_HERE, | |
620 base::Bind(&URLRequestJob::CompleteNotifyDone, | |
621 weak_factory_.GetWeakPtr())); | |
622 } | |
623 | |
624 void URLRequestJob::CompleteNotifyDone() { | |
625 // Check if we should notify the delegate that we're done because of an error. | |
626 if (request_ && | |
627 !request_->status().is_success() && | |
628 request_->has_delegate()) { | |
629 // We report the error differently depending on whether we've called | |
630 // OnResponseStarted yet. | |
631 if (has_handled_response_) { | |
632 // We signal the error by calling OnReadComplete with a bytes_read of -1. | |
633 request_->NotifyReadCompleted(-1); | |
634 } else { | |
635 has_handled_response_ = true; | |
636 request_->NotifyResponseStarted(); | |
637 } | |
638 } | |
639 } | |
640 | |
641 void URLRequestJob::NotifyCanceled() { | |
642 if (!done_) { | |
643 NotifyDone(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED)); | |
644 } | |
645 } | |
646 | |
647 void URLRequestJob::NotifyRestartRequired() { | |
648 DCHECK(!has_handled_response_); | |
649 if (GetStatus().status() != URLRequestStatus::CANCELED) | |
650 request_->Restart(); | |
651 } | |
652 | |
653 void URLRequestJob::OnCallToDelegate() { | |
654 request_->OnCallToDelegate(); | |
655 } | |
656 | |
657 void URLRequestJob::OnCallToDelegateComplete() { | |
658 request_->OnCallToDelegateComplete(); | |
659 } | |
660 | |
661 bool URLRequestJob::ReadRawData(IOBuffer* buf, int buf_size, | |
662 int *bytes_read) { | |
663 DCHECK(bytes_read); | |
664 *bytes_read = 0; | |
665 return true; | |
666 } | |
667 | |
668 void URLRequestJob::DoneReading() { | |
669 // Do nothing. | |
670 } | |
671 | |
672 void URLRequestJob::DoneReadingRedirectResponse() { | |
673 } | |
674 | |
675 void URLRequestJob::FilteredDataRead(int bytes_read) { | |
676 DCHECK(filter_); | |
677 filter_->FlushStreamBuffer(bytes_read); | |
678 } | |
679 | |
680 bool URLRequestJob::ReadFilteredData(int* bytes_read) { | |
681 DCHECK(filter_); | |
682 DCHECK(filtered_read_buffer_.get()); | |
683 DCHECK_GT(filtered_read_buffer_len_, 0); | |
684 DCHECK_LT(filtered_read_buffer_len_, 1000000); // Sanity check. | |
685 DCHECK(!raw_read_buffer_.get()); | |
686 | |
687 *bytes_read = 0; | |
688 bool rv = false; | |
689 | |
690 for (;;) { | |
691 if (is_done()) | |
692 return true; | |
693 | |
694 if (!filter_needs_more_output_space_ && !filter_->stream_data_len()) { | |
695 // We don't have any raw data to work with, so read from the transaction. | |
696 int filtered_data_read; | |
697 if (ReadRawDataForFilter(&filtered_data_read)) { | |
698 if (filtered_data_read > 0) { | |
699 // Give data to filter. | |
700 filter_->FlushStreamBuffer(filtered_data_read); | |
701 } else { | |
702 return true; // EOF. | |
703 } | |
704 } else { | |
705 return false; // IO Pending (or error). | |
706 } | |
707 } | |
708 | |
709 if ((filter_->stream_data_len() || filter_needs_more_output_space_) && | |
710 !is_done()) { | |
711 // Get filtered data. | |
712 int filtered_data_len = filtered_read_buffer_len_; | |
713 int output_buffer_size = filtered_data_len; | |
714 Filter::FilterStatus status = | |
715 filter_->ReadData(filtered_read_buffer_->data(), &filtered_data_len); | |
716 | |
717 if (filter_needs_more_output_space_ && !filtered_data_len) { | |
718 // filter_needs_more_output_space_ was mistaken... there are no more | |
719 // bytes and we should have at least tried to fill up the filter's input | |
720 // buffer. Correct the state, and try again. | |
721 filter_needs_more_output_space_ = false; | |
722 continue; | |
723 } | |
724 filter_needs_more_output_space_ = | |
725 (filtered_data_len == output_buffer_size); | |
726 | |
727 switch (status) { | |
728 case Filter::FILTER_DONE: { | |
729 filter_needs_more_output_space_ = false; | |
730 *bytes_read = filtered_data_len; | |
731 postfilter_bytes_read_ += filtered_data_len; | |
732 rv = true; | |
733 break; | |
734 } | |
735 case Filter::FILTER_NEED_MORE_DATA: { | |
736 // We have finished filtering all data currently in the buffer. | |
737 // There might be some space left in the output buffer. One can | |
738 // consider reading more data from the stream to feed the filter | |
739 // and filling up the output buffer. This leads to more complicated | |
740 // buffer management and data notification mechanisms. | |
741 // We can revisit this issue if there is a real perf need. | |
742 if (filtered_data_len > 0) { | |
743 *bytes_read = filtered_data_len; | |
744 postfilter_bytes_read_ += filtered_data_len; | |
745 rv = true; | |
746 } else { | |
747 // Read again since we haven't received enough data yet (e.g., we | |
748 // may not have a complete gzip header yet). | |
749 continue; | |
750 } | |
751 break; | |
752 } | |
753 case Filter::FILTER_OK: { | |
754 *bytes_read = filtered_data_len; | |
755 postfilter_bytes_read_ += filtered_data_len; | |
756 rv = true; | |
757 break; | |
758 } | |
759 case Filter::FILTER_ERROR: { | |
760 DVLOG(1) << __FUNCTION__ << "() " | |
761 << "\"" << (request_ ? request_->url().spec() : "???") | |
762 << "\"" << " Filter Error"; | |
763 filter_needs_more_output_space_ = false; | |
764 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, | |
765 ERR_CONTENT_DECODING_FAILED)); | |
766 rv = false; | |
767 break; | |
768 } | |
769 default: { | |
770 NOTREACHED(); | |
771 filter_needs_more_output_space_ = false; | |
772 rv = false; | |
773 break; | |
774 } | |
775 } | |
776 | |
777 // If logging all bytes is enabled, log the filtered bytes read. | |
778 if (rv && request() && request()->net_log().IsLoggingBytes() && | |
779 filtered_data_len > 0) { | |
780 request()->net_log().AddByteTransferEvent( | |
781 NetLog::TYPE_URL_REQUEST_JOB_FILTERED_BYTES_READ, | |
782 filtered_data_len, filtered_read_buffer_->data()); | |
783 } | |
784 } else { | |
785 // we are done, or there is no data left. | |
786 rv = true; | |
787 } | |
788 break; | |
789 } | |
790 | |
791 if (rv) { | |
792 // When we successfully finished a read, we no longer need to save the | |
793 // caller's buffers. Release our reference. | |
794 filtered_read_buffer_ = NULL; | |
795 filtered_read_buffer_len_ = 0; | |
796 } | |
797 return rv; | |
798 } | |
799 | |
800 void URLRequestJob::DestroyFilters() { | |
801 filter_.reset(); | |
802 } | |
803 | |
804 const URLRequestStatus URLRequestJob::GetStatus() { | |
805 if (request_) | |
806 return request_->status(); | |
807 // If the request is gone, we must be cancelled. | |
808 return URLRequestStatus(URLRequestStatus::CANCELED, | |
809 ERR_ABORTED); | |
810 } | |
811 | |
812 void URLRequestJob::SetStatus(const URLRequestStatus &status) { | |
813 if (request_) | |
814 request_->set_status(status); | |
815 } | |
816 | |
817 void URLRequestJob::SetProxyServer(const HostPortPair& proxy_server) { | |
818 request_->proxy_server_ = proxy_server; | |
819 } | |
820 | |
821 bool URLRequestJob::ReadRawDataForFilter(int* bytes_read) { | |
822 bool rv = false; | |
823 | |
824 DCHECK(bytes_read); | |
825 DCHECK(filter_.get()); | |
826 | |
827 *bytes_read = 0; | |
828 | |
829 // Get more pre-filtered data if needed. | |
830 // TODO(mbelshe): is it possible that the filter needs *MORE* data | |
831 // when there is some data already in the buffer? | |
832 if (!filter_->stream_data_len() && !is_done()) { | |
833 IOBuffer* stream_buffer = filter_->stream_buffer(); | |
834 int stream_buffer_size = filter_->stream_buffer_size(); | |
835 rv = ReadRawDataHelper(stream_buffer, stream_buffer_size, bytes_read); | |
836 } | |
837 return rv; | |
838 } | |
839 | |
840 bool URLRequestJob::ReadRawDataHelper(IOBuffer* buf, int buf_size, | |
841 int* bytes_read) { | |
842 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
843 tracked_objects::ScopedTracker tracking_profile( | |
844 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
845 "423948 URLRequestJob::ReadRawDataHelper")); | |
846 | |
847 DCHECK(!request_->status().is_io_pending()); | |
848 DCHECK(raw_read_buffer_.get() == NULL); | |
849 | |
850 // Keep a pointer to the read buffer, so we have access to it in the | |
851 // OnRawReadComplete() callback in the event that the read completes | |
852 // asynchronously. | |
853 raw_read_buffer_ = buf; | |
854 bool rv = ReadRawData(buf, buf_size, bytes_read); | |
855 | |
856 if (!request_->status().is_io_pending()) { | |
857 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
858 tracked_objects::ScopedTracker tracking_profile1( | |
859 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
860 "423948 URLRequestJob::ReadRawDataHelper1")); | |
861 | |
862 // If the read completes synchronously, either success or failure, | |
863 // invoke the OnRawReadComplete callback so we can account for the | |
864 // completed read. | |
865 OnRawReadComplete(*bytes_read); | |
866 } | |
867 return rv; | |
868 } | |
869 | |
870 void URLRequestJob::FollowRedirect(const RedirectInfo& redirect_info) { | |
871 int rv = request_->Redirect(redirect_info); | |
872 if (rv != OK) | |
873 NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); | |
874 } | |
875 | |
876 void URLRequestJob::OnRawReadComplete(int bytes_read) { | |
877 DCHECK(raw_read_buffer_.get()); | |
878 // If |filter_| is non-NULL, bytes will be logged after it is applied instead. | |
879 if (!filter_.get() && request() && request()->net_log().IsLoggingBytes() && | |
880 bytes_read > 0) { | |
881 request()->net_log().AddByteTransferEvent( | |
882 NetLog::TYPE_URL_REQUEST_JOB_BYTES_READ, | |
883 bytes_read, raw_read_buffer_->data()); | |
884 } | |
885 | |
886 if (bytes_read > 0) { | |
887 RecordBytesRead(bytes_read); | |
888 } | |
889 raw_read_buffer_ = NULL; | |
890 } | |
891 | |
892 void URLRequestJob::RecordBytesRead(int bytes_read) { | |
893 filter_input_byte_count_ += bytes_read; | |
894 prefilter_bytes_read_ += bytes_read; | |
895 if (!filter_.get()) | |
896 postfilter_bytes_read_ += bytes_read; | |
897 DVLOG(2) << __FUNCTION__ << "() " | |
898 << "\"" << (request_ ? request_->url().spec() : "???") << "\"" | |
899 << " pre bytes read = " << bytes_read | |
900 << " pre total = " << prefilter_bytes_read_ | |
901 << " post total = " << postfilter_bytes_read_; | |
902 UpdatePacketReadTimes(); // Facilitate stats recording if it is active. | |
903 if (network_delegate_) { | |
904 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed. | |
905 tracked_objects::ScopedTracker tracking_profile( | |
906 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
907 "423948 URLRequestJob::RecordBytesRead NotifyRawBytesRead")); | |
908 | |
909 network_delegate_->NotifyRawBytesRead(*request_, bytes_read); | |
910 } | |
911 } | |
912 | |
913 bool URLRequestJob::FilterHasData() { | |
914 return filter_.get() && filter_->stream_data_len(); | |
915 } | |
916 | |
917 void URLRequestJob::UpdatePacketReadTimes() { | |
918 } | |
919 | |
920 RedirectInfo URLRequestJob::ComputeRedirectInfo(const GURL& location, | |
921 int http_status_code) { | |
922 const GURL& url = request_->url(); | |
923 | |
924 RedirectInfo redirect_info; | |
925 | |
926 redirect_info.status_code = http_status_code; | |
927 | |
928 // The request method may change, depending on the status code. | |
929 redirect_info.new_method = | |
930 ComputeMethodForRedirect(request_->method(), http_status_code); | |
931 | |
932 // Move the reference fragment of the old location to the new one if the | |
933 // new one has none. This duplicates mozilla's behavior. | |
934 if (url.is_valid() && url.has_ref() && !location.has_ref() && | |
935 CopyFragmentOnRedirect(location)) { | |
936 GURL::Replacements replacements; | |
937 // Reference the |ref| directly out of the original URL to avoid a | |
938 // malloc. | |
939 replacements.SetRef(url.spec().data(), | |
940 url.parsed_for_possibly_invalid_spec().ref); | |
941 redirect_info.new_url = location.ReplaceComponents(replacements); | |
942 } else { | |
943 redirect_info.new_url = location; | |
944 } | |
945 | |
946 // Update the first-party URL if appropriate. | |
947 if (request_->first_party_url_policy() == | |
948 URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT) { | |
949 redirect_info.new_first_party_for_cookies = redirect_info.new_url; | |
950 } else { | |
951 redirect_info.new_first_party_for_cookies = | |
952 request_->first_party_for_cookies(); | |
953 } | |
954 | |
955 // Alter the referrer if redirecting cross-origin (especially HTTP->HTTPS). | |
956 redirect_info.new_referrer = | |
957 ComputeReferrerForRedirect(request_->referrer_policy(), | |
958 request_->referrer(), | |
959 redirect_info.new_url).spec(); | |
960 | |
961 return redirect_info; | |
962 } | |
963 | |
964 } // namespace net | |
OLD | NEW |