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

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

Issue 2739323003: DevTools protocol interception, blocking & modification of requests (Closed)
Patch Set: Dont assume there's only one element reader Created 3 years, 6 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/render_process_host.h"
18 #include "content/public/browser/resource_request_info.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_observer.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/url_request/url_request.h"
23
24 namespace content {
25
26 namespace {
27 const char kDevToolsURLRequestInterceptorKeyName[] =
28 "DevToolsURLRequestInterceptor";
29
30 class DevToolsURLRequestInterceptorUserData
31 : public base::SupportsUserData::Data {
32 public:
33 explicit DevToolsURLRequestInterceptorUserData(
34 DevToolsURLRequestInterceptor* devtools_url_request_interceptor)
35 : devtools_url_request_interceptor_(devtools_url_request_interceptor) {}
36
37 DevToolsURLRequestInterceptor* devtools_url_request_interceptor() const {
38 return devtools_url_request_interceptor_;
39 }
40
41 private:
42 DevToolsURLRequestInterceptor* devtools_url_request_interceptor_;
43
44 DISALLOW_COPY_AND_ASSIGN(DevToolsURLRequestInterceptorUserData);
45 };
46
47 } // namespace
48
49 DevToolsURLRequestInterceptor::DevToolsURLRequestInterceptor(
50 BrowserContext* browser_context)
51 : browser_context_(browser_context), state_(new State()) {
52 DCHECK_CURRENTLY_ON(BrowserThread::UI);
53 browser_context_->SetUserData(
54 kDevToolsURLRequestInterceptorKeyName,
55 base::MakeUnique<DevToolsURLRequestInterceptorUserData>(this));
56 }
57
58 DevToolsURLRequestInterceptor::~DevToolsURLRequestInterceptor() {
59 // The BrowserContext owns us, so we don't need to unregister
60 // DevToolsURLRequestInterceptorUserData explicitly.
61 }
62
63 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRequest(
64 net::URLRequest* request,
65 net::NetworkDelegate* network_delegate) const {
66 DCHECK_CURRENTLY_ON(BrowserThread::IO);
67 return state()->MaybeCreateDevToolsURLInterceptorRequestJob(request,
68 network_delegate);
69 }
70
71 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect(
72 net::URLRequest* request,
73 net::NetworkDelegate* network_delegate,
74 const GURL& location) const {
75 return nullptr;
76 }
77
78 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse(
79 net::URLRequest* request,
80 net::NetworkDelegate* network_delegate) const {
81 return nullptr;
82 }
83
84 DevToolsURLRequestInterceptor::State::State() : next_id_(0) {}
85
86 DevToolsURLRequestInterceptor::State::~State() {}
87
88 void DevToolsURLRequestInterceptor::State::ContinueInterceptedRequest(
89 std::string interception_id,
90 std::unique_ptr<Modifications> modifications,
91 std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
92 DCHECK_CURRENTLY_ON(BrowserThread::UI);
93
94 BrowserThread::PostTask(
95 BrowserThread::IO, FROM_HERE,
96 base::Bind(&DevToolsURLRequestInterceptor::State::
97 ContinueInterceptedRequestOnIoThread,
98 this, interception_id, base::Passed(std::move(modifications)),
99 base::Passed(std::move(callback))));
100 }
101
102 void DevToolsURLRequestInterceptor::State::ContinueInterceptedRequestOnIoThread(
103 std::string interception_id,
104 std::unique_ptr<Modifications> modifications,
105 std::unique_ptr<ContinueInterceptedRequestCallback> callback) {
106 DCHECK_CURRENTLY_ON(BrowserThread::IO);
107 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
108 if (!job) {
109 BrowserThread::PostTask(
110 BrowserThread::UI, FROM_HERE,
111 base::BindOnce(
112 &ContinueInterceptedRequestCallback::sendFailure,
113 base::Owned(callback.release()),
dgozman 2017/05/31 17:01:10 I think base::Passed(&callback) is the idiomatic w
alex clarke (OOO till 29th) 2017/06/01 12:06:40 IIRC you have to do base::Passed(std::move(callbac
114 protocol::Response::InvalidParams("Invalid InterceptionId.")));
115 return;
116 }
117
118 if (job->ContinueInterceptedRequest(std::move(modifications))) {
119 BrowserThread::PostTask(
120 BrowserThread::UI, FROM_HERE,
121 base::BindOnce(&ContinueInterceptedRequestCallback::sendSuccess,
122 base::Owned(callback.release())));
dgozman 2017/05/31 17:01:10 ditto
alex clarke (OOO till 29th) 2017/06/01 12:06:40 Done.
123 } else {
124 BrowserThread::PostTask(
125 BrowserThread::UI, FROM_HERE,
126 base::BindOnce(
127 &ContinueInterceptedRequestCallback::sendFailure,
128 base::Owned(callback.release()),
dgozman 2017/05/31 17:01:10 ditto
alex clarke (OOO till 29th) 2017/06/01 12:06:40 Done.
129 protocol::Response::InvalidParams("Response already processed.")));
130 }
131 }
132
133 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::
134 MaybeCreateDevToolsURLInterceptorRequestJob(
135 net::URLRequest* request,
136 net::NetworkDelegate* network_delegate) {
137 // Bail out if we're not intercepting anything.
138 if (intercepted_render_frames_.empty()) {
139 DCHECK(intercepted_frame_tree_nodes_.empty());
140 return nullptr;
141 }
142 const ResourceRequestInfo* resource_request_info =
143 ResourceRequestInfo::ForRequest(request);
144 if (!resource_request_info)
145 return nullptr;
146 int child_id = resource_request_info->GetChildID();
147 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId();
148 const InterceptedPage* intercepted_page;
149 if (frame_tree_node_id == -1) {
150 // |frame_tree_node_id| is not set for renderer side requests, fall back to
151 // the RenderFrameID.
152 int render_frame_id = resource_request_info->GetRenderFrameID();
153 const auto find_it = intercepted_render_frames_.find(
154 std::make_pair(render_frame_id, child_id));
155 if (find_it == intercepted_render_frames_.end())
156 return nullptr;
157 intercepted_page = &find_it->second;
158 } else {
159 // |frame_tree_node_id| is set for browser side navigations, so use that
160 // because the RenderFrameID isn't known (neither is the ChildID).
161 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id);
162 if (find_it == intercepted_frame_tree_nodes_.end())
163 return nullptr;
164 intercepted_page = &find_it->second;
165 }
166
167 // We don't want to intercept our own sub requests.
168 if (sub_requests_.find(request) != sub_requests_.end())
169 return nullptr;
170
171 bool is_redirect;
172 std::string interception_id = GetIdForRequest(request, &is_redirect);
173 DevToolsURLInterceptorRequestJob* job = new DevToolsURLInterceptorRequestJob(
174 this, interception_id, request, network_delegate,
175 intercepted_page->web_contents, intercepted_page->network_handler,
176 is_redirect);
177 interception_id_to_job_map_[interception_id] = job;
178 return job;
179 }
180
181 class DevToolsURLRequestInterceptor::State::InterceptedWebContentsObserver
182 : public WebContentsObserver {
183 public:
184 InterceptedWebContentsObserver(
185 WebContents* web_contents,
186 scoped_refptr<DevToolsURLRequestInterceptor::State> state,
187 base::WeakPtr<protocol::NetworkHandler> network_handler)
188 : WebContentsObserver(web_contents),
189 state_(state),
190 network_handler_(network_handler) {
191 DCHECK_CURRENTLY_ON(BrowserThread::UI);
192 }
193
194 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
195 DCHECK_CURRENTLY_ON(BrowserThread::UI);
196 BrowserThread::PostTask(
197 BrowserThread::IO, FROM_HERE,
198 base::BindOnce(&DevToolsURLRequestInterceptor::State::
199 StartInterceptingRequestsInternal,
200 state_, render_frame_host->GetRoutingID(),
201 render_frame_host->GetFrameTreeNodeId(),
202 render_frame_host->GetProcess()->GetID(), web_contents(),
203 network_handler_));
204 }
205
206 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
207 DCHECK_CURRENTLY_ON(BrowserThread::UI);
208 BrowserThread::PostTask(
209 BrowserThread::IO, FROM_HERE,
210 base::BindOnce(&DevToolsURLRequestInterceptor::State::
211 StopInterceptingRequestsInternal,
212 state_, render_frame_host->GetRoutingID(),
213 render_frame_host->GetFrameTreeNodeId(),
214 render_frame_host->GetProcess()->GetID()));
215 }
216
217 private:
218 scoped_refptr<DevToolsURLRequestInterceptor::State> state_;
219 base::WeakPtr<protocol::NetworkHandler> network_handler_;
220 };
221
222 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsInternal(
223 int render_frame_id,
224 int frame_tree_node_id,
225 int process_id,
226 WebContents* web_contents,
227 base::WeakPtr<protocol::NetworkHandler> network_handler) {
228 DCHECK_CURRENTLY_ON(BrowserThread::IO);
229 intercepted_render_frames_.emplace(
230 std::piecewise_construct,
231 std::forward_as_tuple(render_frame_id, process_id),
232 std::forward_as_tuple(web_contents, network_handler));
233 intercepted_frame_tree_nodes_.emplace(
234 std::piecewise_construct, std::forward_as_tuple(frame_tree_node_id),
235 std::forward_as_tuple(web_contents, network_handler));
236 }
237
238 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsInternal(
239 int render_frame_id,
240 int frame_tree_node_id,
241 int process_id) {
242 DCHECK_CURRENTLY_ON(BrowserThread::IO);
243 intercepted_render_frames_.erase(std::make_pair(render_frame_id, process_id));
244 intercepted_frame_tree_nodes_.erase(frame_tree_node_id);
245 }
246
247 void DevToolsURLRequestInterceptor::State::StartInterceptingRequests(
248 WebContents* web_contents,
249 base::WeakPtr<protocol::NetworkHandler> network_handler) {
250 DCHECK_CURRENTLY_ON(BrowserThread::UI);
251 // WebContents methods are UI thread only.
252 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) {
253 BrowserThread::PostTask(
254 BrowserThread::IO, FROM_HERE,
255 base::BindOnce(&DevToolsURLRequestInterceptor::State::
256 StartInterceptingRequestsInternal,
257 this, render_frame_host->GetRoutingID(),
258 render_frame_host->GetFrameTreeNodeId(),
259 render_frame_host->GetProcess()->GetID(), web_contents,
260 network_handler));
261 }
262
263 // Listen for future updates.
264 observers_.emplace(web_contents,
265 base::MakeUnique<InterceptedWebContentsObserver>(
266 web_contents, this, network_handler));
267 }
268
269 void DevToolsURLRequestInterceptor::State::StopInterceptingRequests(
270 WebContents* web_contents) {
271 DCHECK_CURRENTLY_ON(BrowserThread::UI);
272 observers_.erase(web_contents);
273 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
274 base::BindOnce(&DevToolsURLRequestInterceptor::State::
275 StopInterceptingRequestsOnIoThread,
276 this, web_contents));
277 }
278
279 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsOnIoThread(
280 WebContents* web_contents) {
281 DCHECK_CURRENTLY_ON(BrowserThread::IO);
282 // Remove any intercepted render frames associated with |web_contents|.
283 base::flat_map<std::pair<int, int>, InterceptedPage>
284 remaining_intercepted_render_frames;
285 for (const auto pair : intercepted_render_frames_) {
286 if (pair.second.web_contents == web_contents)
287 continue;
288 remaining_intercepted_render_frames.insert(pair);
289 }
290 std::swap(remaining_intercepted_render_frames, intercepted_render_frames_);
291
292 // Remove any intercepted frame tree nodes associated with |web_contents|.
293 base::flat_map<int, InterceptedPage> remaining_intercepted_frame_tree_nodes;
294 for (const auto pair : intercepted_frame_tree_nodes_) {
295 if (pair.second.web_contents == web_contents)
296 continue;
297 remaining_intercepted_frame_tree_nodes.insert(pair);
298 }
299 std::swap(remaining_intercepted_frame_tree_nodes,
300 intercepted_frame_tree_nodes_);
301
302 // Tell any jobs associated with |web_contents| to stop intercepting.
303 for (const auto pair : interception_id_to_job_map_) {
304 if (pair.second->web_contents() == web_contents)
305 pair.second->StopIntercepting();
306 }
307 }
308
309 void DevToolsURLRequestInterceptor::State::RegisterSubRequest(
310 const net::URLRequest* sub_request) {
311 DCHECK_CURRENTLY_ON(BrowserThread::IO);
312 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end());
313 sub_requests_.insert(sub_request);
314 }
315
316 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest(
317 const net::URLRequest* sub_request) {
318 DCHECK_CURRENTLY_ON(BrowserThread::IO);
319 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end());
320 sub_requests_.erase(sub_request);
321 }
322
323 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect(
324 const net::URLRequest* request,
325 std::string id) {
326 DCHECK_CURRENTLY_ON(BrowserThread::IO);
327 expected_redirects_[request] = id;
328 }
329
330 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest(
331 const net::URLRequest* request,
332 bool* is_redirect) {
333 DCHECK_CURRENTLY_ON(BrowserThread::IO);
334 auto find_it = expected_redirects_.find(request);
335 if (find_it == expected_redirects_.end()) {
336 *is_redirect = false;
337 return base::StringPrintf("id-%zu", ++next_id_);
338 }
339 *is_redirect = true;
340 std::string id = find_it->second;
341 expected_redirects_.erase(find_it);
342 return id;
343 }
344
345 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob(
346 const std::string& interception_id) const {
347 DCHECK_CURRENTLY_ON(BrowserThread::IO);
348 const auto it = interception_id_to_job_map_.find(interception_id);
349 if (it == interception_id_to_job_map_.end())
350 return nullptr;
351 return it->second;
352 }
353
354 void DevToolsURLRequestInterceptor::State::JobFinished(
355 const std::string& interception_id) {
356 DCHECK_CURRENTLY_ON(BrowserThread::IO);
357 interception_id_to_job_map_.erase(interception_id);
358 }
359
360 // static
361 DevToolsURLRequestInterceptor*
362 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) {
363 DCHECK_CURRENTLY_ON(BrowserThread::UI);
364 return static_cast<DevToolsURLRequestInterceptorUserData*>(
365 context->GetUserData(kDevToolsURLRequestInterceptorKeyName))
366 ->devtools_url_request_interceptor();
367 }
368
369 DevToolsURLRequestInterceptor::Modifications::Modifications(
370 base::Optional<net::Error> error_reason,
371 base::Optional<std::string> raw_response,
372 protocol::Maybe<std::string> modified_url,
373 protocol::Maybe<std::string> modified_method,
374 protocol::Maybe<std::string> modified_post_data,
375 protocol::Maybe<protocol::Network::Headers> modified_headers)
376 : error_reason(std::move(error_reason)),
377 raw_response(std::move(raw_response)),
378 modified_url(std::move(modified_url)),
379 modified_method(std::move(modified_method)),
380 modified_post_data(std::move(modified_post_data)),
381 modified_headers(std::move(modified_headers)) {}
382
383 DevToolsURLRequestInterceptor::Modifications::~Modifications() {}
384
385 DevToolsURLRequestInterceptor::State::InterceptedPage::InterceptedPage(
dgozman 2017/05/31 17:01:10 nit: could use =default for this.
alex clarke (OOO till 29th) 2017/06/01 12:06:40 Done. Too bad it still has to be out of line.
386 const InterceptedPage& other)
387 : web_contents(other.web_contents),
388 network_handler(other.network_handler) {}
389
390 DevToolsURLRequestInterceptor::State::InterceptedPage::InterceptedPage(
391 WebContents* web_contents,
392 base::WeakPtr<protocol::NetworkHandler> network_handler)
393 : web_contents(web_contents), network_handler(network_handler) {}
394
395 DevToolsURLRequestInterceptor::State::InterceptedPage::~InterceptedPage() {}
dgozman 2017/05/31 17:01:10 ditto
alex clarke (OOO till 29th) 2017/06/01 12:06:40 Done.
396
397 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/devtools/devtools_url_request_interceptor.h ('k') | content/browser/devtools/protocol/network_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698