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"
10 #include "base/bind.h"
11 #include "base/compiler_specific.h"
12 #include "base/memory/singleton.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/strings/string_number_conversions.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
;
36 const int kBufferSize
= 16 * 1024;
40 kStatusDisconnecting
= -2,
41 kStatusConnecting
= -1,
43 // Positive values are used to count open connections.
46 namespace tethering
= ::chrome::devtools::Tethering
;
48 static const char kDevToolsRemoteBrowserTarget
[] = "/devtools/browser";
49 const int kMinVersionPortForwarding
= 28;
51 class SocketTunnel
: public base::NonThreadSafe
{
53 typedef base::Callback
<void(int)> CounterCallback
;
55 static void StartTunnel(const std::string
& host
,
57 const CounterCallback
& callback
,
59 scoped_ptr
<net::StreamSocket
> socket
) {
62 SocketTunnel
* tunnel
= new SocketTunnel(callback
);
63 tunnel
->Start(socket
.Pass(), host
, port
);
67 explicit SocketTunnel(const CounterCallback
& callback
)
69 pending_destruction_(false),
71 about_to_destroy_(false) {
75 void Start(scoped_ptr
<net::StreamSocket
> socket
,
76 const std::string
& host
, int port
) {
77 remote_socket_
.swap(socket
);
79 host_resolver_
= net::HostResolver::CreateDefaultResolver(NULL
);
80 net::HostResolver::RequestInfo
request_info(net::HostPortPair(host
, port
));
81 int result
= host_resolver_
->Resolve(
83 net::DEFAULT_PRIORITY
,
85 base::Bind(&SocketTunnel::OnResolved
, base::Unretained(this)),
88 if (result
!= net::ERR_IO_PENDING
)
92 void OnResolved(int result
) {
98 host_socket_
.reset(new net::TCPClientSocket(address_list_
, NULL
,
99 net::NetLog::Source()));
100 result
= host_socket_
->Connect(base::Bind(&SocketTunnel::OnConnected
,
101 base::Unretained(this)));
102 if (result
!= net::ERR_IO_PENDING
)
107 about_to_destroy_
= true;
109 host_socket_
->Disconnect();
111 remote_socket_
->Disconnect();
115 void OnConnected(int result
) {
121 ++pending_writes_
; // avoid SelfDestruct in first Pump
122 Pump(host_socket_
.get(), remote_socket_
.get());
124 if (pending_destruction_
) {
127 Pump(remote_socket_
.get(), host_socket_
.get());
131 void Pump(net::StreamSocket
* from
, net::StreamSocket
* to
) {
132 scoped_refptr
<net::IOBuffer
> buffer
= new net::IOBuffer(kBufferSize
);
133 int result
= from
->Read(
137 &SocketTunnel::OnRead
, base::Unretained(this), from
, to
, buffer
));
138 if (result
!= net::ERR_IO_PENDING
)
139 OnRead(from
, to
, buffer
, result
);
142 void OnRead(net::StreamSocket
* from
,
143 net::StreamSocket
* to
,
144 scoped_refptr
<net::IOBuffer
> buffer
,
152 scoped_refptr
<net::DrainableIOBuffer
> drainable
=
153 new net::DrainableIOBuffer(buffer
.get(), total
);
156 result
= to
->Write(drainable
.get(),
158 base::Bind(&SocketTunnel::OnWritten
,
159 base::Unretained(this),
163 if (result
!= net::ERR_IO_PENDING
)
164 OnWritten(drainable
, from
, to
, result
);
167 void OnWritten(scoped_refptr
<net::DrainableIOBuffer
> drainable
,
168 net::StreamSocket
* from
,
169 net::StreamSocket
* to
,
177 drainable
->DidConsume(result
);
178 if (drainable
->BytesRemaining() > 0) {
180 result
= to
->Write(drainable
.get(),
181 drainable
->BytesRemaining(),
182 base::Bind(&SocketTunnel::OnWritten
,
183 base::Unretained(this),
187 if (result
!= net::ERR_IO_PENDING
)
188 OnWritten(drainable
, from
, to
, result
);
192 if (pending_destruction_
) {
199 void SelfDestruct() {
200 // In case one of the connections closes, we could get here
201 // from another one due to Disconnect firing back on all
203 if (about_to_destroy_
)
205 if (pending_writes_
> 0) {
206 pending_destruction_
= true;
212 scoped_ptr
<net::StreamSocket
> remote_socket_
;
213 scoped_ptr
<net::StreamSocket
> host_socket_
;
214 scoped_ptr
<net::HostResolver
> host_resolver_
;
215 net::AddressList address_list_
;
217 bool pending_destruction_
;
218 CounterCallback callback_
;
219 bool about_to_destroy_
;
222 typedef DevToolsAndroidBridge::RemoteBrowser::ParsedVersion ParsedVersion
;
224 static bool IsVersionLower(const ParsedVersion
& left
,
225 const ParsedVersion
& right
) {
226 return std::lexicographical_compare(
227 left
.begin(), left
.end(), right
.begin(), right
.end());
230 static bool IsPortForwardingSupported(const ParsedVersion
& version
) {
231 return !version
.empty() && version
[0] >= kMinVersionPortForwarding
;
234 static scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
>
235 FindBestBrowserForTethering(
236 const DevToolsAndroidBridge::RemoteBrowsers browsers
) {
237 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> best_browser
;
238 ParsedVersion newest_version
;
239 for (DevToolsAndroidBridge::RemoteBrowsers::const_iterator it
=
240 browsers
.begin(); it
!= browsers
.end(); ++it
) {
241 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
= *it
;
242 ParsedVersion current_version
= browser
->GetParsedVersion();
243 if (IsPortForwardingSupported(current_version
) &&
244 IsVersionLower(newest_version
, current_version
)) {
245 best_browser
= browser
;
246 newest_version
= current_version
;
254 class PortForwardingController::Connection
255 : public AndroidDeviceManager::AndroidWebSocket::Delegate
{
257 Connection(PortForwardingController
* controller
,
258 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
,
259 const ForwardingMap
& forwarding_map
);
260 ~Connection() override
;
262 const PortStatusMap
& GetPortStatusMap();
264 void UpdateForwardingMap(const ForwardingMap
& new_forwarding_map
);
266 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser() {
271 friend struct content::BrowserThread::DeleteOnThread
<
272 content::BrowserThread::UI
>;
273 friend class base::DeleteHelper
<Connection
>;
275 typedef std::map
<int, std::string
> ForwardingMap
;
276 typedef base::Callback
<void(PortStatus
)> CommandCallback
;
277 typedef std::map
<int, CommandCallback
> CommandCallbackMap
;
279 void SerializeChanges(const std::string
& method
,
280 const ForwardingMap
& old_map
,
281 const ForwardingMap
& new_map
);
283 void SendCommand(const std::string
& method
, int port
);
284 bool ProcessResponse(const std::string
& json
);
286 void ProcessBindResponse(int port
, PortStatus status
);
287 void ProcessUnbindResponse(int port
, PortStatus status
);
289 static void UpdateSocketCountOnHandlerThread(
290 base::WeakPtr
<Connection
> weak_connection
, int port
, int increment
);
291 void UpdateSocketCount(int port
, int increment
);
293 // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
294 void OnSocketOpened() override
;
295 void OnFrameRead(const std::string
& message
) override
;
296 void OnSocketClosed() override
;
298 PortForwardingController
* controller_
;
299 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser_
;
300 scoped_ptr
<AndroidDeviceManager::AndroidWebSocket
> web_socket_
;
303 ForwardingMap forwarding_map_
;
304 CommandCallbackMap pending_responses_
;
305 PortStatusMap port_status_
;
306 base::WeakPtrFactory
<Connection
> weak_factory_
;
308 DISALLOW_COPY_AND_ASSIGN(Connection
);
311 PortForwardingController::Connection::Connection(
312 PortForwardingController
* controller
,
313 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
,
314 const ForwardingMap
& forwarding_map
)
315 : controller_(controller
),
319 forwarding_map_(forwarding_map
),
320 weak_factory_(this) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
322 controller_
->registry_
[browser
->serial()] = this;
323 scoped_refptr
<AndroidDeviceManager::Device
> device(
324 controller_
->bridge_
->FindDevice(browser
->serial()));
325 DCHECK(device
.get());
327 device
->CreateWebSocket(browser
->socket(),
328 kDevToolsRemoteBrowserTarget
, this));
331 PortForwardingController::Connection::~Connection() {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
333 DCHECK(controller_
->registry_
.find(browser_
->serial()) !=
334 controller_
->registry_
.end());
335 controller_
->registry_
.erase(browser_
->serial());
338 void PortForwardingController::Connection::UpdateForwardingMap(
339 const ForwardingMap
& new_forwarding_map
) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
342 SerializeChanges(tethering::unbind::kName
,
343 new_forwarding_map
, forwarding_map_
);
344 SerializeChanges(tethering::bind::kName
,
345 forwarding_map_
, new_forwarding_map
);
347 forwarding_map_
= new_forwarding_map
;
350 void PortForwardingController::Connection::SerializeChanges(
351 const std::string
& method
,
352 const ForwardingMap
& old_map
,
353 const ForwardingMap
& new_map
) {
354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
355 for (ForwardingMap::const_iterator
new_it(new_map
.begin());
356 new_it
!= new_map
.end(); ++new_it
) {
357 int port
= new_it
->first
;
358 const std::string
& location
= new_it
->second
;
359 ForwardingMap::const_iterator old_it
= old_map
.find(port
);
360 if (old_it
!= old_map
.end() && old_it
->second
== location
)
361 continue; // The port points to the same location in both configs, skip.
363 SendCommand(method
, port
);
367 void PortForwardingController::Connection::SendCommand(
368 const std::string
& method
, int port
) {
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
370 base::DictionaryValue params
;
371 if (method
== tethering::bind::kName
) {
372 params
.SetInteger(tethering::bind::kParamPort
, port
);
374 DCHECK_EQ(tethering::unbind::kName
, method
);
375 params
.SetInteger(tethering::unbind::kParamPort
, port
);
377 DevToolsProtocol::Command
command(++command_id_
, method
, ¶ms
);
379 if (method
== tethering::bind::kName
) {
380 pending_responses_
[command
.id()] =
381 base::Bind(&Connection::ProcessBindResponse
,
382 base::Unretained(this), port
);
383 #if defined(DEBUG_DEVTOOLS)
384 port_status_
[port
] = kStatusConnecting
;
385 #endif // defined(DEBUG_DEVTOOLS)
387 PortStatusMap::iterator it
= port_status_
.find(port
);
388 if (it
!= port_status_
.end() && it
->second
== kStatusError
) {
389 // The bind command failed on this port, do not attempt unbind.
390 port_status_
.erase(it
);
394 pending_responses_
[command
.id()] =
395 base::Bind(&Connection::ProcessUnbindResponse
,
396 base::Unretained(this), port
);
397 #if defined(DEBUG_DEVTOOLS)
398 port_status_
[port
] = kStatusDisconnecting
;
399 #endif // defined(DEBUG_DEVTOOLS)
402 web_socket_
->SendFrame(command
.Serialize());
405 bool PortForwardingController::Connection::ProcessResponse(
406 const std::string
& message
) {
407 scoped_ptr
<DevToolsProtocol::Response
> response(
408 DevToolsProtocol::ParseResponse(message
));
412 CommandCallbackMap::iterator it
= pending_responses_
.find(response
->id());
413 if (it
== pending_responses_
.end())
416 it
->second
.Run(response
->error_code() ? kStatusError
: kStatusOK
);
417 pending_responses_
.erase(it
);
421 void PortForwardingController::Connection::ProcessBindResponse(
422 int port
, PortStatus status
) {
423 port_status_
[port
] = status
;
426 void PortForwardingController::Connection::ProcessUnbindResponse(
427 int port
, PortStatus status
) {
428 PortStatusMap::iterator it
= port_status_
.find(port
);
429 if (it
== port_status_
.end())
431 if (status
== kStatusError
)
434 port_status_
.erase(it
);
438 void PortForwardingController::Connection::UpdateSocketCountOnHandlerThread(
439 base::WeakPtr
<Connection
> weak_connection
, int port
, int increment
) {
440 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
441 base::Bind(&Connection::UpdateSocketCount
,
442 weak_connection
, port
, increment
));
445 void PortForwardingController::Connection::UpdateSocketCount(
446 int port
, int increment
) {
447 #if defined(DEBUG_DEVTOOLS)
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
449 PortStatusMap::iterator it
= port_status_
.find(port
);
450 if (it
== port_status_
.end())
452 if (it
->second
< 0 || (it
->second
== 0 && increment
< 0))
454 it
->second
+= increment
;
455 #endif // defined(DEBUG_DEVTOOLS)
458 const PortForwardingController::PortStatusMap
&
459 PortForwardingController::Connection::GetPortStatusMap() {
460 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
464 void PortForwardingController::Connection::OnSocketOpened() {
465 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
467 SerializeChanges(tethering::bind::kName
, ForwardingMap(), forwarding_map_
);
470 void PortForwardingController::Connection::OnSocketClosed() {
474 void PortForwardingController::Connection::OnFrameRead(
475 const std::string
& message
) {
476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
477 if (ProcessResponse(message
))
480 scoped_ptr
<DevToolsProtocol::Notification
> notification(
481 DevToolsProtocol::ParseNotification(message
));
485 if (notification
->method() != tethering::accepted::kName
)
488 base::DictionaryValue
* params
= notification
->params();
493 std::string connection_id
;
494 if (!params
->GetInteger(tethering::accepted::kParamPort
, &port
) ||
495 !params
->GetString(tethering::accepted::kParamConnectionId
,
499 std::map
<int, std::string
>::iterator it
= forwarding_map_
.find(port
);
500 if (it
== forwarding_map_
.end())
503 std::string location
= it
->second
;
504 std::vector
<std::string
> tokens
;
505 Tokenize(location
, ":", &tokens
);
506 int destination_port
= 0;
507 if (tokens
.size() != 2 || !base::StringToInt(tokens
[1], &destination_port
))
509 std::string destination_host
= tokens
[0];
511 SocketTunnel::CounterCallback callback
=
512 base::Bind(&Connection::UpdateSocketCountOnHandlerThread
,
513 weak_factory_
.GetWeakPtr(), port
);
515 scoped_refptr
<AndroidDeviceManager::Device
> device(
516 controller_
->bridge_
->FindDevice(browser_
->serial()));
517 DCHECK(device
.get());
519 connection_id
.c_str(),
520 base::Bind(&SocketTunnel::StartTunnel
,
526 PortForwardingController::PortForwardingController(
528 DevToolsAndroidBridge
* bridge
)
531 pref_service_(profile
->GetPrefs()) {
532 pref_change_registrar_
.Init(pref_service_
);
533 base::Closure callback
= base::Bind(
534 &PortForwardingController::OnPrefsChange
, base::Unretained(this));
535 pref_change_registrar_
.Add(prefs::kDevToolsPortForwardingEnabled
, callback
);
536 pref_change_registrar_
.Add(prefs::kDevToolsPortForwardingConfig
, callback
);
540 PortForwardingController::~PortForwardingController() {}
542 PortForwardingController::ForwardingStatus
543 PortForwardingController::DeviceListChanged(
544 const DevToolsAndroidBridge::RemoteDevices
& devices
) {
545 ForwardingStatus status
;
546 if (forwarding_map_
.empty())
549 for (const auto& device
: devices
) {
550 if (!device
->is_connected())
552 Registry::iterator rit
= registry_
.find(device
->serial());
553 if (rit
== registry_
.end()) {
554 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser(
555 FindBestBrowserForTethering(device
->browsers()));
557 new Connection(this, browser
, forwarding_map_
);
559 status
.push_back(std::make_pair(rit
->second
->browser(),
560 rit
->second
->GetPortStatusMap()));
566 void PortForwardingController::OnPrefsChange() {
567 forwarding_map_
.clear();
569 if (pref_service_
->GetBoolean(prefs::kDevToolsPortForwardingEnabled
)) {
570 const base::DictionaryValue
* dict
=
571 pref_service_
->GetDictionary(prefs::kDevToolsPortForwardingConfig
);
572 for (base::DictionaryValue::Iterator
it(*dict
);
573 !it
.IsAtEnd(); it
.Advance()) {
575 std::string location
;
576 if (base::StringToInt(it
.key(), &port_num
) &&
577 dict
->GetString(it
.key(), &location
))
578 forwarding_map_
[port_num
] = location
;
582 if (!forwarding_map_
.empty()) {
585 std::vector
<Connection
*> registry_copy
;
586 for (Registry::iterator it
= registry_
.begin();
587 it
!= registry_
.end(); ++it
) {
588 registry_copy
.push_back(it
->second
);
590 STLDeleteElements(®istry_copy
);
594 void PortForwardingController::UpdateConnections() {
595 for (Registry::iterator it
= registry_
.begin(); it
!= registry_
.end(); ++it
)
596 it
->second
->UpdateForwardingMap(forwarding_map_
);