Home | History | Annotate | Download | only in url_request
      1 // Copyright (c) 2011 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/string_util.h"
     12 #include "net/base/load_flags.h"
     13 #include "net/base/net_errors.h"
     14 #include "net/url_request/url_request_about_job.h"
     15 #include "net/url_request/url_request_data_job.h"
     16 #include "net/url_request/url_request_error_job.h"
     17 #include "net/url_request/url_request_file_job.h"
     18 #include "net/url_request/url_request_ftp_job.h"
     19 #include "net/url_request/url_request_http_job.h"
     20 
     21 namespace net {
     22 
     23 // The built-in set of protocol factories
     24 namespace {
     25 
     26 struct SchemeToFactory {
     27   const char* scheme;
     28   URLRequest::ProtocolFactory* factory;
     29 };
     30 
     31 }  // namespace
     32 
     33 static const SchemeToFactory kBuiltinFactories[] = {
     34   { "http", URLRequestHttpJob::Factory },
     35   { "https", URLRequestHttpJob::Factory },
     36   { "file", URLRequestFileJob::Factory },
     37 #ifndef ANDROID
     38   { "ftp", URLRequestFtpJob::Factory },
     39   { "about", URLRequestAboutJob::Factory },
     40   { "data", URLRequestDataJob::Factory },
     41 #endif
     42 };
     43 
     44 // static
     45 URLRequestJobManager* URLRequestJobManager::GetInstance() {
     46   return Singleton<URLRequestJobManager>::get();
     47 }
     48 
     49 URLRequestJob* URLRequestJobManager::CreateJob(
     50     URLRequest* request) const {
     51 #ifndef NDEBUG
     52   DCHECK(IsAllowedThread());
     53 #endif
     54 
     55   // If we are given an invalid URL, then don't even try to inspect the scheme.
     56   if (!request->url().is_valid())
     57     return new URLRequestErrorJob(request, ERR_INVALID_URL);
     58 
     59   // We do this here to avoid asking interceptors about unsupported schemes.
     60   const std::string& scheme = request->url().scheme();  // already lowercase
     61   if (!SupportsScheme(scheme))
     62     return new URLRequestErrorJob(request, ERR_UNKNOWN_URL_SCHEME);
     63 
     64   // THREAD-SAFETY NOTICE:
     65   //   We do not need to acquire the lock here since we are only reading our
     66   //   data structures.  They should only be modified on the current thread.
     67 
     68   // See if the request should be intercepted.
     69   if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
     70     InterceptorList::const_iterator i;
     71     for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
     72       URLRequestJob* job = (*i)->MaybeIntercept(request);
     73       if (job)
     74         return job;
     75     }
     76   }
     77 
     78   // See if the request should be handled by a registered protocol factory.
     79   // If the registered factory returns null, then we want to fall-back to the
     80   // built-in protocol factory.
     81   FactoryMap::const_iterator i = factories_.find(scheme);
     82   if (i != factories_.end()) {
     83     URLRequestJob* job = i->second(request, scheme);
     84     if (job)
     85       return job;
     86   }
     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)(request, scheme);
     92       DCHECK(job);  // The built-in factories are not expected to fail!
     93       return job;
     94     }
     95   }
     96 
     97   // If we reached here, then it means that a registered protocol factory
     98   // wasn't interested in handling the URL.  That is fairly unexpected, and we
     99   // don't know have a specific error to report here :-(
    100   LOG(WARNING) << "Failed to map: " << request->url().spec();
    101   return new URLRequestErrorJob(request, ERR_FAILED);
    102 }
    103 
    104 URLRequestJob* URLRequestJobManager::MaybeInterceptRedirect(
    105     URLRequest* request,
    106     const GURL& location) const {
    107 #ifndef NDEBUG
    108   DCHECK(IsAllowedThread());
    109 #endif
    110   if ((request->load_flags() & LOAD_DISABLE_INTERCEPT) ||
    111       (request->status().status() == URLRequestStatus::CANCELED) ||
    112       !request->url().is_valid() ||
    113       !SupportsScheme(request->url().scheme()))
    114     return NULL;
    115 
    116   InterceptorList::const_iterator i;
    117   for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
    118     URLRequestJob* job = (*i)->MaybeInterceptRedirect(request, location);
    119     if (job)
    120       return job;
    121   }
    122   return NULL;
    123 }
    124 
    125 URLRequestJob* URLRequestJobManager::MaybeInterceptResponse(
    126     URLRequest* request) const {
    127 #ifndef NDEBUG
    128   DCHECK(IsAllowedThread());
    129 #endif
    130   if ((request->load_flags() & LOAD_DISABLE_INTERCEPT) ||
    131       (request->status().status() == URLRequestStatus::CANCELED) ||
    132       !request->url().is_valid() ||
    133       !SupportsScheme(request->url().scheme()))
    134     return NULL;
    135 
    136   InterceptorList::const_iterator i;
    137   for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
    138     URLRequestJob* job = (*i)->MaybeInterceptResponse(request);
    139     if (job)
    140       return job;
    141   }
    142   return NULL;
    143 }
    144 
    145 bool URLRequestJobManager::SupportsScheme(const std::string& scheme) const {
    146   // The set of registered factories may change on another thread.
    147   {
    148     base::AutoLock locked(lock_);
    149     if (factories_.find(scheme) != factories_.end())
    150       return true;
    151   }
    152 
    153   for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i)
    154     if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme))
    155       return true;
    156 
    157   return false;
    158 }
    159 
    160 URLRequest::ProtocolFactory* URLRequestJobManager::RegisterProtocolFactory(
    161     const std::string& scheme,
    162     URLRequest::ProtocolFactory* factory) {
    163 #ifndef NDEBUG
    164   DCHECK(IsAllowedThread());
    165 #endif
    166 
    167   base::AutoLock locked(lock_);
    168 
    169   URLRequest::ProtocolFactory* old_factory;
    170   FactoryMap::iterator i = factories_.find(scheme);
    171   if (i != factories_.end()) {
    172     old_factory = i->second;
    173   } else {
    174     old_factory = NULL;
    175   }
    176   if (factory) {
    177     factories_[scheme] = factory;
    178   } else if (i != factories_.end()) {  // uninstall any old one
    179     factories_.erase(i);
    180   }
    181   return old_factory;
    182 }
    183 
    184 void URLRequestJobManager::RegisterRequestInterceptor(
    185     URLRequest::Interceptor* interceptor) {
    186 #ifndef NDEBUG
    187   DCHECK(IsAllowedThread());
    188 #endif
    189 
    190   base::AutoLock locked(lock_);
    191 
    192   DCHECK(std::find(interceptors_.begin(), interceptors_.end(), interceptor) ==
    193          interceptors_.end());
    194   interceptors_.push_back(interceptor);
    195 }
    196 
    197 void URLRequestJobManager::UnregisterRequestInterceptor(
    198     URLRequest::Interceptor* interceptor) {
    199 #ifndef NDEBUG
    200   DCHECK(IsAllowedThread());
    201 #endif
    202 
    203   base::AutoLock locked(lock_);
    204 
    205   InterceptorList::iterator i =
    206       std::find(interceptors_.begin(), interceptors_.end(), interceptor);
    207   DCHECK(i != interceptors_.end());
    208   interceptors_.erase(i);
    209 }
    210 
    211 URLRequestJobManager::URLRequestJobManager() : enable_file_access_(false) {
    212 #ifndef NDEBUG
    213   allowed_thread_ = 0;
    214   allowed_thread_initialized_ = false;
    215 #endif
    216 }
    217 
    218 URLRequestJobManager::~URLRequestJobManager() {}
    219 
    220 }  // namespace net
    221