summaryrefslogtreecommitdiff
path: root/chromium/net/url_request/url_request_filter.cc
blob: 9a19dde37df817ec1a8242b979f6874d72d51ffb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/url_request/url_request_filter.h"

#include "base/logging.h"
#include "base/task/current_thread.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_job.h"
#include "net/url_request/url_request_job_factory.h"

namespace net {

namespace {

// When adding interceptors, DCHECK that this function returns true.
bool OnMessageLoopForInterceptorAddition() {
  // Return true if called on a MessageLoopForIO or if there is no MessageLoop.
  // Checking for a MessageLoopForIO is a best effort at determining whether the
  // current thread is a networking thread.  Allowing cases without a
  // MessageLoop is required for some tests where there is no chance to insert
  // an interceptor between a networking thread being started and a resource
  // request being issued.
  return base::CurrentIOThread::IsSet() || !base::CurrentThread::IsSet();
}

// When removing interceptors, DCHECK that this function returns true.
bool OnMessageLoopForInterceptorRemoval() {
  // Checking for a CurrentIOThread is a best effort at determining
  // whether the current thread is a networking thread.
  return base::CurrentIOThread::IsSet();
}

}  // namespace

URLRequestFilter* URLRequestFilter::shared_instance_ = nullptr;

// static
URLRequestFilter* URLRequestFilter::GetInstance() {
  DCHECK(OnMessageLoopForInterceptorAddition());
  if (!shared_instance_)
    shared_instance_ = new URLRequestFilter;
  return shared_instance_;
}

void URLRequestFilter::AddHostnameInterceptor(
    const std::string& scheme,
    const std::string& hostname,
    std::unique_ptr<URLRequestInterceptor> interceptor) {
  DCHECK(OnMessageLoopForInterceptorAddition());
  DCHECK_EQ(0u, hostname_interceptor_map_.count(make_pair(scheme, hostname)));
  hostname_interceptor_map_[make_pair(scheme, hostname)] =
      std::move(interceptor);

#ifndef NDEBUG
  // Check to see if we're masking URLs in the url_interceptor_map_.
  for (const auto& pair : url_interceptor_map_) {
    const GURL& url = GURL(pair.first);
    HostnameInterceptorMap::const_iterator host_it =
        hostname_interceptor_map_.find(make_pair(url.scheme(), url.host()));
    if (host_it != hostname_interceptor_map_.end())
      NOTREACHED();
  }
#endif  // !NDEBUG
}

void URLRequestFilter::RemoveHostnameHandler(const std::string& scheme,
                                             const std::string& hostname) {
  DCHECK(OnMessageLoopForInterceptorRemoval());
  int removed = hostname_interceptor_map_.erase(make_pair(scheme, hostname));
  DCHECK(removed);

  // Note that we don't unregister from the URLRequest ProtocolFactory as
  // this would leave no protocol factory for the remaining hostname and URL
  // handlers.
}

bool URLRequestFilter::AddUrlInterceptor(
    const GURL& url,
    std::unique_ptr<URLRequestInterceptor> interceptor) {
  DCHECK(OnMessageLoopForInterceptorAddition());
  if (!url.is_valid())
    return false;
  DCHECK_EQ(0u, url_interceptor_map_.count(url.spec()));
  url_interceptor_map_[url.spec()] = std::move(interceptor);

  // Check to see if this URL is masked by a hostname handler.
  DCHECK_EQ(0u, hostname_interceptor_map_.count(make_pair(url.scheme(),
                                                          url.host())));

  return true;
}

void URLRequestFilter::RemoveUrlHandler(const GURL& url) {
  DCHECK(OnMessageLoopForInterceptorRemoval());
  size_t removed = url_interceptor_map_.erase(url.spec());
  DCHECK(removed);
  // Note that we don't unregister from the URLRequest ProtocolFactory as
  // this would leave no protocol factory for the remaining hostname and URL
  // handlers.
}

void URLRequestFilter::ClearHandlers() {
  DCHECK(OnMessageLoopForInterceptorRemoval());
  url_interceptor_map_.clear();
  hostname_interceptor_map_.clear();
  hit_count_ = 0;
}

std::unique_ptr<URLRequestJob> URLRequestFilter::MaybeInterceptRequest(
    URLRequest* request) const {
  DCHECK(base::CurrentIOThread::Get());
  if (!request->url().is_valid())
    return nullptr;

  std::unique_ptr<URLRequestJob> job;

  // Check the hostname map first.
  const std::string hostname = request->url().host();
  const std::string scheme = request->url().scheme();

  {
    auto it = hostname_interceptor_map_.find(make_pair(scheme, hostname));
    if (it != hostname_interceptor_map_.end())
      job = it->second->MaybeInterceptRequest(request);
  }

  if (!job) {
    // Not in the hostname map, check the url map.
    const std::string& url = request->url().spec();
    auto it = url_interceptor_map_.find(url);
    if (it != url_interceptor_map_.end())
      job = it->second->MaybeInterceptRequest(request);
  }
  if (job) {
    DVLOG(1) << "URLRequestFilter hit for " << request->url().spec();
    hit_count_++;
  }
  return job;
}

URLRequestFilter::URLRequestFilter() {
  DCHECK(OnMessageLoopForInterceptorAddition());
  URLRequestJobFactory::SetInterceptorForTesting(this);
}

URLRequestFilter::~URLRequestFilter() {
  DCHECK(OnMessageLoopForInterceptorRemoval());
  URLRequestJobFactory::SetInterceptorForTesting(nullptr);
}

}  // namespace net