Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(639)

Side by Side Diff: content/browser/devtools/devtools_url_request_interceptor.cc

Issue 2739323003: DevTools protocol interception, blocking & modification of requests (Closed)
Patch Set: Fix Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 "content/browser/devtools/devtools_url_request_interceptor.h"
6
7 #include "base/memory/ptr_util.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/supports_user_data.h"
10 #include "content/browser/devtools/devtools_agent_host_impl.h"
11 #include "content/browser/devtools/devtools_url_interceptor_request_job.h"
12 #include "content/browser/devtools/protocol/network_handler.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/devtools_agent_host.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/resource_request_info.h"
18 #include "content/public/browser/web_contents.h"
19 #include "content/public/browser/web_contents_observer.h"
20 #include "net/http/http_request_headers.h"
21 #include "net/url_request/url_request.h"
22
23 namespace content {
24
25 namespace {
26 const char kDevToolsURLRequestInterceptorKeyName[] =
27 "DevToolsURLRequestInterceptor";
28
29 class DevToolsURLRequestInterceptorUserData
30 : public base::SupportsUserData::Data {
31 public:
32 explicit DevToolsURLRequestInterceptorUserData(
33 DevToolsURLRequestInterceptor* devtools_url_request_interceptor)
34 : devtools_url_request_interceptor_(devtools_url_request_interceptor) {}
35
36 DevToolsURLRequestInterceptor* devtools_url_request_interceptor() const {
37 return devtools_url_request_interceptor_;
38 }
39
40 private:
41 DevToolsURLRequestInterceptor* devtools_url_request_interceptor_;
42
43 DISALLOW_COPY_AND_ASSIGN(DevToolsURLRequestInterceptorUserData);
44 };
45
46 } // namespace
47
48 DevToolsURLRequestInterceptor::DevToolsURLRequestInterceptor(
49 BrowserContext* browser_context)
50 : browser_context_(browser_context), state_(new State()) {
51 DCHECK_CURRENTLY_ON(BrowserThread::UI);
52 browser_context_->SetUserData(
53 kDevToolsURLRequestInterceptorKeyName,
54 base::MakeUnique<DevToolsURLRequestInterceptorUserData>(this));
55 }
56
57 DevToolsURLRequestInterceptor::~DevToolsURLRequestInterceptor() {
58 // The BrowserContext owns us, so we don't need to unregister
59 // DevToolsURLRequestInterceptorUserData explicitly.
60 }
61
62 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRequest(
63 net::URLRequest* request,
64 net::NetworkDelegate* network_delegate) const {
65 DCHECK_CURRENTLY_ON(BrowserThread::IO);
66
67 base::WeakPtr<protocol::NetworkHandler> network_handler;
68 if (!state().ShouldInterceptRequest(request, &network_handler))
69 return nullptr;
70
71 bool is_redirect;
72 std::string intercept_id = state().GetIdForRequest(request, &is_redirect);
73 return new DevToolsURLInterceptorRequestJob(
74 state().GetWeakPtr(), intercept_id, request, network_delegate,
75 network_handler, is_redirect);
76 }
77
78 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect(
79 net::URLRequest* request,
80 net::NetworkDelegate* network_delegate,
81 const GURL& location) const {
82 return nullptr;
83 }
84
85 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse(
86 net::URLRequest* request,
87 net::NetworkDelegate* network_delegate) const {
88 return nullptr;
89 }
90
91 DevToolsURLRequestInterceptor::State::State()
92 : next_id_(0), weak_ptr_factory_(this) {}
93
94 DevToolsURLRequestInterceptor::State::~State() {}
95
96 void DevToolsURLRequestInterceptor::State::AllowRequest(
97 std::string interception_id,
98 CommandCallback callback) {
99 DCHECK_CURRENTLY_ON(BrowserThread::IO);
100 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
101 if (!job) {
102 std::move(callback).Run(CommandStatus::UnknownInterceptionId);
103 } else {
104 std::move(callback).Run(job->AllowRequest());
105 }
106 }
107
108 void DevToolsURLRequestInterceptor::State::BlockRequest(
109 std::string interception_id,
110 net::Error error_reason,
111 CommandCallback callback) {
112 DCHECK_CURRENTLY_ON(BrowserThread::IO);
113 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
114 if (!job) {
115 std::move(callback).Run(CommandStatus::UnknownInterceptionId);
116 } else {
117 std::move(callback).Run(job->BlockRequest(error_reason));
118 }
119 }
120
121 void DevToolsURLRequestInterceptor::State::ModifyRequest(
122 std::string interception_id,
123 std::unique_ptr<Modifications> modifications,
124 CommandCallback callback) {
125 DCHECK_CURRENTLY_ON(BrowserThread::IO);
126 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
127 if (!job) {
128 std::move(callback).Run(CommandStatus::UnknownInterceptionId);
129 } else {
130 std::move(callback).Run(job->ModifyRequest(std::move(modifications)));
131 }
132 }
133
134 void DevToolsURLRequestInterceptor::State::MockResponse(
135 std::string interception_id,
136 std::string raw_response,
137 CommandCallback callback) {
138 DCHECK_CURRENTLY_ON(BrowserThread::IO);
139 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
140 if (!job) {
141 std::move(callback).Run(CommandStatus::UnknownInterceptionId);
142 } else {
143 std::move(callback).Run(job->MockResponse(std::move(raw_response)));
144 }
145 }
146
147 bool DevToolsURLRequestInterceptor::State::ShouldInterceptRequest(
148 const net::URLRequest* request,
149 base::WeakPtr<protocol::NetworkHandler>* network_handler) const {
150 const ResourceRequestInfo* resource_request_info =
151 ResourceRequestInfo::ForRequest(request);
152 if (!resource_request_info)
153 return false;
154 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId();
155 if (frame_tree_node_id == -1) {
156 // |frame_tree_node_id| is not set for renderer side requests, fall back to
157 // the RenderFrameID.
158 int render_frame_id = resource_request_info->GetRenderFrameID();
dgozman 2017/05/22 22:27:04 Note that render frame ids clash between different
alex clarke (OOO till 29th) 2017/05/23 15:46:56 There was a bug here, it needed the process id too
159 const auto find_it = intercepted_render_frames_.find(render_frame_id);
160 if (find_it == intercepted_render_frames_.end())
161 return false;
162 *network_handler = find_it->second;
163 } else {
164 // |frame_tree_node_id| is set for browser side navigations, so use that
165 // because the RenderFrameID isn't known.
166 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id);
167 if (find_it == intercepted_frame_tree_nodes_.end())
168 return false;
169 *network_handler = find_it->second;
170 }
171
172 // We don't want to intercept our own sub requests.
173 return sub_requests_.find(request) == sub_requests_.end();
174 }
175
176 class DevToolsURLRequestInterceptor::State::InterceptedWebContentsObserver
177 : public WebContentsObserver {
178 public:
179 InterceptedWebContentsObserver(
180 WebContents* web_contents,
181 base::WeakPtr<DevToolsURLRequestInterceptor::State> state,
182 base::WeakPtr<protocol::NetworkHandler> network_handler)
183 : WebContentsObserver(web_contents),
184 state_(state),
185 network_handler_(network_handler) {}
186
187 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
188 DCHECK_CURRENTLY_ON(BrowserThread::UI);
189 BrowserThread::PostTask(
190 BrowserThread::IO, FROM_HERE,
191 base::BindOnce(&DevToolsURLRequestInterceptor::State::
192 StartInterceptingRequestsInternal,
193 state_, render_frame_host->GetRoutingID(),
194 render_frame_host->GetFrameTreeNodeId(),
195 network_handler_));
196 }
197
198 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
199 DCHECK_CURRENTLY_ON(BrowserThread::UI);
200 BrowserThread::PostTask(
201 BrowserThread::IO, FROM_HERE,
202 base::BindOnce(&DevToolsURLRequestInterceptor::State::
203 StopInterceptingRequestsInternal,
204 state_, render_frame_host->GetRoutingID(),
205 render_frame_host->GetFrameTreeNodeId()));
206 }
207
208 private:
209 base::WeakPtr<DevToolsURLRequestInterceptor::State> state_;
210 base::WeakPtr<protocol::NetworkHandler> network_handler_;
211 };
212
213 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsInternal(
214 int render_frame_id,
215 int frame_tree_node_id,
216 base::WeakPtr<protocol::NetworkHandler> network_handler) {
217 DCHECK_CURRENTLY_ON(BrowserThread::IO);
218 intercepted_render_frames_[render_frame_id] = network_handler;
219 intercepted_frame_tree_nodes_[frame_tree_node_id] = network_handler;
dgozman 2017/05/22 22:27:04 It's unfortunate that you have to observe and keep
alex clarke (OOO till 29th) 2017/05/23 15:46:56 Agreed, I'd rather not do that but we need to do t
220 }
221
222 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsInternal(
223 int render_frame_id,
224 int frame_tree_node_id) {
225 DCHECK_CURRENTLY_ON(BrowserThread::IO);
226 intercepted_render_frames_.erase(render_frame_id);
227 intercepted_frame_tree_nodes_.erase(frame_tree_node_id);
228 }
229
230 void DevToolsURLRequestInterceptor::State::StartInterceptingRequests(
231 WebContents* web_contents,
232 base::WeakPtr<protocol::NetworkHandler> network_handler) {
233 DCHECK_CURRENTLY_ON(BrowserThread::IO);
234 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) {
235 intercepted_render_frames_[render_frame_host->GetRoutingID()] =
236 network_handler;
237 intercepted_frame_tree_nodes_[render_frame_host->GetFrameTreeNodeId()] =
238 network_handler;
239 }
240
241 // Listen for future updates.
242 observers_.emplace(
243 web_contents,
244 base::MakeUnique<InterceptedWebContentsObserver>(
245 web_contents, weak_ptr_factory_.GetWeakPtr(), network_handler));
246 }
247
248 void DevToolsURLRequestInterceptor::State::StopInterceptingRequests(
249 WebContents* web_contents) {
250 DCHECK_CURRENTLY_ON(BrowserThread::IO);
251 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) {
252 intercepted_render_frames_.erase(render_frame_host->GetRoutingID());
253 intercepted_frame_tree_nodes_.erase(
254 render_frame_host->GetFrameTreeNodeId());
255 }
256 observers_.erase(web_contents);
257 }
258
259 void DevToolsURLRequestInterceptor::State::RegisterSubRequest(
260 const net::URLRequest* sub_request) {
261 DCHECK_CURRENTLY_ON(BrowserThread::IO);
262 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end());
263 sub_requests_.insert(sub_request);
264 }
265
266 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest(
267 const net::URLRequest* sub_request) {
268 DCHECK_CURRENTLY_ON(BrowserThread::IO);
269 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end());
270 sub_requests_.erase(sub_request);
271 }
272
273 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect(
274 const net::URLRequest* request,
275 std::string id) {
276 DCHECK_CURRENTLY_ON(BrowserThread::IO);
277 expected_redirects_[request] = id;
278 }
279
280 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest(
281 const net::URLRequest* request,
282 bool* is_redirect) {
283 DCHECK_CURRENTLY_ON(BrowserThread::IO);
284 auto find_it = expected_redirects_.find(request);
285 if (find_it == expected_redirects_.end()) {
286 *is_redirect = false;
287 return base::StringPrintf("id-%zu", ++next_id_);
288 }
289 *is_redirect = true;
290 std::string id = find_it->second;
291 expected_redirects_.erase(find_it);
292 return id;
293 }
294
295 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob(
296 const std::string& interception_id) const {
297 DCHECK_CURRENTLY_ON(BrowserThread::IO);
298 const auto it = interception_id_to_job_map_.find(interception_id);
299 if (it == interception_id_to_job_map_.end())
300 return nullptr;
301 return it->second;
302 }
303
304 void DevToolsURLRequestInterceptor::State::RegisterJob(
305 DevToolsURLInterceptorRequestJob* job,
306 const std::string& interception_id) {
307 DCHECK_CURRENTLY_ON(BrowserThread::IO);
308 interception_id_to_job_map_[interception_id] = job;
309 }
310
311 void DevToolsURLRequestInterceptor::State::UnregisterJob(
312 const std::string& interception_id) {
313 DCHECK_CURRENTLY_ON(BrowserThread::IO);
314 interception_id_to_job_map_.erase(interception_id);
315 }
316
317 // static
318 DevToolsURLRequestInterceptor*
319 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) {
320 DCHECK_CURRENTLY_ON(BrowserThread::UI);
321 return static_cast<DevToolsURLRequestInterceptorUserData*>(
322 context->GetUserData(kDevToolsURLRequestInterceptorKeyName))
323 ->devtools_url_request_interceptor();
324 }
325
326 DevToolsURLRequestInterceptor::Modifications::Modifications(
327 protocol::Maybe<std::string> url,
328 protocol::Maybe<std::string> method,
329 protocol::Maybe<std::string> post_data,
330 protocol::Maybe<protocol::Network::Headers> headers)
331 : url(std::move(url)),
332 method(std::move(method)),
333 post_data(std::move(post_data)),
334 headers(std::move(headers)) {}
335
336 DevToolsURLRequestInterceptor::Modifications::~Modifications() {}
337
338 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698