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_filter.h"
6
7#include <set>
8
9#include "base/logging.h"
10#include "base/stl_util.h"
11#include "net/url_request/url_request_job_factory_impl.h"
12
13namespace net {
14
15namespace {
16
17class URLRequestFilterInterceptor : public URLRequestInterceptor {
18 public:
19  explicit URLRequestFilterInterceptor(URLRequest::ProtocolFactory* factory)
20      : factory_(factory) {}
21  virtual ~URLRequestFilterInterceptor() {}
22
23  // URLRequestInterceptor implementation.
24  virtual URLRequestJob* MaybeInterceptRequest(
25      URLRequest* request, NetworkDelegate* network_delegate) const OVERRIDE {
26    return factory_(request, network_delegate, request->url().scheme());
27  }
28
29 private:
30  URLRequest::ProtocolFactory* factory_;
31
32  DISALLOW_COPY_AND_ASSIGN(URLRequestFilterInterceptor);
33};
34
35}  // namespace
36
37URLRequestFilter* URLRequestFilter::shared_instance_ = NULL;
38
39// static
40URLRequestFilter* URLRequestFilter::GetInstance() {
41  if (!shared_instance_)
42    shared_instance_ = new URLRequestFilter;
43  return shared_instance_;
44}
45
46void URLRequestFilter::AddHostnameHandler(const std::string& scheme,
47    const std::string& hostname, URLRequest::ProtocolFactory* factory) {
48  AddHostnameInterceptor(
49      scheme, hostname,
50      scoped_ptr<URLRequestInterceptor>(
51          new URLRequestFilterInterceptor(factory)));
52}
53
54void URLRequestFilter::AddHostnameInterceptor(
55    const std::string& scheme,
56    const std::string& hostname,
57    scoped_ptr<URLRequestInterceptor> interceptor) {
58  DCHECK_EQ(0u, hostname_interceptor_map_.count(make_pair(scheme, hostname)));
59  hostname_interceptor_map_[make_pair(scheme, hostname)] =
60      interceptor.release();
61
62#ifndef NDEBUG
63  // Check to see if we're masking URLs in the url_interceptor_map_.
64  for (URLInterceptorMap::const_iterator it = url_interceptor_map_.begin();
65       it != url_interceptor_map_.end(); ++it) {
66    const GURL& url = GURL(it->first);
67    HostnameInterceptorMap::const_iterator host_it =
68        hostname_interceptor_map_.find(make_pair(url.scheme(), url.host()));
69    if (host_it != hostname_interceptor_map_.end())
70      NOTREACHED();
71  }
72#endif  // !NDEBUG
73}
74
75void URLRequestFilter::RemoveHostnameHandler(const std::string& scheme,
76                                             const std::string& hostname) {
77  HostnameInterceptorMap::iterator it =
78      hostname_interceptor_map_.find(make_pair(scheme, hostname));
79  DCHECK(it != hostname_interceptor_map_.end());
80
81  delete it->second;
82  hostname_interceptor_map_.erase(it);
83  // Note that we don't unregister from the URLRequest ProtocolFactory as
84  // this would leave no protocol factory for the remaining hostname and URL
85  // handlers.
86}
87
88bool URLRequestFilter::AddUrlHandler(
89    const GURL& url,
90    URLRequest::ProtocolFactory* factory) {
91  return AddUrlInterceptor(
92      url,
93      scoped_ptr<URLRequestInterceptor>(
94          new URLRequestFilterInterceptor(factory)));
95}
96
97bool URLRequestFilter::AddUrlInterceptor(
98    const GURL& url,
99    scoped_ptr<URLRequestInterceptor> interceptor) {
100  if (!url.is_valid())
101    return false;
102  DCHECK_EQ(0u, url_interceptor_map_.count(url.spec()));
103  url_interceptor_map_[url.spec()] = interceptor.release();
104
105  // Check to see if this URL is masked by a hostname handler.
106  DCHECK_EQ(0u, hostname_interceptor_map_.count(make_pair(url.scheme(),
107                                                          url.host())));
108
109  return true;
110}
111
112void URLRequestFilter::RemoveUrlHandler(const GURL& url) {
113  URLInterceptorMap::iterator it = url_interceptor_map_.find(url.spec());
114  DCHECK(it != url_interceptor_map_.end());
115
116  delete it->second;
117  url_interceptor_map_.erase(it);
118  // Note that we don't unregister from the URLRequest ProtocolFactory as
119  // this would leave no protocol factory for the remaining hostname and URL
120  // handlers.
121}
122
123void URLRequestFilter::ClearHandlers() {
124  STLDeleteValues(&url_interceptor_map_);
125  STLDeleteValues(&hostname_interceptor_map_);
126  hit_count_ = 0;
127}
128
129URLRequestJob* URLRequestFilter::MaybeInterceptRequest(
130    URLRequest* request,
131    NetworkDelegate* network_delegate) const {
132  URLRequestJob* job = NULL;
133  if (!request->url().is_valid())
134    return NULL;
135
136  // Check the hostname map first.
137  const std::string hostname = request->url().host();
138  const std::string scheme = request->url().scheme();
139
140  HostnameInterceptorMap::const_iterator it =
141      hostname_interceptor_map_.find(make_pair(scheme, hostname));
142  if (it != hostname_interceptor_map_.end())
143    job = it->second->MaybeInterceptRequest(request, network_delegate);
144
145  if (!job) {
146    // Not in the hostname map, check the url map.
147    const std::string& url = request->url().spec();
148    URLInterceptorMap::const_iterator it = url_interceptor_map_.find(url);
149    if (it != url_interceptor_map_.end())
150      job = it->second->MaybeInterceptRequest(request, network_delegate);
151  }
152  if (job) {
153    DVLOG(1) << "URLRequestFilter hit for " << request->url().spec();
154    hit_count_++;
155  }
156  return job;
157}
158
159URLRequestFilter::URLRequestFilter() : hit_count_(0) {
160  URLRequestJobFactoryImpl::SetInterceptorForTesting(this);
161}
162
163URLRequestFilter::~URLRequestFilter() {
164  URLRequestJobFactoryImpl::SetInterceptorForTesting(NULL);
165}
166
167}  // namespace net
168