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

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

Issue 2739323003: DevTools protocol interception, blocking & modification of requests (Closed)
Patch Set: Hopefully sorted out the threading 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/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
68 const InterceptedPage* intercepted_page;
69 if (!state()->ShouldInterceptRequest(request, &intercepted_page))
70 return nullptr;
71
72 return state()->CreateDevToolsURLInterceptorRequestJob(
dgozman 2017/05/30 21:44:27 Perhaps, it makes sense to combine ShouldIntercept
alex clarke (OOO till 29th) 2017/05/31 15:52:02 Done.
73 request, network_delegate, intercepted_page);
74 }
75
76 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect(
77 net::URLRequest* request,
78 net::NetworkDelegate* network_delegate,
79 const GURL& location) const {
80 return nullptr;
81 }
82
83 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse(
84 net::URLRequest* request,
85 net::NetworkDelegate* network_delegate) const {
86 return nullptr;
87 }
88
89 DevToolsURLRequestInterceptor::State::State() : next_id_(0) {}
90
91 DevToolsURLRequestInterceptor::State::~State() {}
92
93 void DevToolsURLRequestInterceptor::State::ContinueInterceptedRequest(
94 std::string interception_id,
95 std::unique_ptr<Modifications> modifications,
96 CommandCallback callback) {
97 DCHECK_CURRENTLY_ON(BrowserThread::IO);
98 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
99 if (!job) {
100 std::move(callback).Run(CommandStatus::UnknownInterceptionId);
101 } else {
102 std::move(callback).Run(
103 job->ContinueInterceptedRequest(std::move(modifications)));
104 }
105 }
106
107 bool DevToolsURLRequestInterceptor::State::ShouldInterceptRequest(
108 const net::URLRequest* request,
109 const InterceptedPage** intercepted_page) const {
110 // Bail out if we're not intercepting anything.
111 if (intercepted_render_frames_.empty()) {
112 DCHECK(intercepted_frame_tree_nodes_.empty());
113 return false;
114 }
115 const ResourceRequestInfo* resource_request_info =
116 ResourceRequestInfo::ForRequest(request);
117 if (!resource_request_info)
118 return false;
119 int child_id = resource_request_info->GetChildID();
120 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId();
121 if (frame_tree_node_id == -1) {
122 // |frame_tree_node_id| is not set for renderer side requests, fall back to
123 // the RenderFrameID.
124 int render_frame_id = resource_request_info->GetRenderFrameID();
125 const auto find_it = intercepted_render_frames_.find(
126 std::make_pair(render_frame_id, child_id));
127 if (find_it == intercepted_render_frames_.end())
128 return false;
129 *intercepted_page = &find_it->second;
130 } else {
131 // |frame_tree_node_id| is set for browser side navigations, so use that
132 // because the RenderFrameID isn't known (neither is the ChildID).
133 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id);
134 if (find_it == intercepted_frame_tree_nodes_.end())
135 return false;
136 *intercepted_page = &find_it->second;
137 }
138
139 // We don't want to intercept our own sub requests.
140 return sub_requests_.find(request) == sub_requests_.end();
141 }
142
143 DevToolsURLInterceptorRequestJob*
144 DevToolsURLRequestInterceptor::State::CreateDevToolsURLInterceptorRequestJob(
145 net::URLRequest* request,
146 net::NetworkDelegate* network_delegate,
147 const InterceptedPage* intercepted_page) {
148 DCHECK_CURRENTLY_ON(BrowserThread::IO);
149 bool is_redirect;
150 std::string interception_id = GetIdForRequest(request, &is_redirect);
151 DevToolsURLInterceptorRequestJob* job = new DevToolsURLInterceptorRequestJob(
152 this, interception_id, request, network_delegate,
153 intercepted_page->web_contents, intercepted_page->network_handler,
154 is_redirect);
155 interception_id_to_job_map_[interception_id] = job;
156 return job;
157 }
158
159 class DevToolsURLRequestInterceptor::State::InterceptedWebContentsObserver
160 : public WebContentsObserver {
161 public:
162 InterceptedWebContentsObserver(
163 WebContents* web_contents,
164 scoped_refptr<DevToolsURLRequestInterceptor::State> state,
165 base::WeakPtr<protocol::NetworkHandler> network_handler)
166 : WebContentsObserver(web_contents),
167 state_(state),
168 network_handler_(network_handler) {
169 DCHECK_CURRENTLY_ON(BrowserThread::UI);
170 }
171
172 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
173 DCHECK_CURRENTLY_ON(BrowserThread::UI);
174 BrowserThread::PostTask(
175 BrowserThread::IO, FROM_HERE,
176 base::BindOnce(&DevToolsURLRequestInterceptor::State::
177 StartInterceptingRequestsInternal,
178 state_, render_frame_host->GetRoutingID(),
179 render_frame_host->GetFrameTreeNodeId(),
180 render_frame_host->GetProcess()->GetID(), web_contents(),
181 network_handler_));
182 }
183
184 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
185 DCHECK_CURRENTLY_ON(BrowserThread::UI);
186 BrowserThread::PostTask(
187 BrowserThread::IO, FROM_HERE,
188 base::BindOnce(&DevToolsURLRequestInterceptor::State::
189 StopInterceptingRequestsInternal,
190 state_, render_frame_host->GetRoutingID(),
191 render_frame_host->GetFrameTreeNodeId(),
192 render_frame_host->GetProcess()->GetID()));
193 }
194
195 private:
196 scoped_refptr<DevToolsURLRequestInterceptor::State> state_;
197 base::WeakPtr<protocol::NetworkHandler> network_handler_;
198 };
199
200 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsInternal(
201 int render_frame_id,
202 int frame_tree_node_id,
203 int process_id,
204 WebContents* web_contents,
205 base::WeakPtr<protocol::NetworkHandler> network_handler) {
206 DCHECK_CURRENTLY_ON(BrowserThread::IO);
207 intercepted_render_frames_[std::make_pair(render_frame_id, process_id)] =
208 InterceptedPage(web_contents, network_handler);
209 intercepted_frame_tree_nodes_[frame_tree_node_id] =
210 InterceptedPage(web_contents, network_handler);
211 }
212
213 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsInternal(
214 int render_frame_id,
215 int frame_tree_node_id,
216 int process_id) {
217 DCHECK_CURRENTLY_ON(BrowserThread::IO);
218 intercepted_render_frames_.erase(std::make_pair(render_frame_id, process_id));
219 intercepted_frame_tree_nodes_.erase(frame_tree_node_id);
220 }
221
222 void DevToolsURLRequestInterceptor::State::StartInterceptingRequests(
223 WebContents* web_contents,
224 base::WeakPtr<protocol::NetworkHandler> network_handler) {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI);
226 // WebContents methods are UI thread only.
227 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) {
228 BrowserThread::PostTask(
229 BrowserThread::IO, FROM_HERE,
230 base::BindOnce(&DevToolsURLRequestInterceptor::State::
231 StartInterceptingRequestsInternal,
232 this, render_frame_host->GetRoutingID(),
233 render_frame_host->GetFrameTreeNodeId(),
234 render_frame_host->GetProcess()->GetID(), web_contents,
235 network_handler));
236 }
237
238 // Listen for future updates.
239 observers_.emplace(web_contents,
240 base::MakeUnique<InterceptedWebContentsObserver>(
241 web_contents, this, network_handler));
242 }
243
244 void DevToolsURLRequestInterceptor::State::StopInterceptingRequests(
245 WebContents* web_contents) {
246 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 observers_.erase(web_contents);
248 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
249 base::BindOnce(&DevToolsURLRequestInterceptor::State::
250 StopInterceptingRequestsOnIoThread,
251 this, web_contents));
252 }
253
254 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsOnIoThread(
255 WebContents* web_contents) {
256 DCHECK_CURRENTLY_ON(BrowserThread::IO);
257 // Remove any intercepted render frames associated with |web_contents|.
258 base::flat_map<std::pair<int, int>, InterceptedPage>
259 remaining_intercepted_render_frames;
260 for (const auto pair : intercepted_render_frames_) {
261 if (pair.second.web_contents == web_contents)
262 continue;
263 remaining_intercepted_render_frames.insert(pair);
264 }
265 std::swap(remaining_intercepted_render_frames, intercepted_render_frames_);
266
267 // Remove any intercepted frame tree nodes associated with |web_contents|.
268 base::flat_map<int, InterceptedPage> remaining_intercepted_frame_tree_nodes;
269 for (const auto pair : intercepted_frame_tree_nodes_) {
270 if (pair.second.web_contents == web_contents)
271 continue;
272 remaining_intercepted_frame_tree_nodes.insert(pair);
273 }
274 std::swap(remaining_intercepted_frame_tree_nodes,
275 intercepted_frame_tree_nodes_);
276
277 // Tell any jobs associated with |web_contents| to stop intercepting.
278 for (const auto pair : interception_id_to_job_map_) {
279 if (pair.second->web_contents() == web_contents)
280 pair.second->StopIntercepting();
281 }
282 }
283
284 void DevToolsURLRequestInterceptor::State::RegisterSubRequest(
285 const net::URLRequest* sub_request) {
286 DCHECK_CURRENTLY_ON(BrowserThread::IO);
287 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end());
288 sub_requests_.insert(sub_request);
289 }
290
291 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest(
292 const net::URLRequest* sub_request) {
293 DCHECK_CURRENTLY_ON(BrowserThread::IO);
294 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end());
295 sub_requests_.erase(sub_request);
296 }
297
298 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect(
299 const net::URLRequest* request,
300 std::string id) {
301 DCHECK_CURRENTLY_ON(BrowserThread::IO);
302 expected_redirects_[request] = id;
303 }
304
305 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest(
306 const net::URLRequest* request,
307 bool* is_redirect) {
308 DCHECK_CURRENTLY_ON(BrowserThread::IO);
309 auto find_it = expected_redirects_.find(request);
310 if (find_it == expected_redirects_.end()) {
311 *is_redirect = false;
312 return base::StringPrintf("id-%zu", ++next_id_);
313 }
314 *is_redirect = true;
315 std::string id = find_it->second;
316 expected_redirects_.erase(find_it);
317 return id;
318 }
319
320 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob(
321 const std::string& interception_id) const {
322 DCHECK_CURRENTLY_ON(BrowserThread::IO);
323 const auto it = interception_id_to_job_map_.find(interception_id);
324 if (it == interception_id_to_job_map_.end())
325 return nullptr;
326 return it->second;
327 }
328
329 void DevToolsURLRequestInterceptor::State::JobFinished(
330 const std::string& interception_id) {
331 DCHECK_CURRENTLY_ON(BrowserThread::IO);
332 interception_id_to_job_map_.erase(interception_id);
333 }
334
335 // static
336 DevToolsURLRequestInterceptor*
337 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) {
338 DCHECK_CURRENTLY_ON(BrowserThread::UI);
339 return static_cast<DevToolsURLRequestInterceptorUserData*>(
340 context->GetUserData(kDevToolsURLRequestInterceptorKeyName))
341 ->devtools_url_request_interceptor();
342 }
343
344 DevToolsURLRequestInterceptor::Modifications::Modifications(
345 base::Optional<net::Error> error_reason,
346 base::Optional<std::string> raw_response,
347 protocol::Maybe<std::string> modified_url,
348 protocol::Maybe<std::string> modified_method,
349 protocol::Maybe<std::string> modified_post_data,
350 protocol::Maybe<protocol::Network::Headers> modified_headers)
351 : error_reason(std::move(error_reason)),
352 raw_response(std::move(raw_response)),
353 modified_url(std::move(modified_url)),
354 modified_method(std::move(modified_method)),
355 modified_post_data(std::move(modified_post_data)),
356 modified_headers(std::move(modified_headers)) {}
357
358 DevToolsURLRequestInterceptor::Modifications::~Modifications() {}
359
360 DevToolsURLRequestInterceptor::InterceptedPage::InterceptedPage()
361 : web_contents(nullptr) {}
362
363 DevToolsURLRequestInterceptor::InterceptedPage::InterceptedPage(
364 const InterceptedPage& other)
365 : web_contents(other.web_contents),
366 network_handler(other.network_handler) {}
367
368 DevToolsURLRequestInterceptor::InterceptedPage::InterceptedPage(
369 WebContents* web_contents,
370 base::WeakPtr<protocol::NetworkHandler> network_handler)
371 : web_contents(web_contents), network_handler(network_handler) {}
372
373 DevToolsURLRequestInterceptor::InterceptedPage::~InterceptedPage() {}
374
375 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698