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 
     37 // static
     38 URLRequestJobManager* URLRequestJobManager::GetInstance() {
     39   return Singleton<URLRequestJobManager>::get();
     40 }
     41 
     42 URLRequestJob* URLRequestJobManager::CreateJob(
     43     URLRequest* request, NetworkDelegate* network_delegate) const {
     44   DCHECK(IsAllowedThread());
     45 
     46   // If we are given an invalid URL, then don't even try to inspect the scheme.
     47   if (!request->url().is_valid())
     48     return new URLRequestErrorJob(request, network_delegate, ERR_INVALID_URL);
     49 
     50   // We do this here to avoid asking interceptors about unsupported schemes.
     51   const URLRequestJobFactory* job_factory = NULL;
     52   job_factory = request->context()->job_factory();
     53 
     54   const std::string& scheme = request->url().scheme();  // already lowercase
     55   if (job_factory) {
     56     if (!job_factory->IsHandledProtocol(scheme)) {
     57       return new URLRequestErrorJob(
     58           request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
     59     }
     60   } else if (!SupportsScheme(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   if (job_factory) {
     84     URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler(
     85         scheme, request, network_delegate);
     86     if (job)
     87       return job;
     88   }
     89 
     90   // TODO(willchan): Remove this in favor of
     91   // URLRequestJobFactory::ProtocolHandler.
     92   // See if the request should be handled by a registered protocol factory.
     93   // If the registered factory returns null, then we want to fall-back to the
     94   // built-in protocol factory.
     95   FactoryMap::const_iterator i = factories_.find(scheme);
     96   if (i != factories_.end()) {
     97     URLRequestJob* job = i->second(request, network_delegate, scheme);
     98     if (job)
     99       return job;
    100   }
    101 
    102   // See if the request should be handled by a built-in protocol factory.
    103   for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
    104     if (scheme == kBuiltinFactories[i].scheme) {
    105       URLRequestJob* job = (kBuiltinFactories[i].factory)(
    106           request, network_delegate, scheme);
    107       DCHECK(job);  // The built-in factories are not expected to fail!
    108       return job;
    109     }
    110   }
    111 
    112   // If we reached here, then it means that a registered protocol factory
    113   // wasn't interested in handling the URL.  That is fairly unexpected, and we
    114   // don't have a specific error to report here :-(
    115   LOG(WARNING) << "Failed to map: " << request->url().spec();
    116   return new URLRequestErrorJob(request, network_delegate, ERR_FAILED);
    117 }
    118 
    119 URLRequestJob* URLRequestJobManager::MaybeInterceptRedirect(
    120     URLRequest* request,
    121     NetworkDelegate* network_delegate,
    122     const GURL& location) const {
    123   DCHECK(IsAllowedThread());
    124   if (!request->url().is_valid() ||
    125       request->load_flags() & LOAD_DISABLE_INTERCEPT ||
    126       request->status().status() == URLRequestStatus::CANCELED) {
    127     return NULL;
    128   }
    129 
    130   const URLRequestJobFactory* job_factory = NULL;
    131   job_factory = request->context()->job_factory();
    132 
    133   const std::string& scheme = request->url().scheme();  // already lowercase
    134   if (job_factory) {
    135     if (!job_factory->IsHandledProtocol(scheme)) {
    136       return NULL;
    137     }
    138   } else if (!SupportsScheme(scheme)) {
    139     return NULL;
    140   }
    141 
    142   InterceptorList::const_iterator i;
    143   for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
    144     URLRequestJob* job = (*i)->MaybeInterceptRedirect(request,
    145                                                       network_delegate,
    146                                                       location);
    147     if (job)
    148       return job;
    149   }
    150   return NULL;
    151 }
    152 
    153 URLRequestJob* URLRequestJobManager::MaybeInterceptResponse(
    154     URLRequest* request, NetworkDelegate* network_delegate) const {
    155   DCHECK(IsAllowedThread());
    156   if (!request->url().is_valid() ||
    157       request->load_flags() & LOAD_DISABLE_INTERCEPT ||
    158       request->status().status() == URLRequestStatus::CANCELED) {
    159     return NULL;
    160   }
    161 
    162   const URLRequestJobFactory* job_factory = NULL;
    163   job_factory = request->context()->job_factory();
    164 
    165   const std::string& scheme = request->url().scheme();  // already lowercase
    166   if (job_factory) {
    167     if (!job_factory->IsHandledProtocol(scheme)) {
    168       return NULL;
    169     }
    170   } else if (!SupportsScheme(scheme)) {
    171     return NULL;
    172   }
    173 
    174   InterceptorList::const_iterator i;
    175   for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
    176     URLRequestJob* job = (*i)->MaybeInterceptResponse(request,
    177                                                       network_delegate);
    178     if (job)
    179       return job;
    180   }
    181   return NULL;
    182 }
    183 
    184 bool URLRequestJobManager::SupportsScheme(const std::string& scheme) const {
    185   // The set of registered factories may change on another thread.
    186   {
    187     base::AutoLock locked(lock_);
    188     if (factories_.find(scheme) != factories_.end())
    189       return true;
    190   }
    191 
    192   for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i)
    193     if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme))
    194       return true;
    195 
    196   return false;
    197 }
    198 
    199 URLRequest::ProtocolFactory* URLRequestJobManager::RegisterProtocolFactory(
    200     const std::string& scheme,
    201     URLRequest::ProtocolFactory* factory) {
    202   DCHECK(IsAllowedThread());
    203 
    204   base::AutoLock locked(lock_);
    205 
    206   URLRequest::ProtocolFactory* old_factory;
    207   FactoryMap::iterator i = factories_.find(scheme);
    208   if (i != factories_.end()) {
    209     old_factory = i->second;
    210   } else {
    211     old_factory = NULL;
    212   }
    213   if (factory) {
    214     factories_[scheme] = factory;
    215   } else if (i != factories_.end()) {  // uninstall any old one
    216     factories_.erase(i);
    217   }
    218   return old_factory;
    219 }
    220 
    221 void URLRequestJobManager::RegisterRequestInterceptor(
    222     URLRequest::Interceptor* interceptor) {
    223   DCHECK(IsAllowedThread());
    224 
    225   base::AutoLock locked(lock_);
    226 
    227   DCHECK(std::find(interceptors_.begin(), interceptors_.end(), interceptor) ==
    228          interceptors_.end());
    229   interceptors_.push_back(interceptor);
    230 }
    231 
    232 void URLRequestJobManager::UnregisterRequestInterceptor(
    233     URLRequest::Interceptor* interceptor) {
    234   DCHECK(IsAllowedThread());
    235 
    236   base::AutoLock locked(lock_);
    237 
    238   InterceptorList::iterator i =
    239       std::find(interceptors_.begin(), interceptors_.end(), interceptor);
    240   DCHECK(i != interceptors_.end());
    241   interceptors_.erase(i);
    242 }
    243 
    244 URLRequestJobManager::URLRequestJobManager()
    245     : allowed_thread_(0),
    246       allowed_thread_initialized_(false) {
    247 }
    248 
    249 URLRequestJobManager::~URLRequestJobManager() {}
    250 
    251 }  // namespace net
    252