url_request_job_manager.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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
20namespace net {
21
22// The built-in set of protocol factories
23namespace {
24
25struct SchemeToFactory {
26  const char* scheme;
27  URLRequest::ProtocolFactory* factory;
28};
29
30}  // namespace
31
32static 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
44URLRequestJobManager* URLRequestJobManager::GetInstance() {
45  return Singleton<URLRequestJobManager>::get();
46}
47
48URLRequestJob* 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
125URLRequestJob* 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
159URLRequestJob* 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
190bool 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
205URLRequest::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
227void 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
238void 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
250URLRequestJobManager::URLRequestJobManager()
251    : allowed_thread_(0),
252      allowed_thread_initialized_(false) {
253}
254
255URLRequestJobManager::~URLRequestJobManager() {}
256
257}  // namespace net
258