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