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