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