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