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