OLD | NEW |
(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/resource_request_info.h" |
| 17 #include "net/http/http_request_headers.h" |
| 18 #include "net/url_request/url_request.h" |
| 19 |
| 20 namespace content { |
| 21 |
| 22 namespace { |
| 23 const char kDevToolsURLRequestInterceptorKeyName[] = |
| 24 "DevToolsURLRequestInterceptor"; |
| 25 |
| 26 class DevToolsURLRequestInterceptorUserData |
| 27 : public base::SupportsUserData::Data { |
| 28 public: |
| 29 explicit DevToolsURLRequestInterceptorUserData( |
| 30 DevToolsURLRequestInterceptor* devtools_url_request_interceptor) |
| 31 : devtools_url_request_interceptor_(devtools_url_request_interceptor) {} |
| 32 |
| 33 DevToolsURLRequestInterceptor* devtools_url_request_interceptor() const { |
| 34 return devtools_url_request_interceptor_; |
| 35 } |
| 36 |
| 37 private: |
| 38 DevToolsURLRequestInterceptor* devtools_url_request_interceptor_; |
| 39 |
| 40 DISALLOW_COPY_AND_ASSIGN(DevToolsURLRequestInterceptorUserData); |
| 41 }; |
| 42 |
| 43 } // namespace |
| 44 |
| 45 DevToolsURLRequestInterceptor::DevToolsURLRequestInterceptor( |
| 46 BrowserContext* browser_context) |
| 47 : browser_context_(browser_context), weak_ptr_factory_(this) { |
| 48 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 49 browser_context_->SetUserData( |
| 50 kDevToolsURLRequestInterceptorKeyName, |
| 51 base::MakeUnique<DevToolsURLRequestInterceptorUserData>(this)); |
| 52 |
| 53 // The State object needs to be created on the IO thread or we can't use |
| 54 // WeakPtr when posing tasks to run on the IO thread. |
| 55 BrowserThread::PostTask( |
| 56 BrowserThread::IO, FROM_HERE, |
| 57 base::BindOnce(&DevToolsURLRequestInterceptor::CreateStateOnIoThread, |
| 58 weak_ptr_factory_.GetWeakPtr())); |
| 59 } |
| 60 |
| 61 DevToolsURLRequestInterceptor::~DevToolsURLRequestInterceptor() { |
| 62 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 63 // The BrowserContext owns us, so we don't need to unregister |
| 64 // DevToolsURLRequestInterceptorUserData explicitly. |
| 65 } |
| 66 |
| 67 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRequest( |
| 68 net::URLRequest* request, |
| 69 net::NetworkDelegate* network_delegate) const { |
| 70 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 71 |
| 72 base::WeakPtr<protocol::NetworkHandler> network_handler; |
| 73 if (!state().ShouldInterceptRequest(request, &network_handler)) |
| 74 return nullptr; |
| 75 |
| 76 bool is_redirect; |
| 77 return new DevToolsURLInterceptorRequestJob( |
| 78 state().GetWeakPtr(), state().GetIdForRequest(request, &is_redirect), |
| 79 request, network_delegate, network_handler, is_redirect); |
| 80 } |
| 81 |
| 82 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptRedirect( |
| 83 net::URLRequest* request, |
| 84 net::NetworkDelegate* network_delegate, |
| 85 const GURL& location) const { |
| 86 return nullptr; |
| 87 } |
| 88 |
| 89 net::URLRequestJob* DevToolsURLRequestInterceptor::MaybeInterceptResponse( |
| 90 net::URLRequest* request, |
| 91 net::NetworkDelegate* network_delegate) const { |
| 92 return nullptr; |
| 93 } |
| 94 |
| 95 void DevToolsURLRequestInterceptor::State::AllowRequest( |
| 96 protocol::String interception_id, |
| 97 CommandCallback callback) { |
| 98 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 99 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); |
| 100 if (!job) { |
| 101 std::move(callback).Run(CommandStatus::UnknownInterceptionId); |
| 102 } else { |
| 103 std::move(callback).Run(job->AllowRequest()); |
| 104 } |
| 105 } |
| 106 |
| 107 void DevToolsURLRequestInterceptor::State::BlockRequest( |
| 108 protocol::String interception_id, |
| 109 net::Error error_reason, |
| 110 CommandCallback callback) { |
| 111 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 112 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); |
| 113 if (!job) { |
| 114 std::move(callback).Run(CommandStatus::UnknownInterceptionId); |
| 115 } else { |
| 116 std::move(callback).Run(job->BlockRequest(error_reason)); |
| 117 } |
| 118 } |
| 119 |
| 120 void DevToolsURLRequestInterceptor::State::ModifyRequest( |
| 121 protocol::String interception_id, |
| 122 std::unique_ptr<Modifications> modifications, |
| 123 CommandCallback callback) { |
| 124 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 125 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); |
| 126 if (!job) { |
| 127 std::move(callback).Run(CommandStatus::UnknownInterceptionId); |
| 128 } else { |
| 129 std::move(callback).Run(job->ModifyRequest(std::move(modifications))); |
| 130 } |
| 131 } |
| 132 |
| 133 void DevToolsURLRequestInterceptor::State::MockResponse( |
| 134 protocol::String interception_id, |
| 135 protocol::String raw_response, |
| 136 CommandCallback callback) { |
| 137 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 138 DevToolsURLInterceptorRequestJob* job = GetJob(interception_id); |
| 139 if (!job) { |
| 140 std::move(callback).Run(CommandStatus::UnknownInterceptionId); |
| 141 } else { |
| 142 std::move(callback).Run(job->MockResponse(std::move(raw_response))); |
| 143 } |
| 144 } |
| 145 |
| 146 DevToolsURLRequestInterceptor::State::State() |
| 147 : next_id_(0), weak_ptr_factory_(this) {} |
| 148 |
| 149 DevToolsURLRequestInterceptor::State::~State() {} |
| 150 |
| 151 bool DevToolsURLRequestInterceptor::State::ShouldInterceptRequest( |
| 152 const net::URLRequest* request, |
| 153 base::WeakPtr<protocol::NetworkHandler>* network_handler) const { |
| 154 const ResourceRequestInfo* resource_request_info = |
| 155 ResourceRequestInfo::ForRequest(request); |
| 156 if (!resource_request_info) |
| 157 return false; |
| 158 int frame_tree_node_id = resource_request_info->GetFrameTreeNodeId(); |
| 159 if (frame_tree_node_id == -1) { |
| 160 // |frame_tree_node_id| is not set for renderer side requests, fall back to |
| 161 // the RenderFrameID. |
| 162 int render_frame_id = resource_request_info->GetRenderFrameID(); |
| 163 const auto find_it = intercepted_render_frames_.find(render_frame_id); |
| 164 if (find_it == intercepted_render_frames_.end()) |
| 165 return false; |
| 166 *network_handler = find_it->second; |
| 167 } else { |
| 168 // |frame_tree_node_id| is set for browser side navigations, so use that |
| 169 // because the RenderFrameID isn't known. |
| 170 const auto find_it = intercepted_frame_tree_nodes_.find(frame_tree_node_id); |
| 171 if (find_it == intercepted_frame_tree_nodes_.end()) |
| 172 return false; |
| 173 *network_handler = find_it->second; |
| 174 } |
| 175 |
| 176 // We don't want to intercept our own sub requests. |
| 177 return sub_requests_.find(request) == sub_requests_.end(); |
| 178 } |
| 179 |
| 180 void DevToolsURLRequestInterceptor::State::StartInterceptingRequestsFrom( |
| 181 int render_frame_id, |
| 182 int frame_tree_node_id, |
| 183 base::WeakPtr<protocol::NetworkHandler> network_handler) { |
| 184 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 185 intercepted_render_frames_[render_frame_id] = network_handler; |
| 186 intercepted_frame_tree_nodes_[frame_tree_node_id] = network_handler; |
| 187 } |
| 188 |
| 189 void DevToolsURLRequestInterceptor::State::StopInterceptingRequestsFrom( |
| 190 int render_frame_id, |
| 191 int frame_tree_node_id) { |
| 192 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 193 intercepted_render_frames_.erase(render_frame_id); |
| 194 intercepted_frame_tree_nodes_.erase(frame_tree_node_id); |
| 195 } |
| 196 |
| 197 void DevToolsURLRequestInterceptor::State::RegisterSubRequest( |
| 198 const net::URLRequest* sub_request) { |
| 199 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 200 DCHECK(sub_requests_.find(sub_request) == sub_requests_.end()); |
| 201 sub_requests_.insert(sub_request); |
| 202 } |
| 203 |
| 204 void DevToolsURLRequestInterceptor::State::UnregisterSubRequest( |
| 205 const net::URLRequest* sub_request) { |
| 206 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 207 DCHECK(sub_requests_.find(sub_request) != sub_requests_.end()); |
| 208 sub_requests_.erase(sub_request); |
| 209 } |
| 210 |
| 211 void DevToolsURLRequestInterceptor::State::ExpectRequestAfterRedirect( |
| 212 const net::URLRequest* request, |
| 213 std::string id) { |
| 214 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 215 expected_redirects_[request] = id; |
| 216 } |
| 217 |
| 218 std::string DevToolsURLRequestInterceptor::State::GetIdForRequest( |
| 219 const net::URLRequest* request, |
| 220 bool* is_redirect) { |
| 221 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 222 auto find_it = expected_redirects_.find(request); |
| 223 if (find_it == expected_redirects_.end()) { |
| 224 *is_redirect = false; |
| 225 return base::StringPrintf("id-%zu", ++next_id_); |
| 226 } |
| 227 *is_redirect = true; |
| 228 std::string id = find_it->second; |
| 229 expected_redirects_.erase(find_it); |
| 230 return id; |
| 231 } |
| 232 |
| 233 DevToolsURLInterceptorRequestJob* DevToolsURLRequestInterceptor::State::GetJob( |
| 234 const std::string& interception_id) const { |
| 235 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 236 const auto it = interception_id_to_job_map_.find(interception_id); |
| 237 if (it == interception_id_to_job_map_.end()) |
| 238 return nullptr; |
| 239 return it->second; |
| 240 } |
| 241 |
| 242 void DevToolsURLRequestInterceptor::State::RegisterJob( |
| 243 DevToolsURLInterceptorRequestJob* job, |
| 244 const std::string& interception_id) { |
| 245 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 246 interception_id_to_job_map_[interception_id] = job; |
| 247 } |
| 248 |
| 249 void DevToolsURLRequestInterceptor::State::UnregisterJob( |
| 250 const std::string& interception_id) { |
| 251 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 252 interception_id_to_job_map_.erase(interception_id); |
| 253 } |
| 254 |
| 255 // static |
| 256 DevToolsURLRequestInterceptor* |
| 257 DevToolsURLRequestInterceptor::FromBrowserContext(BrowserContext* context) { |
| 258 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 259 return static_cast<DevToolsURLRequestInterceptorUserData*>( |
| 260 context->GetUserData(kDevToolsURLRequestInterceptorKeyName)) |
| 261 ->devtools_url_request_interceptor(); |
| 262 } |
| 263 |
| 264 void DevToolsURLRequestInterceptor::CreateStateOnIoThread() { |
| 265 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 266 state_.reset(new State()); |
| 267 } |
| 268 |
| 269 } // namespace content |
OLD | NEW |