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 21namespace net { 22 23// The built-in set of protocol factories 24namespace { 25 26struct SchemeToFactory { 27 const char* scheme; 28 URLRequest::ProtocolFactory* factory; 29}; 30 31} // namespace 32 33static 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 45URLRequestJobManager* URLRequestJobManager::GetInstance() { 46 return Singleton<URLRequestJobManager>::get(); 47} 48 49URLRequestJob* 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 104URLRequestJob* 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 125URLRequestJob* 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 145bool 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 160URLRequest::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 184void 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 197void 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 211URLRequestJobManager::URLRequestJobManager() : enable_file_access_(false) { 212#ifndef NDEBUG 213 allowed_thread_ = 0; 214 allowed_thread_initialized_ = false; 215#endif 216} 217 218URLRequestJobManager::~URLRequestJobManager() {} 219 220} // namespace net 221