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.
5 #include "net/url_request/url_request_job_manager.h"
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/base/network_delegate.h"
15 #include "net/url_request/url_request_about_job.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/url_request/url_request_data_job.h"
18 #include "net/url_request/url_request_error_job.h"
19 #include "net/url_request/url_request_file_job.h"
20 #include "net/url_request/url_request_ftp_job.h"
21 #include "net/url_request/url_request_http_job.h"
22 #include "net/url_request/url_request_job_factory.h"
26 // The built-in set of protocol factories
29 struct SchemeToFactory
{
31 URLRequest::ProtocolFactory
* factory
;
36 static const SchemeToFactory kBuiltinFactories
[] = {
37 { "http", URLRequestHttpJob::Factory
},
38 { "https", URLRequestHttpJob::Factory
},
39 { "file", URLRequestFileJob::Factory
},
40 #if !defined(DISABLE_FTP_SUPPORT)
41 { "ftp", URLRequestFtpJob::Factory
},
43 { "about", URLRequestAboutJob::Factory
},
44 { "data", URLRequestDataJob::Factory
},
48 URLRequestJobManager
* URLRequestJobManager::GetInstance() {
49 return Singleton
<URLRequestJobManager
>::get();
52 URLRequestJob
* URLRequestJobManager::CreateJob(
53 URLRequest
* request
, NetworkDelegate
* network_delegate
) const {
54 DCHECK(IsAllowedThread());
56 // If we are given an invalid URL, then don't even try to inspect the scheme.
57 if (!request
->url().is_valid())
58 return new URLRequestErrorJob(request
, network_delegate
, ERR_INVALID_URL
);
60 // We do this here to avoid asking interceptors about unsupported schemes.
61 const URLRequestJobFactory
* job_factory
= NULL
;
62 job_factory
= request
->context()->job_factory();
64 const std::string
& scheme
= request
->url().scheme(); // already lowercase
66 if (!job_factory
->IsHandledProtocol(scheme
)) {
67 return new URLRequestErrorJob(
68 request
, network_delegate
, ERR_UNKNOWN_URL_SCHEME
);
70 } else if (!SupportsScheme(scheme
)) {
71 return new URLRequestErrorJob(
72 request
, network_delegate
, ERR_UNKNOWN_URL_SCHEME
);
75 // THREAD-SAFETY NOTICE:
76 // We do not need to acquire the lock here since we are only reading our
77 // data structures. They should only be modified on the current thread.
79 // See if the request should be intercepted.
83 URLRequestJob
* job
= job_factory
->MaybeCreateJobWithInterceptor(
84 request
, network_delegate
);
89 // TODO(willchan): Remove this in favor of URLRequestJobFactory::Interceptor.
90 if (!(request
->load_flags() & LOAD_DISABLE_INTERCEPT
)) {
91 InterceptorList::const_iterator i
;
92 for (i
= interceptors_
.begin(); i
!= interceptors_
.end(); ++i
) {
93 URLRequestJob
* job
= (*i
)->MaybeIntercept(request
, network_delegate
);
100 URLRequestJob
* job
= job_factory
->MaybeCreateJobWithProtocolHandler(
101 scheme
, request
, network_delegate
);
106 // TODO(willchan): Remove this in favor of
107 // URLRequestJobFactory::ProtocolHandler.
108 // See if the request should be handled by a registered protocol factory.
109 // If the registered factory returns null, then we want to fall-back to the
110 // built-in protocol factory.
111 FactoryMap::const_iterator i
= factories_
.find(scheme
);
112 if (i
!= factories_
.end()) {
113 URLRequestJob
* job
= i
->second(request
, network_delegate
, scheme
);
118 // See if the request should be handled by a built-in protocol factory.
119 for (size_t i
= 0; i
< arraysize(kBuiltinFactories
); ++i
) {
120 if (scheme
== kBuiltinFactories
[i
].scheme
) {
121 URLRequestJob
* job
= (kBuiltinFactories
[i
].factory
)(
122 request
, network_delegate
, scheme
);
123 DCHECK(job
); // The built-in factories are not expected to fail!
128 // If we reached here, then it means that a registered protocol factory
129 // wasn't interested in handling the URL. That is fairly unexpected, and we
130 // don't have a specific error to report here :-(
131 LOG(WARNING
) << "Failed to map: " << request
->url().spec();
132 return new URLRequestErrorJob(request
, network_delegate
, ERR_FAILED
);
135 URLRequestJob
* URLRequestJobManager::MaybeInterceptRedirect(
137 NetworkDelegate
* network_delegate
,
138 const GURL
& location
) const {
139 DCHECK(IsAllowedThread());
140 if (!request
->url().is_valid() ||
141 request
->load_flags() & LOAD_DISABLE_INTERCEPT
||
142 request
->status().status() == URLRequestStatus::CANCELED
) {
146 const URLRequestJobFactory
* job_factory
= NULL
;
147 job_factory
= request
->context()->job_factory();
149 const std::string
& scheme
= request
->url().scheme(); // already lowercase
151 if (!job_factory
->IsHandledProtocol(scheme
)) {
154 } else if (!SupportsScheme(scheme
)) {
158 URLRequestJob
* job
= NULL
;
160 job
= job_factory
->MaybeInterceptRedirect(
161 location
, request
, network_delegate
);
165 InterceptorList::const_iterator i
;
166 for (i
= interceptors_
.begin(); i
!= interceptors_
.end(); ++i
) {
167 job
= (*i
)->MaybeInterceptRedirect(request
, network_delegate
, location
);
174 URLRequestJob
* URLRequestJobManager::MaybeInterceptResponse(
175 URLRequest
* request
, NetworkDelegate
* network_delegate
) const {
176 DCHECK(IsAllowedThread());
177 if (!request
->url().is_valid() ||
178 request
->load_flags() & LOAD_DISABLE_INTERCEPT
||
179 request
->status().status() == URLRequestStatus::CANCELED
) {
183 const URLRequestJobFactory
* job_factory
= NULL
;
184 job_factory
= request
->context()->job_factory();
186 const std::string
& scheme
= request
->url().scheme(); // already lowercase
188 if (!job_factory
->IsHandledProtocol(scheme
)) {
191 } else if (!SupportsScheme(scheme
)) {
195 URLRequestJob
* job
= NULL
;
197 job
= job_factory
->MaybeInterceptResponse(request
, network_delegate
);
201 InterceptorList::const_iterator i
;
202 for (i
= interceptors_
.begin(); i
!= interceptors_
.end(); ++i
) {
203 job
= (*i
)->MaybeInterceptResponse(request
, network_delegate
);
210 bool URLRequestJobManager::SupportsScheme(const std::string
& scheme
) const {
211 // The set of registered factories may change on another thread.
213 base::AutoLock
locked(lock_
);
214 if (factories_
.find(scheme
) != factories_
.end())
218 for (size_t i
= 0; i
< arraysize(kBuiltinFactories
); ++i
)
219 if (LowerCaseEqualsASCII(scheme
, kBuiltinFactories
[i
].scheme
))
225 URLRequest::ProtocolFactory
* URLRequestJobManager::RegisterProtocolFactory(
226 const std::string
& scheme
,
227 URLRequest::ProtocolFactory
* factory
) {
228 DCHECK(IsAllowedThread());
230 base::AutoLock
locked(lock_
);
232 URLRequest::ProtocolFactory
* old_factory
;
233 FactoryMap::iterator i
= factories_
.find(scheme
);
234 if (i
!= factories_
.end()) {
235 old_factory
= i
->second
;
240 factories_
[scheme
] = factory
;
241 } else if (i
!= factories_
.end()) { // uninstall any old one
247 void URLRequestJobManager::RegisterRequestInterceptor(
248 URLRequest::Interceptor
* interceptor
) {
249 DCHECK(IsAllowedThread());
251 base::AutoLock
locked(lock_
);
253 DCHECK(std::find(interceptors_
.begin(), interceptors_
.end(), interceptor
) ==
254 interceptors_
.end());
255 interceptors_
.push_back(interceptor
);
258 void URLRequestJobManager::UnregisterRequestInterceptor(
259 URLRequest::Interceptor
* interceptor
) {
260 DCHECK(IsAllowedThread());
262 base::AutoLock
locked(lock_
);
264 InterceptorList::iterator i
=
265 std::find(interceptors_
.begin(), interceptors_
.end(), interceptor
);
266 DCHECK(i
!= interceptors_
.end());
267 interceptors_
.erase(i
);
270 URLRequestJobManager::URLRequestJobManager()
271 : allowed_thread_(0),
272 allowed_thread_initialized_(false) {
275 URLRequestJobManager::~URLRequestJobManager() {}