Home | History | Annotate | Download | only in url_request
      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_manager.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/memory/singleton.h"
     10 #include "build/build_config.h"
     11 #include "base/strings/string_util.h"
     12 #include "net/base/load_flags.h"
     13 #include "net/base/net_errors.h"
     14 #include "net/base/network_delegate.h"
     15 #include "net/url_request/url_request_context.h"
     16 #include "net/url_request/url_request_error_job.h"
     17 #include "net/url_request/url_request_http_job.h"
     18 #include "net/url_request/url_request_job_factory.h"
     19 
     20 namespace net {
     21 
     22 // The built-in set of protocol factories
     23 namespace {
     24 
     25 struct SchemeToFactory {
     26   const char* scheme;
     27   URLRequest::ProtocolFactory* factory;
     28 };
     29 
     30 }  // namespace
     31 
     32 static const SchemeToFactory kBuiltinFactories[] = {
     33   { "http", URLRequestHttpJob::Factory },
     34   { "https", URLRequestHttpJob::Factory },
     35 
     36 #if !defined(OS_IOS)
     37   { "ws", URLRequestHttpJob::Factory },
     38   { "wss", URLRequestHttpJob::Factory },
     39 #endif  // !defined(OS_IOS)
     40 };
     41 
     42 // static
     43 URLRequestJobManager* URLRequestJobManager::GetInstance() {
     44   return Singleton<URLRequestJobManager>::get();
     45 }
     46 
     47 URLRequestJob* URLRequestJobManager::CreateJob(
     48     URLRequest* request, NetworkDelegate* network_delegate) const {
     49   DCHECK(IsAllowedThread());
     50 
     51   // If we are given an invalid URL, then don't even try to inspect the scheme.
     52   if (!request->url().is_valid())
     53     return new URLRequestErrorJob(request, network_delegate, ERR_INVALID_URL);
     54 
     55   // We do this here to avoid asking interceptors about unsupported schemes.
     56   const URLRequestJobFactory* job_factory = NULL;
     57   job_factory = request->context()->job_factory();
     58 
     59   const std::string& scheme = request->url().scheme();  // already lowercase
     60   if (!job_factory->IsHandledProtocol(scheme)) {
     61     return new URLRequestErrorJob(
     62         request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
     63   }
     64 
     65   // THREAD-SAFETY NOTICE:
     66   //   We do not need to acquire the lock here since we are only reading our
     67   //   data structures.  They should only be modified on the current thread.
     68 
     69   // See if the request should be intercepted.
     70   //
     71 
     72   // TODO(pauljensen): Remove this when AppCacheInterceptor is a
     73   // ProtocolHandler, see crbug.com/161547.
     74   if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
     75     InterceptorList::const_iterator i;
     76     for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
     77       URLRequestJob* job = (*i)->MaybeIntercept(request, network_delegate);
     78       if (job)
     79         return job;
     80     }
     81   }
     82 
     83   URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler(
     84       scheme, request, network_delegate);
     85   if (job)
     86     return job;
     87 
     88   // See if the request should be handled by a built-in protocol factory.
     89   for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
     90     if (scheme == kBuiltinFactories[i].scheme) {
     91       URLRequestJob* job = (kBuiltinFactories[i].factory)(
     92           request, network_delegate, scheme);
     93       DCHECK(job);  // The built-in factories are not expected to fail!
     94       return job;
     95     }
     96   }
     97 
     98   // If we reached here, then it means that a registered protocol factory
     99   // wasn't interested in handling the URL.  That is fairly unexpected, and we
    100   // don't have a specific error to report here :-(
    101   LOG(WARNING) << "Failed to map: " << request->url().spec();
    102   return new URLRequestErrorJob(request, network_delegate, ERR_FAILED);
    103 }
    104 
    105 URLRequestJob* URLRequestJobManager::MaybeInterceptRedirect(
    106     URLRequest* request,
    107     NetworkDelegate* network_delegate,
    108     const GURL& location) const {
    109   DCHECK(IsAllowedThread());
    110   if (!request->url().is_valid() ||
    111       request->load_flags() & LOAD_DISABLE_INTERCEPT ||
    112       request->status().status() == URLRequestStatus::CANCELED) {
    113     return NULL;
    114   }
    115 
    116   const URLRequestJobFactory* job_factory = NULL;
    117   job_factory = request->context()->job_factory();
    118 
    119   const std::string& scheme = request->url().scheme();  // already lowercase
    120   if (!job_factory->IsHandledProtocol(scheme))
    121     return NULL;
    122 
    123   InterceptorList::const_iterator i;
    124   for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
    125     URLRequestJob* job = (*i)->MaybeInterceptRedirect(request,
    126                                                       network_delegate,
    127                                                       location);
    128     if (job)
    129       return job;
    130   }
    131   return NULL;
    132 }
    133 
    134 URLRequestJob* URLRequestJobManager::MaybeInterceptResponse(
    135     URLRequest* request, NetworkDelegate* network_delegate) const {
    136   DCHECK(IsAllowedThread());
    137   if (!request->url().is_valid() ||
    138       request->load_flags() & LOAD_DISABLE_INTERCEPT ||
    139       request->status().status() == URLRequestStatus::CANCELED) {
    140     return NULL;
    141   }
    142 
    143   const URLRequestJobFactory* job_factory = NULL;
    144   job_factory = request->context()->job_factory();
    145 
    146   const std::string& scheme = request->url().scheme();  // already lowercase
    147   if (!job_factory->IsHandledProtocol(scheme))
    148     return NULL;
    149 
    150   InterceptorList::const_iterator i;
    151   for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
    152     URLRequestJob* job = (*i)->MaybeInterceptResponse(request,
    153                                                       network_delegate);
    154     if (job)
    155       return job;
    156   }
    157   return NULL;
    158 }
    159 
    160 // static
    161 bool URLRequestJobManager::SupportsScheme(const std::string& scheme) {
    162   for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
    163     if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme))
    164       return true;
    165   }
    166 
    167   return false;
    168 }
    169 
    170 void URLRequestJobManager::RegisterRequestInterceptor(
    171     URLRequest::Interceptor* interceptor) {
    172   DCHECK(IsAllowedThread());
    173 
    174   base::AutoLock locked(lock_);
    175 
    176   DCHECK(std::find(interceptors_.begin(), interceptors_.end(), interceptor) ==
    177          interceptors_.end());
    178   interceptors_.push_back(interceptor);
    179 }
    180 
    181 void URLRequestJobManager::UnregisterRequestInterceptor(
    182     URLRequest::Interceptor* interceptor) {
    183   DCHECK(IsAllowedThread());
    184 
    185   base::AutoLock locked(lock_);
    186 
    187   InterceptorList::iterator i =
    188       std::find(interceptors_.begin(), interceptors_.end(), interceptor);
    189   DCHECK(i != interceptors_.end());
    190   interceptors_.erase(i);
    191 }
    192 
    193 URLRequestJobManager::URLRequestJobManager()
    194     : allowed_thread_(0),
    195       allowed_thread_initialized_(false) {
    196 }
    197 
    198 URLRequestJobManager::~URLRequestJobManager() {}
    199 
    200 }  // namespace net
    201