Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / devtools / device / port_forwarding_controller.cc
blob2f4a27eebcb762370d44afdee228022b5596f02f
1 // Copyright 2014 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.
5 #include "chrome/browser/devtools/device/port_forwarding_controller.h"
7 #include <map>
9 #include "base/bind.h"
10 #include "base/compiler_specific.h"
11 #include "base/memory/singleton.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/threading/non_thread_safe.h"
19 #include "chrome/browser/devtools/devtools_protocol.h"
20 #include "chrome/browser/devtools/devtools_protocol_constants.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/pref_names.h"
23 #include "components/keyed_service/content/browser_context_dependency_manager.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "net/base/address_list.h"
26 #include "net/base/io_buffer.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/net_util.h"
29 #include "net/dns/host_resolver.h"
30 #include "net/socket/tcp_client_socket.h"
32 using content::BrowserThread;
34 namespace {
36 const int kBufferSize = 16 * 1024;
38 enum {
39 kStatusError = -3,
40 kStatusDisconnecting = -2,
41 kStatusConnecting = -1,
42 kStatusOK = 0,
45 namespace tethering = ::chrome::devtools::Tethering;
47 static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
49 class SocketTunnel : public base::NonThreadSafe {
50 public:
51 static void StartTunnel(const std::string& host,
52 int port,
53 int result,
54 scoped_ptr<net::StreamSocket> socket) {
55 if (result == net::OK)
56 new SocketTunnel(socket.Pass(), host, port);
59 private:
60 SocketTunnel(scoped_ptr<net::StreamSocket> socket,
61 const std::string& host,
62 int port)
63 : remote_socket_(socket.Pass()),
64 pending_writes_(0),
65 pending_destruction_(false) {
66 host_resolver_ = net::HostResolver::CreateDefaultResolver(nullptr);
67 net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
68 int result = host_resolver_->Resolve(
69 request_info,
70 net::DEFAULT_PRIORITY,
71 &address_list_,
72 base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
73 nullptr,
74 net::BoundNetLog());
75 if (result != net::ERR_IO_PENDING)
76 OnResolved(result);
79 void OnResolved(int result) {
80 if (result < 0) {
81 SelfDestruct();
82 return;
85 host_socket_.reset(new net::TCPClientSocket(address_list_, nullptr,
86 net::NetLog::Source()));
87 result = host_socket_->Connect(base::Bind(&SocketTunnel::OnConnected,
88 base::Unretained(this)));
89 if (result != net::ERR_IO_PENDING)
90 OnConnected(result);
93 void OnConnected(int result) {
94 if (result < 0) {
95 SelfDestruct();
96 return;
99 ++pending_writes_; // avoid SelfDestruct in first Pump
100 Pump(host_socket_.get(), remote_socket_.get());
101 --pending_writes_;
102 if (pending_destruction_) {
103 SelfDestruct();
104 } else {
105 Pump(remote_socket_.get(), host_socket_.get());
109 void Pump(net::StreamSocket* from, net::StreamSocket* to) {
110 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
111 int result = from->Read(
112 buffer.get(),
113 kBufferSize,
114 base::Bind(
115 &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
116 if (result != net::ERR_IO_PENDING)
117 OnRead(from, to, buffer, result);
120 void OnRead(net::StreamSocket* from,
121 net::StreamSocket* to,
122 scoped_refptr<net::IOBuffer> buffer,
123 int result) {
124 if (result <= 0) {
125 SelfDestruct();
126 return;
129 int total = result;
130 scoped_refptr<net::DrainableIOBuffer> drainable =
131 new net::DrainableIOBuffer(buffer.get(), total);
133 ++pending_writes_;
134 result = to->Write(drainable.get(),
135 total,
136 base::Bind(&SocketTunnel::OnWritten,
137 base::Unretained(this),
138 drainable,
139 from,
140 to));
141 if (result != net::ERR_IO_PENDING)
142 OnWritten(drainable, from, to, result);
145 void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
146 net::StreamSocket* from,
147 net::StreamSocket* to,
148 int result) {
149 --pending_writes_;
150 if (result < 0) {
151 SelfDestruct();
152 return;
155 drainable->DidConsume(result);
156 if (drainable->BytesRemaining() > 0) {
157 ++pending_writes_;
158 result = to->Write(drainable.get(),
159 drainable->BytesRemaining(),
160 base::Bind(&SocketTunnel::OnWritten,
161 base::Unretained(this),
162 drainable,
163 from,
164 to));
165 if (result != net::ERR_IO_PENDING)
166 OnWritten(drainable, from, to, result);
167 return;
170 if (pending_destruction_) {
171 SelfDestruct();
172 return;
174 Pump(from, to);
177 void SelfDestruct() {
178 if (pending_writes_ > 0) {
179 pending_destruction_ = true;
180 return;
182 delete this;
185 scoped_ptr<net::StreamSocket> remote_socket_;
186 scoped_ptr<net::StreamSocket> host_socket_;
187 scoped_ptr<net::HostResolver> host_resolver_;
188 net::AddressList address_list_;
189 int pending_writes_;
190 bool pending_destruction_;
193 } // namespace
195 class PortForwardingController::Connection
196 : public AndroidDeviceManager::AndroidWebSocket::Delegate {
197 public:
198 Connection(PortForwardingController* controller,
199 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
200 const ForwardingMap& forwarding_map);
201 ~Connection() override;
203 const PortStatusMap& GetPortStatusMap();
205 void UpdateForwardingMap(const ForwardingMap& new_forwarding_map);
207 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser() {
208 return browser_;
211 private:
212 friend struct content::BrowserThread::DeleteOnThread<
213 content::BrowserThread::UI>;
214 friend class base::DeleteHelper<Connection>;
216 typedef std::map<int, std::string> ForwardingMap;
217 typedef base::Callback<void(PortStatus)> CommandCallback;
218 typedef std::map<int, CommandCallback> CommandCallbackMap;
220 void SerializeChanges(const std::string& method,
221 const ForwardingMap& old_map,
222 const ForwardingMap& new_map);
224 void SendCommand(const std::string& method, int port);
225 bool ProcessResponse(const std::string& json);
227 void ProcessBindResponse(int port, PortStatus status);
228 void ProcessUnbindResponse(int port, PortStatus status);
230 // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
231 void OnSocketOpened() override;
232 void OnFrameRead(const std::string& message) override;
233 void OnSocketClosed() override;
235 PortForwardingController* controller_;
236 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
237 scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
238 int command_id_;
239 bool connected_;
240 ForwardingMap forwarding_map_;
241 CommandCallbackMap pending_responses_;
242 PortStatusMap port_status_;
244 DISALLOW_COPY_AND_ASSIGN(Connection);
247 PortForwardingController::Connection::Connection(
248 PortForwardingController* controller,
249 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
250 const ForwardingMap& forwarding_map)
251 : controller_(controller),
252 browser_(browser),
253 command_id_(0),
254 connected_(false),
255 forwarding_map_(forwarding_map) {
256 DCHECK_CURRENTLY_ON(BrowserThread::UI);
257 controller_->registry_[browser->serial()] = this;
258 scoped_refptr<AndroidDeviceManager::Device> device(
259 controller_->bridge_->FindDevice(browser->serial()));
260 DCHECK(device.get());
261 web_socket_.reset(
262 device->CreateWebSocket(browser->socket(),
263 kDevToolsRemoteBrowserTarget, this));
266 PortForwardingController::Connection::~Connection() {
267 DCHECK_CURRENTLY_ON(BrowserThread::UI);
268 DCHECK(controller_->registry_.find(browser_->serial()) !=
269 controller_->registry_.end());
270 controller_->registry_.erase(browser_->serial());
273 void PortForwardingController::Connection::UpdateForwardingMap(
274 const ForwardingMap& new_forwarding_map) {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI);
276 if (connected_) {
277 SerializeChanges(tethering::unbind::kName,
278 new_forwarding_map, forwarding_map_);
279 SerializeChanges(tethering::bind::kName,
280 forwarding_map_, new_forwarding_map);
282 forwarding_map_ = new_forwarding_map;
285 void PortForwardingController::Connection::SerializeChanges(
286 const std::string& method,
287 const ForwardingMap& old_map,
288 const ForwardingMap& new_map) {
289 DCHECK_CURRENTLY_ON(BrowserThread::UI);
290 for (ForwardingMap::const_iterator new_it(new_map.begin());
291 new_it != new_map.end(); ++new_it) {
292 int port = new_it->first;
293 const std::string& location = new_it->second;
294 ForwardingMap::const_iterator old_it = old_map.find(port);
295 if (old_it != old_map.end() && old_it->second == location)
296 continue; // The port points to the same location in both configs, skip.
298 SendCommand(method, port);
302 void PortForwardingController::Connection::SendCommand(
303 const std::string& method, int port) {
304 DCHECK_CURRENTLY_ON(BrowserThread::UI);
305 scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue);
306 if (method == tethering::bind::kName) {
307 params->SetInteger(tethering::bind::kParamPort, port);
308 } else {
309 DCHECK_EQ(tethering::unbind::kName, method);
310 params->SetInteger(tethering::unbind::kParamPort, port);
312 int id = ++command_id_;
314 if (method == tethering::bind::kName) {
315 pending_responses_[id] =
316 base::Bind(&Connection::ProcessBindResponse,
317 base::Unretained(this), port);
318 #if defined(DEBUG_DEVTOOLS)
319 port_status_[port] = kStatusConnecting;
320 #endif // defined(DEBUG_DEVTOOLS)
321 } else {
322 PortStatusMap::iterator it = port_status_.find(port);
323 if (it != port_status_.end() && it->second == kStatusError) {
324 // The bind command failed on this port, do not attempt unbind.
325 port_status_.erase(it);
326 return;
329 pending_responses_[id] =
330 base::Bind(&Connection::ProcessUnbindResponse,
331 base::Unretained(this), port);
332 #if defined(DEBUG_DEVTOOLS)
333 port_status_[port] = kStatusDisconnecting;
334 #endif // defined(DEBUG_DEVTOOLS)
337 web_socket_->SendFrame(
338 DevToolsProtocol::SerializeCommand(id, method, params.Pass()));
341 bool PortForwardingController::Connection::ProcessResponse(
342 const std::string& message) {
343 int id = 0;
344 int error_code = 0;
345 if (!DevToolsProtocol::ParseResponse(message, &id, &error_code))
346 return false;
348 CommandCallbackMap::iterator it = pending_responses_.find(id);
349 if (it == pending_responses_.end())
350 return false;
352 it->second.Run(error_code ? kStatusError : kStatusOK);
353 pending_responses_.erase(it);
354 return true;
357 void PortForwardingController::Connection::ProcessBindResponse(
358 int port, PortStatus status) {
359 port_status_[port] = status;
362 void PortForwardingController::Connection::ProcessUnbindResponse(
363 int port, PortStatus status) {
364 PortStatusMap::iterator it = port_status_.find(port);
365 if (it == port_status_.end())
366 return;
367 if (status == kStatusError)
368 it->second = status;
369 else
370 port_status_.erase(it);
373 const PortForwardingController::PortStatusMap&
374 PortForwardingController::Connection::GetPortStatusMap() {
375 DCHECK_CURRENTLY_ON(BrowserThread::UI);
376 return port_status_;
379 void PortForwardingController::Connection::OnSocketOpened() {
380 DCHECK_CURRENTLY_ON(BrowserThread::UI);
381 connected_ = true;
382 SerializeChanges(tethering::bind::kName, ForwardingMap(), forwarding_map_);
385 void PortForwardingController::Connection::OnSocketClosed() {
386 delete this;
389 void PortForwardingController::Connection::OnFrameRead(
390 const std::string& message) {
391 DCHECK_CURRENTLY_ON(BrowserThread::UI);
392 if (ProcessResponse(message))
393 return;
395 std::string method;
396 scoped_ptr<base::DictionaryValue> params;
397 if (!DevToolsProtocol::ParseNotification(message, &method, &params))
398 return;
400 if (method != tethering::accepted::kName || !params)
401 return;
403 int port;
404 std::string connection_id;
405 if (!params->GetInteger(tethering::accepted::kParamPort, &port) ||
406 !params->GetString(tethering::accepted::kParamConnectionId,
407 &connection_id))
408 return;
410 std::map<int, std::string>::iterator it = forwarding_map_.find(port);
411 if (it == forwarding_map_.end())
412 return;
414 std::string location = it->second;
415 std::vector<std::string> tokens = base::SplitString(
416 location, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
417 int destination_port = 0;
418 if (tokens.size() != 2 || !base::StringToInt(tokens[1], &destination_port))
419 return;
420 std::string destination_host = tokens[0];
422 scoped_refptr<AndroidDeviceManager::Device> device(
423 controller_->bridge_->FindDevice(browser_->serial()));
424 DCHECK(device.get());
425 device->OpenSocket(
426 connection_id.c_str(),
427 base::Bind(&SocketTunnel::StartTunnel,
428 destination_host,
429 destination_port));
432 PortForwardingController::PortForwardingController(
433 Profile* profile,
434 DevToolsAndroidBridge* bridge)
435 : bridge_(bridge),
436 pref_service_(profile->GetPrefs()) {
437 pref_change_registrar_.Init(pref_service_);
438 base::Closure callback = base::Bind(
439 &PortForwardingController::OnPrefsChange, base::Unretained(this));
440 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, callback);
441 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, callback);
442 OnPrefsChange();
445 PortForwardingController::~PortForwardingController() {}
447 PortForwardingController::ForwardingStatus
448 PortForwardingController::DeviceListChanged(
449 const DevToolsAndroidBridge::RemoteDevices& devices) {
450 ForwardingStatus status;
451 if (forwarding_map_.empty())
452 return status;
454 for (const auto& device : devices) {
455 if (!device->is_connected())
456 continue;
457 Registry::iterator rit = registry_.find(device->serial());
458 if (rit == registry_.end()) {
459 if (device->browsers().size() > 0)
460 new Connection(this, device->browsers()[0], forwarding_map_);
461 } else {
462 status.push_back(std::make_pair(rit->second->browser(),
463 rit->second->GetPortStatusMap()));
466 return status;
469 void PortForwardingController::OnPrefsChange() {
470 forwarding_map_.clear();
472 if (pref_service_->GetBoolean(prefs::kDevToolsPortForwardingEnabled)) {
473 const base::DictionaryValue* dict =
474 pref_service_->GetDictionary(prefs::kDevToolsPortForwardingConfig);
475 for (base::DictionaryValue::Iterator it(*dict);
476 !it.IsAtEnd(); it.Advance()) {
477 int port_num;
478 std::string location;
479 if (base::StringToInt(it.key(), &port_num) &&
480 dict->GetString(it.key(), &location))
481 forwarding_map_[port_num] = location;
485 if (!forwarding_map_.empty()) {
486 UpdateConnections();
487 } else {
488 std::vector<Connection*> registry_copy;
489 for (Registry::iterator it = registry_.begin();
490 it != registry_.end(); ++it) {
491 registry_copy.push_back(it->second);
493 STLDeleteElements(&registry_copy);
497 void PortForwardingController::UpdateConnections() {
498 for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
499 it->second->UpdateForwardingMap(forwarding_map_);