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

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

Issue 2739323003: DevTools protocol interception, blocking & modification of requests (Closed)
Patch Set: Address remaining TODOs 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 base::WeakPtr<protocol::NetworkHandler> network_handler;
69 if (!state().ShouldInterceptRequest(request, &network_handler))
70 return nullptr;
71
72 bool is_redirect;
73 std::string interception_id = state().GetIdForRequest(request, &is_redirect);
74 return new DevToolsURLInterceptorRequestJob(
75 state().GetWeakPtr(), interception_id, request, network_delegate,
76 network_handler, is_redirect);
77 }
78
79 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect(
80 net::URLRequest* request,
81 net::NetworkDelegate* network_delegate,
82 const GURL& location) const {
83 return nullptr;
84 }
85
86 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse(
87 net::URLRequest* request,
88 net::NetworkDelegate* network_delegate) const {
89 return nullptr;
90 }
91
92 DevToolsURLRequestInterceptor::State::State()
93 : next_id_(0), weak_ptr_factory_(this) {}
94
95 DevToolsURLRequestInterceptor::State::~State() {}
96
97 void DevToolsURLRequestInterceptor::State::ContinueRequest(
98 std::string interception_id,
99 std::unique_ptr<Modifications> modifications,
100 CommandCallback callback) {
101 DCHECK_CURRENTLY_ON(BrowserThread::IO);
102 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id);
103 if (!job) {
104 std::move(callback).Run(CommandStatus::UnknownInterceptionId);
dgozman 2017/05/25 21:29:35 Wow, that's an interesting C++ construct.
alex clarke (OOO till 29th) 2017/05/26 19:37:03 Agreed, I wish it wasn't necessary.
105 } else {
106 std::move(callback).Run(job->ContinueRequest(std::move(modifications)));
107 }
108 }
109
110 bool DevToolsURLRequestInterceptor::State::ShouldInterceptRequest(
111 const net::URLRequest* request,
112 base::WeakPtr<protocol::NetworkHandler>* network_handler) const {
113 const ResourceRequestInfo* resource_request_info =
114 ResourceRequestInfo::ForRequest(request);
115 if (!resource_request_info)
116 return false;
117 int child_id = resource_request_info->GetChildID();
118 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId();
119 if (frame_tree_node_id == -1) {
120 // |frame_tree_node_id| is not set for renderer side requests, fall back to
121 // the RenderFrameID.
122 int render_frame_id = resource_request_info->GetRenderFrameID();
123 const auto find_it = intercepted_render_frames_.find(
124 std::make_pair(render_frame_id, child_id));
125 if (find_it == intercepted_render_frames_.end())
126 return false;
127 *network_handler = find_it->second;
128 } else {
129 // |frame_tree_node_id| is set for browser side navigations, so use that
130 // because the RenderFrameID isn't known (neither is the ChildIDe
dgozman 2017/05/25 21:29:36 nit: comment looks unfinished
alex clarke (OOO till 29th) 2017/05/26 19:37:03 Done.
131 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id);
132 if (find_it == intercepted_frame_tree_nodes_.end())
133 return false;
134 *network_handler = find_it->second;
135 }
136
137 // We don't want to intercept our own sub requests.
138 return sub_requests_.find(request) == sub_requests_.end();
dgozman 2017/05/25 21:29:35 Let's return |false| early from this method, as it
alex clarke (OOO till 29th) 2017/05/26 19:37:03 I agree with the sentiment of failing fast, howeve
dgozman 2017/05/30 21:44:27 Does this mean we have sub requests for webcontent
alex clarke (OOO till 29th) 2017/05/31 15:52:02 It's not :) There is a 1:1 relationship between a
139 }
140
141 class DevToolsURLRequestInterceptor::State::InterceptedWebContentsObserver
142 : public WebContentsObserver {
143 public:
144 InterceptedWebContentsObserver(
145 WebContents* web_contents,
146 base::WeakPtr<DevToolsURLRequestInterceptor::State> state,
147 base::WeakPtr<protocol::NetworkHandler> network_handler)
148 : WebContentsObserver(web_contents),
149 state_(state),
150 network_handler_(network_handler) {}
151
152 void RenderFrameCreated(RenderFrameHost* render_frame_host) override {
153 DCHECK_CURRENTLY_ON(BrowserThread::UI);
154 BrowserThread::PostTask(
155 BrowserThread::IO, FROM_HERE,
156 base::BindOnce(&DevToolsURLRequestInterceptor::State::
157 StartInterceptingRequestsInternal,
158 state_, render_frame_host->GetRoutingID(),
159 render_frame_host->GetFrameTreeNodeId(),
160 render_frame_host->GetProcess()->GetID(),
161 network_handler_));
162 }
163
164 void RenderFrameDeleted(RenderFrameHost* render_frame_host) override {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI);
166 BrowserThread::PostTask(
167 BrowserThread::IO, FROM_HERE,
168 base::BindOnce(&DevToolsURLRequestInterceptor::State::
169 StopInterceptingRequestsInternal,
170 state_, render_frame_host->GetRoutingID(),
171 render_frame_host->GetFrameTreeNodeId(),
172 render_frame_host->GetProcess()->GetID()));
173 }
174
175 private:
176 base::WeakPtr<DevToolsURLRequestInterceptor::State> state_;
177 base::WeakPtr<protocol::NetworkHandler> network_handler_;
178 };
179
180 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsInternal(
181 int render_frame_id,
182 int frame_tree_node_id,
183 int process_id,
184 base::WeakPtr<protocol::NetworkHandler> network_handler) {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO);
186 intercepted_render_frames_[std::make_pair(render_frame_id, process_id)] =
187 network_handler;
188 intercepted_frame_tree_nodes_[frame_tree_node_id] = network_handler;
189 }
190
191 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsInternal(
192 int render_frame_id,
193 int frame_tree_node_id,
194 int process_id) {
195 DCHECK_CURRENTLY_ON(BrowserThread::IO);
196 intercepted_render_frames_.erase(std::make_pair(render_frame_id, process_id));
197 intercepted_frame_tree_nodes_.erase(frame_tree_node_id);
198 }
199
200 void DevToolsURLRequestInterceptor::State::StartInterceptingRequests(
201 WebContents* web_contents,
202 base::WeakPtr<protocol::NetworkHandler> network_handler) {
203 DCHECK_CURRENTLY_ON(BrowserThread::IO);
204 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) {
dgozman 2017/05/25 21:29:35 I don't think WebContents' methods are callable on
alex clarke (OOO till 29th) 2017/05/26 19:37:03 OK done. Too bad there are not more dchecks on the
205 StartInterceptingRequestsInternal(render_frame_host->GetRoutingID(),
206 render_frame_host->GetFrameTreeNodeId(),
207 render_frame_host->GetProcess()->GetID(),
208 network_handler);
209 }
210
211 // Listen for future updates.
212 observers_.emplace(
213 web_contents,
214 base::MakeUnique<InterceptedWebContentsObserver>(
215 web_contents, weak_ptr_factory_.GetWeakPtr(), network_handler));
216 }
217
218 void DevToolsURLRequestInterceptor::State::StopInterceptingRequests(
dgozman 2017/05/25 21:29:35 We should also release all requests suspended at t
alex clarke (OOO till 29th) 2017/05/26 19:37:03 Good point, done.
219 WebContents* web_contents) {
220 DCHECK_CURRENTLY_ON(BrowserThread::IO);
221 for (RenderFrameHost* render_frame_host : web_contents->GetAllFrames()) {
222 StopInterceptingRequestsInternal(render_frame_host->GetRoutingID(),
223 render_frame_host->GetFrameTreeNodeId(),
224 render_frame_host->GetProcess()->GetID());
225 }
226 observers_.erase(web_contents);
227 }
228
229 void DevToolsURLRequestInterceptor::State::RegisterSubRequest(
230 const net::URLRequest* sub_request) {
231 DCHECK_CURRENTLY_ON(BrowserThread::IO);
232 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end());
233 sub_requests_.insert(sub_request);
234 }
235
236 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest(
237 const net::URLRequest* sub_request) {
238 DCHECK_CURRENTLY_ON(BrowserThread::IO);
239 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end());
240 sub_requests_.erase(sub_request);
241 }
242
243 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect(
244 const net::URLRequest* request,
245 std::string id) {
246 DCHECK_CURRENTLY_ON(BrowserThread::IO);
247 expected_redirects_[request] = id;
248 }
249
250 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest(
251 const net::URLRequest* request,
252 bool* is_redirect) {
253 DCHECK_CURRENTLY_ON(BrowserThread::IO);
254 auto find_it = expected_redirects_.find(request);
255 if (find_it == expected_redirects_.end()) {
256 *is_redirect = false;
257 return base::StringPrintf("id-%zu", ++next_id_);
258 }
259 *is_redirect = true;
260 std::string id = find_it->second;
261 expected_redirects_.erase(find_it);
262 return id;
263 }
264
265 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob(
266 const std::string& interception_id) const {
267 DCHECK_CURRENTLY_ON(BrowserThread::IO);
268 const auto it = interception_id_to_job_map_.find(interception_id);
269 if (it == interception_id_to_job_map_.end())
270 return nullptr;
271 return it->second;
272 }
273
274 void DevToolsURLRequestInterceptor::State::RegisterJob(
275 DevToolsURLInterceptorRequestJob* job,
276 const std::string& interception_id) {
277 DCHECK_CURRENTLY_ON(BrowserThread::IO);
278 interception_id_to_job_map_[interception_id] = job;
279 }
280
281 void DevToolsURLRequestInterceptor::State::UnregisterJob(
282 const std::string& interception_id) {
283 DCHECK_CURRENTLY_ON(BrowserThread::IO);
284 interception_id_to_job_map_.erase(interception_id);
285 }
286
287 // static
288 DevToolsURLRequestInterceptor*
289 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) {
290 DCHECK_CURRENTLY_ON(BrowserThread::UI);
291 return static_cast<DevToolsURLRequestInterceptorUserData*>(
292 context->GetUserData(kDevToolsURLRequestInterceptorKeyName))
293 ->devtools_url_request_interceptor();
294 }
295
296 DevToolsURLRequestInterceptor::Modifications::Modifications(
297 base::Optional<net::Error> error_reason,
298 base::Optional<std::string> raw_response,
299 protocol::Maybe<std::string> modified_url,
300 protocol::Maybe<std::string> modified_method,
301 protocol::Maybe<std::string> modified_post_data,
302 protocol::Maybe<protocol::Network::Headers> modified_headers)
303 : error_reason(std::move(error_reason)),
304 raw_response(std::move(raw_response)),
305 modified_url(std::move(modified_url)),
306 modified_method(std::move(modified_method)),
307 modified_post_data(std::move(modified_post_data)),
308 modified_headers(std::move(modified_headers)) {}
309
310 DevToolsURLRequestInterceptor::Modifications::~Modifications() {}
311
312 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698