1 // Copyright 2011 The Chromium Authors
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 "base/logging.h"
8 #include "base/task/current_thread.h"
9 #include "net/url_request/url_request.h"
10 #include "net/url_request/url_request_job.h"
11 #include "net/url_request/url_request_job_factory.h"
12
13 namespace net {
14
15 namespace {
16
17 // When adding interceptors, DCHECK that this function returns true.
OnMessageLoopForInterceptorAddition()18 bool OnMessageLoopForInterceptorAddition() {
19 // Return true if called on a MessageLoopForIO or if there is no MessageLoop.
20 // Checking for a MessageLoopForIO is a best effort at determining whether the
21 // current thread is a networking thread. Allowing cases without a
22 // MessageLoop is required for some tests where there is no chance to insert
23 // an interceptor between a networking thread being started and a resource
24 // request being issued.
25 return base::CurrentIOThread::IsSet() || !base::CurrentThread::IsSet();
26 }
27
28 // When removing interceptors, DCHECK that this function returns true.
OnMessageLoopForInterceptorRemoval()29 bool OnMessageLoopForInterceptorRemoval() {
30 // Checking for a CurrentIOThread is a best effort at determining
31 // whether the current thread is a networking thread.
32 return base::CurrentIOThread::IsSet();
33 }
34
35 } // namespace
36
37 URLRequestFilter* URLRequestFilter::shared_instance_ = nullptr;
38
39 // static
GetInstance()40 URLRequestFilter* URLRequestFilter::GetInstance() {
41 DCHECK(OnMessageLoopForInterceptorAddition());
42 if (!shared_instance_)
43 shared_instance_ = new URLRequestFilter;
44 return shared_instance_;
45 }
46
AddHostnameInterceptor(const std::string & scheme,const std::string & hostname,std::unique_ptr<URLRequestInterceptor> interceptor)47 void URLRequestFilter::AddHostnameInterceptor(
48 const std::string& scheme,
49 const std::string& hostname,
50 std::unique_ptr<URLRequestInterceptor> interceptor) {
51 DCHECK(OnMessageLoopForInterceptorAddition());
52 DCHECK_EQ(0u, hostname_interceptor_map_.count(std::pair(scheme, hostname)));
53 hostname_interceptor_map_[std::pair(scheme, hostname)] =
54 std::move(interceptor);
55
56 #ifndef NDEBUG
57 // Check to see if we're masking URLs in the url_interceptor_map_.
58 for (const auto& pair : url_interceptor_map_) {
59 const GURL& url = GURL(pair.first);
60 HostnameInterceptorMap::const_iterator host_it =
61 hostname_interceptor_map_.find(std::pair(url.scheme(), url.host()));
62 if (host_it != hostname_interceptor_map_.end())
63 NOTREACHED();
64 }
65 #endif // !NDEBUG
66 }
67
RemoveHostnameHandler(const std::string & scheme,const std::string & hostname)68 void URLRequestFilter::RemoveHostnameHandler(const std::string& scheme,
69 const std::string& hostname) {
70 DCHECK(OnMessageLoopForInterceptorRemoval());
71 int removed = hostname_interceptor_map_.erase(std::pair(scheme, hostname));
72 DCHECK(removed);
73 }
74
AddUrlInterceptor(const GURL & url,std::unique_ptr<URLRequestInterceptor> interceptor)75 bool URLRequestFilter::AddUrlInterceptor(
76 const GURL& url,
77 std::unique_ptr<URLRequestInterceptor> interceptor) {
78 DCHECK(OnMessageLoopForInterceptorAddition());
79 if (!url.is_valid())
80 return false;
81 DCHECK_EQ(0u, url_interceptor_map_.count(url.spec()));
82 url_interceptor_map_[url.spec()] = std::move(interceptor);
83
84 // Check to see if this URL is masked by a hostname handler.
85 DCHECK_EQ(
86 0u, hostname_interceptor_map_.count(std::pair(url.scheme(), url.host())));
87
88 return true;
89 }
90
RemoveUrlHandler(const GURL & url)91 void URLRequestFilter::RemoveUrlHandler(const GURL& url) {
92 DCHECK(OnMessageLoopForInterceptorRemoval());
93 size_t removed = url_interceptor_map_.erase(url.spec());
94 DCHECK(removed);
95 }
96
ClearHandlers()97 void URLRequestFilter::ClearHandlers() {
98 DCHECK(OnMessageLoopForInterceptorRemoval());
99 url_interceptor_map_.clear();
100 hostname_interceptor_map_.clear();
101 hit_count_ = 0;
102 }
103
MaybeInterceptRequest(URLRequest * request) const104 std::unique_ptr<URLRequestJob> URLRequestFilter::MaybeInterceptRequest(
105 URLRequest* request) const {
106 DCHECK(base::CurrentIOThread::Get());
107 if (!request->url().is_valid())
108 return nullptr;
109
110 std::unique_ptr<URLRequestJob> job;
111
112 // Check the hostname map first.
113 const std::string hostname = request->url().host();
114 const std::string scheme = request->url().scheme();
115
116 {
117 auto it = hostname_interceptor_map_.find(std::pair(scheme, hostname));
118 if (it != hostname_interceptor_map_.end())
119 job = it->second->MaybeInterceptRequest(request);
120 }
121
122 if (!job) {
123 // Not in the hostname map, check the url map.
124 const std::string& url = request->url().spec();
125 auto it = url_interceptor_map_.find(url);
126 if (it != url_interceptor_map_.end())
127 job = it->second->MaybeInterceptRequest(request);
128 }
129 if (job) {
130 DVLOG(1) << "URLRequestFilter hit for " << request->url().spec();
131 hit_count_++;
132 }
133 return job;
134 }
135
URLRequestFilter()136 URLRequestFilter::URLRequestFilter() {
137 DCHECK(OnMessageLoopForInterceptorAddition());
138 URLRequestJobFactory::SetInterceptorForTesting(this);
139 }
140
~URLRequestFilter()141 URLRequestFilter::~URLRequestFilter() {
142 DCHECK(OnMessageLoopForInterceptorRemoval());
143 URLRequestJobFactory::SetInterceptorForTesting(nullptr);
144 }
145
146 } // namespace net
147