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/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"
22 // The built-in set of protocol factories
25 struct SchemeToFactory
{
27 URLRequest::ProtocolFactory
* factory
;
32 static const SchemeToFactory kBuiltinFactories
[] = {
33 { "http", URLRequestHttpJob::Factory
},
34 { "https", URLRequestHttpJob::Factory
},
37 { "ws", URLRequestHttpJob::Factory
},
38 { "wss", URLRequestHttpJob::Factory
},
39 #endif // !defined(OS_IOS)
44 URLRequestJobManager
* URLRequestJobManager::GetInstance() {
45 return Singleton
<URLRequestJobManager
>::get();
48 URLRequestJob
* URLRequestJobManager::CreateJob(
49 URLRequest
* request
, NetworkDelegate
* network_delegate
) const {
50 DCHECK(IsAllowedThread());
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
);
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();
60 const std::string
& scheme
= request
->url().scheme(); // already lowercase
62 if (!job_factory
->IsHandledProtocol(scheme
)) {
63 return new URLRequestErrorJob(
64 request
, network_delegate
, ERR_UNKNOWN_URL_SCHEME
);
66 } else if (!SupportsScheme(scheme
)) {
67 return new URLRequestErrorJob(
68 request
, network_delegate
, ERR_UNKNOWN_URL_SCHEME
);
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.
75 // See if the request should be intercepted.
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
);
90 URLRequestJob
* job
= job_factory
->MaybeCreateJobWithProtocolHandler(
91 scheme
, request
, network_delegate
);
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
);
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!
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
);
125 URLRequestJob
* URLRequestJobManager::MaybeInterceptRedirect(
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
) {
136 const URLRequestJobFactory
* job_factory
= NULL
;
137 job_factory
= request
->context()->job_factory();
139 const std::string
& scheme
= request
->url().scheme(); // already lowercase
141 if (!job_factory
->IsHandledProtocol(scheme
)) {
144 } else if (!SupportsScheme(scheme
)) {
148 InterceptorList::const_iterator i
;
149 for (i
= interceptors_
.begin(); i
!= interceptors_
.end(); ++i
) {
150 URLRequestJob
* job
= (*i
)->MaybeInterceptRedirect(request
,
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
) {
168 const URLRequestJobFactory
* job_factory
= NULL
;
169 job_factory
= request
->context()->job_factory();
171 const std::string
& scheme
= request
->url().scheme(); // already lowercase
173 if (!job_factory
->IsHandledProtocol(scheme
)) {
176 } else if (!SupportsScheme(scheme
)) {
180 InterceptorList::const_iterator i
;
181 for (i
= interceptors_
.begin(); i
!= interceptors_
.end(); ++i
) {
182 URLRequestJob
* job
= (*i
)->MaybeInterceptResponse(request
,
190 bool URLRequestJobManager::SupportsScheme(const std::string
& scheme
) const {
191 // The set of registered factories may change on another thread.
193 base::AutoLock
locked(lock_
);
194 if (factories_
.find(scheme
) != factories_
.end())
198 for (size_t i
= 0; i
< arraysize(kBuiltinFactories
); ++i
)
199 if (LowerCaseEqualsASCII(scheme
, kBuiltinFactories
[i
].scheme
))
205 URLRequest::ProtocolFactory
* URLRequestJobManager::RegisterProtocolFactory(
206 const std::string
& scheme
,
207 URLRequest::ProtocolFactory
* factory
) {
208 DCHECK(IsAllowedThread());
210 base::AutoLock
locked(lock_
);
212 URLRequest::ProtocolFactory
* old_factory
;
213 FactoryMap::iterator i
= factories_
.find(scheme
);
214 if (i
!= factories_
.end()) {
215 old_factory
= i
->second
;
220 factories_
[scheme
] = factory
;
221 } else if (i
!= factories_
.end()) { // uninstall any old one
227 void URLRequestJobManager::RegisterRequestInterceptor(
228 URLRequest::Interceptor
* interceptor
) {
229 DCHECK(IsAllowedThread());
231 base::AutoLock
locked(lock_
);
233 DCHECK(std::find(interceptors_
.begin(), interceptors_
.end(), interceptor
) ==
234 interceptors_
.end());
235 interceptors_
.push_back(interceptor
);
238 void URLRequestJobManager::UnregisterRequestInterceptor(
239 URLRequest::Interceptor
* interceptor
) {
240 DCHECK(IsAllowedThread());
242 base::AutoLock
locked(lock_
);
244 InterceptorList::iterator i
=
245 std::find(interceptors_
.begin(), interceptors_
.end(), interceptor
);
246 DCHECK(i
!= interceptors_
.end());
247 interceptors_
.erase(i
);
250 URLRequestJobManager::URLRequestJobManager()
251 : allowed_thread_(0),
252 allowed_thread_initialized_(false) {
255 URLRequestJobManager::~URLRequestJobManager() {}