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// static
43URLRequestJobManager* URLRequestJobManager::GetInstance() {
44  return Singleton<URLRequestJobManager>::get();
45}
46
47URLRequestJob* URLRequestJobManager::CreateJob(
48    URLRequest* request, NetworkDelegate* network_delegate) const {
49  DCHECK(IsAllowedThread());
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, network_delegate, ERR_INVALID_URL);
54
55  // We do this here to avoid asking interceptors about unsupported schemes.
56  const URLRequestJobFactory* job_factory = NULL;
57  job_factory = request->context()->job_factory();
58
59  const std::string& scheme = request->url().scheme();  // already lowercase
60  if (!job_factory->IsHandledProtocol(scheme)) {
61    return new URLRequestErrorJob(
62        request, network_delegate, ERR_UNKNOWN_URL_SCHEME);
63  }
64
65  // THREAD-SAFETY NOTICE:
66  //   We do not need to acquire the lock here since we are only reading our
67  //   data structures.  They should only be modified on the current thread.
68
69  // See if the request should be intercepted.
70  //
71
72  // TODO(pauljensen): Remove this when AppCacheInterceptor is a
73  // ProtocolHandler, see crbug.com/161547.
74  if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) {
75    InterceptorList::const_iterator i;
76    for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
77      URLRequestJob* job = (*i)->MaybeIntercept(request, network_delegate);
78      if (job)
79        return job;
80    }
81  }
82
83  URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler(
84      scheme, request, network_delegate);
85  if (job)
86    return job;
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)(
92          request, network_delegate, scheme);
93      DCHECK(job);  // The built-in factories are not expected to fail!
94      return job;
95    }
96  }
97
98  // If we reached here, then it means that a registered protocol factory
99  // wasn't interested in handling the URL.  That is fairly unexpected, and we
100  // don't have a specific error to report here :-(
101  LOG(WARNING) << "Failed to map: " << request->url().spec();
102  return new URLRequestErrorJob(request, network_delegate, ERR_FAILED);
103}
104
105URLRequestJob* URLRequestJobManager::MaybeInterceptRedirect(
106    URLRequest* request,
107    NetworkDelegate* network_delegate,
108    const GURL& location) const {
109  DCHECK(IsAllowedThread());
110  if (!request->url().is_valid() ||
111      request->load_flags() & LOAD_DISABLE_INTERCEPT ||
112      request->status().status() == URLRequestStatus::CANCELED) {
113    return NULL;
114  }
115
116  const URLRequestJobFactory* job_factory = NULL;
117  job_factory = request->context()->job_factory();
118
119  const std::string& scheme = request->url().scheme();  // already lowercase
120  if (!job_factory->IsHandledProtocol(scheme))
121    return NULL;
122
123  InterceptorList::const_iterator i;
124  for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
125    URLRequestJob* job = (*i)->MaybeInterceptRedirect(request,
126                                                      network_delegate,
127                                                      location);
128    if (job)
129      return job;
130  }
131  return NULL;
132}
133
134URLRequestJob* URLRequestJobManager::MaybeInterceptResponse(
135    URLRequest* request, NetworkDelegate* network_delegate) const {
136  DCHECK(IsAllowedThread());
137  if (!request->url().is_valid() ||
138      request->load_flags() & LOAD_DISABLE_INTERCEPT ||
139      request->status().status() == URLRequestStatus::CANCELED) {
140    return NULL;
141  }
142
143  const URLRequestJobFactory* job_factory = NULL;
144  job_factory = request->context()->job_factory();
145
146  const std::string& scheme = request->url().scheme();  // already lowercase
147  if (!job_factory->IsHandledProtocol(scheme))
148    return NULL;
149
150  InterceptorList::const_iterator i;
151  for (i = interceptors_.begin(); i != interceptors_.end(); ++i) {
152    URLRequestJob* job = (*i)->MaybeInterceptResponse(request,
153                                                      network_delegate);
154    if (job)
155      return job;
156  }
157  return NULL;
158}
159
160// static
161bool URLRequestJobManager::SupportsScheme(const std::string& scheme) {
162  for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) {
163    if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme))
164      return true;
165  }
166
167  return false;
168}
169
170void URLRequestJobManager::RegisterRequestInterceptor(
171    URLRequest::Interceptor* interceptor) {
172  DCHECK(IsAllowedThread());
173
174  base::AutoLock locked(lock_);
175
176  DCHECK(std::find(interceptors_.begin(), interceptors_.end(), interceptor) ==
177         interceptors_.end());
178  interceptors_.push_back(interceptor);
179}
180
181void URLRequestJobManager::UnregisterRequestInterceptor(
182    URLRequest::Interceptor* interceptor) {
183  DCHECK(IsAllowedThread());
184
185  base::AutoLock locked(lock_);
186
187  InterceptorList::iterator i =
188      std::find(interceptors_.begin(), interceptors_.end(), interceptor);
189  DCHECK(i != interceptors_.end());
190  interceptors_.erase(i);
191}
192
193URLRequestJobManager::URLRequestJobManager()
194    : allowed_thread_(0),
195      allowed_thread_initialized_(false) {
196}
197
198URLRequestJobManager::~URLRequestJobManager() {}
199
200}  // namespace net
201