url_request.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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.h"
6
7#include "base/compiler_specific.h"
8#include "base/message_loop.h"
9#include "base/metrics/stats_counters.h"
10#include "base/singleton.h"
11#include "net/base/load_flags.h"
12#include "net/base/net_errors.h"
13#include "net/base/net_log.h"
14#include "net/base/ssl_cert_request_info.h"
15#include "net/base/upload_data.h"
16#include "net/http/http_response_headers.h"
17#include "net/http/http_util.h"
18#include "net/url_request/url_request_context.h"
19#include "net/url_request/url_request_job.h"
20#include "net/url_request/url_request_job_manager.h"
21#include "net/url_request/url_request_netlog_params.h"
22
23using base::Time;
24using net::UploadData;
25using std::string;
26
27namespace {
28
29// Max number of http redirects to follow.  Same number as gecko.
30const int kMaxRedirects = 20;
31
32URLRequestJobManager* GetJobManager() {
33  return Singleton<URLRequestJobManager>::get();
34}
35
36// Discard headers which have meaning in POST (Content-Length, Content-Type,
37// Origin).
38void StripPostSpecificHeaders(net::HttpRequestHeaders* headers) {
39  // These are headers that may be attached to a POST.
40  headers->RemoveHeader(net::HttpRequestHeaders::kContentLength);
41  headers->RemoveHeader(net::HttpRequestHeaders::kContentType);
42  headers->RemoveHeader(net::HttpRequestHeaders::kOrigin);
43}
44
45}  // namespace
46
47///////////////////////////////////////////////////////////////////////////////
48// URLRequest::Interceptor
49
50URLRequestJob* URLRequest::Interceptor::MaybeInterceptRedirect(
51    URLRequest* request,
52    const GURL& location) {
53  return NULL;
54}
55
56URLRequestJob* URLRequest::Interceptor::MaybeInterceptResponse(
57    URLRequest* request) {
58  return NULL;
59}
60
61///////////////////////////////////////////////////////////////////////////////
62// URLRequest::Delegate
63
64void URLRequest::Delegate::OnReceivedRedirect(URLRequest* request,
65                                              const GURL& new_url,
66                                              bool* defer_redirect) {
67}
68
69void URLRequest::Delegate::OnAuthRequired(URLRequest* request,
70                                          net::AuthChallengeInfo* auth_info) {
71  request->CancelAuth();
72}
73
74void URLRequest::Delegate::OnCertificateRequested(
75    URLRequest* request,
76    net::SSLCertRequestInfo* cert_request_info) {
77  request->ContinueWithCertificate(NULL);
78}
79
80void URLRequest::Delegate::OnSSLCertificateError(URLRequest* request,
81                                                 int cert_error,
82                                                 net::X509Certificate* cert) {
83  request->Cancel();
84}
85
86void URLRequest::Delegate::OnGetCookies(URLRequest* request,
87                                        bool blocked_by_policy) {
88}
89
90void URLRequest::Delegate::OnSetCookie(URLRequest* request,
91                                       const std::string& cookie_line,
92                                       bool blocked_by_policy) {
93}
94
95///////////////////////////////////////////////////////////////////////////////
96// URLRequest
97
98URLRequest::URLRequest(const GURL& url, Delegate* delegate)
99    : url_(url),
100      original_url_(url),
101      method_("GET"),
102      load_flags_(net::LOAD_NORMAL),
103      delegate_(delegate),
104      is_pending_(false),
105      enable_profiling_(false),
106      redirect_limit_(kMaxRedirects),
107      final_upload_progress_(0),
108      priority_(net::LOWEST) {
109  SIMPLE_STATS_COUNTER("URLRequestCount");
110
111  // Sanity check out environment.
112  DCHECK(MessageLoop::current()) <<
113      "The current MessageLoop must exist";
114  DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type()) <<
115      "The current MessageLoop must be TYPE_IO";
116}
117
118URLRequest::~URLRequest() {
119  Cancel();
120
121  if (job_)
122    OrphanJob();
123
124  set_context(NULL);
125}
126
127// static
128URLRequest::ProtocolFactory* URLRequest::RegisterProtocolFactory(
129    const string& scheme, ProtocolFactory* factory) {
130  return GetJobManager()->RegisterProtocolFactory(scheme, factory);
131}
132
133// static
134void URLRequest::RegisterRequestInterceptor(Interceptor* interceptor) {
135  GetJobManager()->RegisterRequestInterceptor(interceptor);
136}
137
138// static
139void URLRequest::UnregisterRequestInterceptor(Interceptor* interceptor) {
140  GetJobManager()->UnregisterRequestInterceptor(interceptor);
141}
142
143void URLRequest::AppendBytesToUpload(const char* bytes, int bytes_len) {
144  DCHECK(bytes_len > 0 && bytes);
145  if (!upload_)
146    upload_ = new UploadData();
147  upload_->AppendBytes(bytes, bytes_len);
148}
149
150void URLRequest::AppendFileRangeToUpload(
151    const FilePath& file_path,
152    uint64 offset,
153    uint64 length,
154    const base::Time& expected_modification_time) {
155  DCHECK(file_path.value().length() > 0 && length > 0);
156  if (!upload_)
157    upload_ = new UploadData();
158  upload_->AppendFileRange(file_path, offset, length,
159                           expected_modification_time);
160}
161
162void URLRequest::set_upload(net::UploadData* upload) {
163  upload_ = upload;
164}
165
166// Get the upload data directly.
167net::UploadData* URLRequest::get_upload() {
168  return upload_.get();
169}
170
171bool URLRequest::has_upload() const {
172  return upload_ != NULL;
173}
174
175void URLRequest::SetExtraRequestHeaderById(int id, const string& value,
176                                           bool overwrite) {
177  DCHECK(!is_pending_);
178  NOTREACHED() << "implement me!";
179}
180
181void URLRequest::SetExtraRequestHeaderByName(const string& name,
182                                             const string& value,
183                                             bool overwrite) {
184  DCHECK(!is_pending_);
185  NOTREACHED() << "implement me!";
186}
187
188void URLRequest::SetExtraRequestHeaders(
189    const net::HttpRequestHeaders& headers) {
190  DCHECK(!is_pending_);
191  extra_request_headers_ = headers;
192
193  // NOTE: This method will likely become non-trivial once the other setters
194  // for request headers are implemented.
195}
196
197net::LoadState URLRequest::GetLoadState() const {
198  return job_ ? job_->GetLoadState() : net::LOAD_STATE_IDLE;
199}
200
201uint64 URLRequest::GetUploadProgress() const {
202  if (!job_) {
203    // We haven't started or the request was cancelled
204    return 0;
205  }
206  if (final_upload_progress_) {
207    // The first job completed and none of the subsequent series of
208    // GETs when following redirects will upload anything, so we return the
209    // cached results from the initial job, the POST.
210    return final_upload_progress_;
211  }
212  return job_->GetUploadProgress();
213}
214
215void URLRequest::GetResponseHeaderById(int id, string* value) {
216  DCHECK(job_);
217  NOTREACHED() << "implement me!";
218}
219
220void URLRequest::GetResponseHeaderByName(const string& name, string* value) {
221  DCHECK(value);
222  if (response_info_.headers) {
223    response_info_.headers->GetNormalizedHeader(name, value);
224  } else {
225    value->clear();
226  }
227}
228
229void URLRequest::GetAllResponseHeaders(string* headers) {
230  DCHECK(headers);
231  if (response_info_.headers) {
232    response_info_.headers->GetNormalizedHeaders(headers);
233  } else {
234    headers->clear();
235  }
236}
237
238net::HttpResponseHeaders* URLRequest::response_headers() const {
239  return response_info_.headers.get();
240}
241
242bool URLRequest::GetResponseCookies(ResponseCookies* cookies) {
243  DCHECK(job_);
244  return job_->GetResponseCookies(cookies);
245}
246
247void URLRequest::GetMimeType(string* mime_type) {
248  DCHECK(job_);
249  job_->GetMimeType(mime_type);
250}
251
252void URLRequest::GetCharset(string* charset) {
253  DCHECK(job_);
254  job_->GetCharset(charset);
255}
256
257int URLRequest::GetResponseCode() {
258  DCHECK(job_);
259  return job_->GetResponseCode();
260}
261
262// static
263bool URLRequest::IsHandledProtocol(const std::string& scheme) {
264  return GetJobManager()->SupportsScheme(scheme);
265}
266
267// static
268bool URLRequest::IsHandledURL(const GURL& url) {
269  if (!url.is_valid()) {
270    // We handle error cases.
271    return true;
272  }
273
274  return IsHandledProtocol(url.scheme());
275}
276
277// static
278void URLRequest::AllowFileAccess() {
279  GetJobManager()->set_enable_file_access(true);
280}
281
282// static
283bool URLRequest::IsFileAccessAllowed() {
284  return GetJobManager()->enable_file_access();
285}
286
287
288void URLRequest::set_first_party_for_cookies(
289    const GURL& first_party_for_cookies) {
290  first_party_for_cookies_ = first_party_for_cookies;
291}
292
293void URLRequest::set_method(const std::string& method) {
294  DCHECK(!is_pending_);
295  method_ = method;
296}
297
298void URLRequest::set_referrer(const std::string& referrer) {
299  DCHECK(!is_pending_);
300  referrer_ = referrer;
301}
302
303GURL URLRequest::GetSanitizedReferrer() const {
304  GURL ret(referrer());
305
306  // Ensure that we do not send username and password fields in the referrer.
307  if (ret.has_username() || ret.has_password()) {
308    GURL::Replacements referrer_mods;
309    referrer_mods.ClearUsername();
310    referrer_mods.ClearPassword();
311    ret = ret.ReplaceComponents(referrer_mods);
312  }
313
314  return ret;
315}
316
317void URLRequest::Start() {
318  StartJob(GetJobManager()->CreateJob(this));
319}
320
321///////////////////////////////////////////////////////////////////////////////
322
323void URLRequest::StartJob(URLRequestJob* job) {
324  DCHECK(!is_pending_);
325  DCHECK(!job_);
326
327  net_log_.BeginEvent(
328      net::NetLog::TYPE_URL_REQUEST_START_JOB,
329      make_scoped_refptr(new URLRequestStartEventParameters(
330          url_, method_, load_flags_, priority_)));
331
332  job_ = job;
333  job_->SetExtraRequestHeaders(extra_request_headers_);
334
335  if (upload_.get())
336    job_->SetUpload(upload_.get());
337
338  is_pending_ = true;
339
340  response_info_.request_time = Time::Now();
341  response_info_.was_cached = false;
342
343  // Don't allow errors to be sent from within Start().
344  // TODO(brettw) this may cause NotifyDone to be sent synchronously,
345  // we probably don't want this: they should be sent asynchronously so
346  // the caller does not get reentered.
347  job_->Start();
348}
349
350void URLRequest::Restart() {
351  // Should only be called if the original job didn't make any progress.
352  DCHECK(job_ && !job_->has_response_started());
353  RestartWithJob(GetJobManager()->CreateJob(this));
354}
355
356void URLRequest::RestartWithJob(URLRequestJob *job) {
357  DCHECK(job->request() == this);
358  PrepareToRestart();
359  StartJob(job);
360}
361
362void URLRequest::Cancel() {
363  DoCancel(net::ERR_ABORTED, net::SSLInfo());
364}
365
366void URLRequest::SimulateError(int os_error) {
367  DoCancel(os_error, net::SSLInfo());
368}
369
370void URLRequest::SimulateSSLError(int os_error, const net::SSLInfo& ssl_info) {
371  // This should only be called on a started request.
372  if (!is_pending_ || !job_ || job_->has_response_started()) {
373    NOTREACHED();
374    return;
375  }
376  DoCancel(os_error, ssl_info);
377}
378
379void URLRequest::DoCancel(int os_error, const net::SSLInfo& ssl_info) {
380  DCHECK(os_error < 0);
381
382  // If the URL request already has an error status, then canceling is a no-op.
383  // Plus, we don't want to change the error status once it has been set.
384  if (status_.is_success()) {
385    status_.set_status(URLRequestStatus::CANCELED);
386    status_.set_os_error(os_error);
387    response_info_.ssl_info = ssl_info;
388  }
389
390  // There's nothing to do if we are not waiting on a Job.
391  if (!is_pending_ || !job_)
392    return;
393
394  job_->Kill();
395
396  // The Job will call our NotifyDone method asynchronously.  This is done so
397  // that the Delegate implementation can call Cancel without having to worry
398  // about being called recursively.
399}
400
401bool URLRequest::Read(net::IOBuffer* dest, int dest_size, int* bytes_read) {
402  DCHECK(job_);
403  DCHECK(bytes_read);
404  DCHECK(!job_->is_done());
405  *bytes_read = 0;
406
407  if (dest_size == 0) {
408    // Caller is not too bright.  I guess we've done what they asked.
409    return true;
410  }
411
412  // Once the request fails or is cancelled, read will just return 0 bytes
413  // to indicate end of stream.
414  if (!status_.is_success()) {
415    return true;
416  }
417
418  return job_->Read(dest, dest_size, bytes_read);
419}
420
421void URLRequest::StopCaching() {
422  DCHECK(job_);
423  job_->StopCaching();
424}
425
426void URLRequest::ReceivedRedirect(const GURL& location, bool* defer_redirect) {
427  URLRequestJob* job = GetJobManager()->MaybeInterceptRedirect(this, location);
428  if (job) {
429    RestartWithJob(job);
430  } else if (delegate_) {
431    delegate_->OnReceivedRedirect(this, location, defer_redirect);
432  }
433}
434
435void URLRequest::ResponseStarted() {
436  scoped_refptr<net::NetLog::EventParameters> params;
437  if (!status_.is_success())
438    params = new net::NetLogIntegerParameter("net_error", status_.os_error());
439  net_log_.EndEvent(net::NetLog::TYPE_URL_REQUEST_START_JOB, params);
440
441  URLRequestJob* job = GetJobManager()->MaybeInterceptResponse(this);
442  if (job) {
443    RestartWithJob(job);
444  } else if (delegate_) {
445    delegate_->OnResponseStarted(this);
446  }
447}
448
449void URLRequest::FollowDeferredRedirect() {
450  CHECK(job_);
451  CHECK(status_.is_success());
452
453  job_->FollowDeferredRedirect();
454}
455
456void URLRequest::SetAuth(const string16& username, const string16& password) {
457  DCHECK(job_);
458  DCHECK(job_->NeedsAuth());
459
460  job_->SetAuth(username, password);
461}
462
463void URLRequest::CancelAuth() {
464  DCHECK(job_);
465  DCHECK(job_->NeedsAuth());
466
467  job_->CancelAuth();
468}
469
470void URLRequest::ContinueWithCertificate(net::X509Certificate* client_cert) {
471  DCHECK(job_);
472
473  job_->ContinueWithCertificate(client_cert);
474}
475
476void URLRequest::ContinueDespiteLastError() {
477  DCHECK(job_);
478
479  job_->ContinueDespiteLastError();
480}
481
482void URLRequest::PrepareToRestart() {
483  DCHECK(job_);
484
485  // Close the current URL_REQUEST_START_JOB, since we will be starting a new
486  // one.
487  net_log_.EndEvent(net::NetLog::TYPE_URL_REQUEST_START_JOB, NULL);
488
489  job_->Kill();
490  OrphanJob();
491
492  response_info_ = net::HttpResponseInfo();
493  status_ = URLRequestStatus();
494  is_pending_ = false;
495}
496
497void URLRequest::OrphanJob() {
498  job_->Kill();
499  job_->DetachRequest();  // ensures that the job will not call us again
500  job_ = NULL;
501}
502
503int URLRequest::Redirect(const GURL& location, int http_status_code) {
504  if (net_log_.IsLoggingAllEvents()) {
505    net_log_.AddEvent(
506        net::NetLog::TYPE_URL_REQUEST_REDIRECTED,
507        make_scoped_refptr(new net::NetLogStringParameter(
508            "location", location.possibly_invalid_spec())));
509  }
510  if (redirect_limit_ <= 0) {
511    DVLOG(1) << "disallowing redirect: exceeds limit";
512    return net::ERR_TOO_MANY_REDIRECTS;
513  }
514
515  if (!location.is_valid())
516    return net::ERR_INVALID_URL;
517
518  if (!job_->IsSafeRedirect(location)) {
519    DVLOG(1) << "disallowing redirect: unsafe protocol";
520    return net::ERR_UNSAFE_REDIRECT;
521  }
522
523  bool strip_post_specific_headers = false;
524  if (http_status_code != 307) {
525    // NOTE: Even though RFC 2616 says to preserve the request method when
526    // following a 302 redirect, normal browsers don't do that.  Instead, they
527    // all convert a POST into a GET in response to a 302 and so shall we.  For
528    // 307 redirects, browsers preserve the method.  The RFC says to prompt the
529    // user to confirm the generation of a new POST request, but IE omits this
530    // prompt and so shall we.
531    strip_post_specific_headers = method_ == "POST";
532    method_ = "GET";
533    upload_ = NULL;
534  }
535
536  // Suppress the referrer if we're redirecting out of https.
537  if (GURL(referrer_).SchemeIsSecure() && !location.SchemeIsSecure())
538    referrer_.clear();
539
540  url_ = location;
541  --redirect_limit_;
542
543  if (strip_post_specific_headers) {
544    // If being switched from POST to GET, must remove headers that were
545    // specific to the POST and don't have meaning in GET. For example
546    // the inclusion of a multipart Content-Type header in GET can cause
547    // problems with some servers:
548    // http://code.google.com/p/chromium/issues/detail?id=843
549    StripPostSpecificHeaders(&extra_request_headers_);
550  }
551
552  if (!final_upload_progress_)
553    final_upload_progress_ = job_->GetUploadProgress();
554
555  PrepareToRestart();
556  Start();
557  return net::OK;
558}
559
560URLRequestContext* URLRequest::context() {
561  return context_.get();
562}
563
564void URLRequest::set_context(URLRequestContext* context) {
565  scoped_refptr<URLRequestContext> prev_context = context_;
566
567  context_ = context;
568
569  // If the context this request belongs to has changed, update the tracker.
570  if (prev_context != context) {
571    net_log_.EndEvent(net::NetLog::TYPE_REQUEST_ALIVE, NULL);
572    net_log_ = net::BoundNetLog();
573
574    if (context) {
575      net_log_ = net::BoundNetLog::Make(context->net_log(),
576                                        net::NetLog::SOURCE_URL_REQUEST);
577      net_log_.BeginEvent(net::NetLog::TYPE_REQUEST_ALIVE, NULL);
578    }
579  }
580}
581
582int64 URLRequest::GetExpectedContentSize() const {
583  int64 expected_content_size = -1;
584  if (job_)
585    expected_content_size = job_->expected_content_size();
586
587  return expected_content_size;
588}
589
590URLRequest::UserData* URLRequest::GetUserData(const void* key) const {
591  UserDataMap::const_iterator found = user_data_.find(key);
592  if (found != user_data_.end())
593    return found->second.get();
594  return NULL;
595}
596
597void URLRequest::SetUserData(const void* key, UserData* data) {
598  user_data_[key] = linked_ptr<UserData>(data);
599}
600