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