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/profiles/profile.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/keyed_service/content/browser_context_dependency_manager.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "net/base/address_list.h"
25 #include "net/base/io_buffer.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_util.h"
28 #include "net/dns/host_resolver.h"
29 #include "net/socket/tcp_client_socket.h"
31 using content::BrowserThread
;
35 const int kBufferSize
= 16 * 1024;
39 kStatusDisconnecting
= -2,
40 kStatusConnecting
= -1,
42 // Positive values are used to count open connections.
45 static const char kPortAttribute
[] = "port";
46 static const char kConnectionIdAttribute
[] = "connectionId";
47 static const char kTetheringAccepted
[] = "Tethering.accepted";
48 static const char kTetheringBind
[] = "Tethering.bind";
49 static const char kTetheringUnbind
[] = "Tethering.unbind";
51 static const char kDevToolsRemoteBrowserTarget
[] = "/devtools/browser";
52 const int kMinVersionPortForwarding
= 28;
54 class SocketTunnel
: public base::NonThreadSafe
{
56 typedef base::Callback
<void(int)> CounterCallback
;
58 static void StartTunnel(const std::string
& host
,
60 const CounterCallback
& callback
,
62 net::StreamSocket
* socket
) {
65 SocketTunnel
* tunnel
= new SocketTunnel(callback
);
66 tunnel
->Start(socket
, host
, port
);
70 explicit SocketTunnel(const CounterCallback
& callback
)
72 pending_destruction_(false),
74 about_to_destroy_(false) {
78 void Start(net::StreamSocket
* socket
, const std::string
& host
, int port
) {
79 remote_socket_
.reset(socket
);
81 host_resolver_
= net::HostResolver::CreateDefaultResolver(NULL
);
82 net::HostResolver::RequestInfo
request_info(net::HostPortPair(host
, port
));
83 int result
= host_resolver_
->Resolve(
85 net::DEFAULT_PRIORITY
,
87 base::Bind(&SocketTunnel::OnResolved
, base::Unretained(this)),
90 if (result
!= net::ERR_IO_PENDING
)
94 void OnResolved(int result
) {
100 host_socket_
.reset(new net::TCPClientSocket(address_list_
, NULL
,
101 net::NetLog::Source()));
102 result
= host_socket_
->Connect(base::Bind(&SocketTunnel::OnConnected
,
103 base::Unretained(this)));
104 if (result
!= net::ERR_IO_PENDING
)
109 about_to_destroy_
= true;
111 host_socket_
->Disconnect();
113 remote_socket_
->Disconnect();
117 void OnConnected(int result
) {
123 Pump(host_socket_
.get(), remote_socket_
.get());
124 Pump(remote_socket_
.get(), host_socket_
.get());
127 void Pump(net::StreamSocket
* from
, net::StreamSocket
* to
) {
128 scoped_refptr
<net::IOBuffer
> buffer
= new net::IOBuffer(kBufferSize
);
129 int result
= from
->Read(
133 &SocketTunnel::OnRead
, base::Unretained(this), from
, to
, buffer
));
134 if (result
!= net::ERR_IO_PENDING
)
135 OnRead(from
, to
, buffer
, result
);
138 void OnRead(net::StreamSocket
* from
,
139 net::StreamSocket
* to
,
140 scoped_refptr
<net::IOBuffer
> buffer
,
148 scoped_refptr
<net::DrainableIOBuffer
> drainable
=
149 new net::DrainableIOBuffer(buffer
.get(), total
);
152 result
= to
->Write(drainable
.get(),
154 base::Bind(&SocketTunnel::OnWritten
,
155 base::Unretained(this),
159 if (result
!= net::ERR_IO_PENDING
)
160 OnWritten(drainable
, from
, to
, result
);
163 void OnWritten(scoped_refptr
<net::DrainableIOBuffer
> drainable
,
164 net::StreamSocket
* from
,
165 net::StreamSocket
* to
,
173 drainable
->DidConsume(result
);
174 if (drainable
->BytesRemaining() > 0) {
176 result
= to
->Write(drainable
.get(),
177 drainable
->BytesRemaining(),
178 base::Bind(&SocketTunnel::OnWritten
,
179 base::Unretained(this),
183 if (result
!= net::ERR_IO_PENDING
)
184 OnWritten(drainable
, from
, to
, result
);
188 if (pending_destruction_
) {
195 void SelfDestruct() {
196 // In case one of the connections closes, we could get here
197 // from another one due to Disconnect firing back on all
199 if (about_to_destroy_
)
201 if (pending_writes_
> 0) {
202 pending_destruction_
= true;
208 scoped_ptr
<net::StreamSocket
> remote_socket_
;
209 scoped_ptr
<net::StreamSocket
> host_socket_
;
210 scoped_ptr
<net::HostResolver
> host_resolver_
;
211 net::AddressList address_list_
;
213 bool pending_destruction_
;
214 CounterCallback callback_
;
215 bool about_to_destroy_
;
218 typedef DevToolsAndroidBridge::RemoteBrowser::ParsedVersion ParsedVersion
;
220 static bool IsVersionLower(const ParsedVersion
& left
,
221 const ParsedVersion
& right
) {
222 return std::lexicographical_compare(
223 left
.begin(), left
.end(), right
.begin(), right
.end());
226 static bool IsPortForwardingSupported(const ParsedVersion
& version
) {
227 return !version
.empty() && version
[0] >= kMinVersionPortForwarding
;
230 static scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
>
231 FindBestBrowserForTethering(
232 const DevToolsAndroidBridge::RemoteBrowsers browsers
) {
233 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> best_browser
;
234 ParsedVersion newest_version
;
235 for (DevToolsAndroidBridge::RemoteBrowsers::const_iterator it
=
236 browsers
.begin(); it
!= browsers
.end(); ++it
) {
237 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
= *it
;
238 ParsedVersion current_version
= browser
->GetParsedVersion();
239 if (browser
->IsChrome() &&
240 IsPortForwardingSupported(current_version
) &&
241 IsVersionLower(newest_version
, current_version
)) {
242 best_browser
= browser
;
243 newest_version
= current_version
;
251 class PortForwardingController::Connection
252 : public DevToolsAndroidBridge::AndroidWebSocket::Delegate
,
253 public base::RefCountedThreadSafe
<
255 content::BrowserThread::DeleteOnUIThread
> {
257 Connection(Registry
* registry
,
258 scoped_refptr
<DevToolsAndroidBridge::RemoteDevice
> device
,
259 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
,
260 const ForwardingMap
& forwarding_map
);
262 const PortStatusMap
& GetPortStatusMap();
264 void UpdateForwardingMap(const ForwardingMap
& new_forwarding_map
);
269 friend struct content::BrowserThread::DeleteOnThread
<
270 content::BrowserThread::UI
>;
271 friend class base::DeleteHelper
<Connection
>;
273 virtual ~Connection();
275 typedef std::map
<int, std::string
> ForwardingMap
;
277 typedef base::Callback
<void(PortStatus
)> CommandCallback
;
278 typedef std::map
<int, CommandCallback
> CommandCallbackMap
;
280 void SerializeChanges(const std::string
& method
,
281 const ForwardingMap
& old_map
,
282 const ForwardingMap
& new_map
);
284 void SendCommand(const std::string
& method
, int port
);
285 bool ProcessResponse(const std::string
& json
);
287 void ProcessBindResponse(int port
, PortStatus status
);
288 void ProcessUnbindResponse(int port
, PortStatus status
);
290 void UpdateSocketCountOnHandlerThread(int port
, int increment
);
291 void UpdateSocketCount(int port
, int increment
);
293 // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
294 virtual void OnSocketOpened() OVERRIDE
;
295 virtual void OnFrameRead(const std::string
& message
) OVERRIDE
;
296 virtual void OnSocketClosed(bool closed_by_device
) OVERRIDE
;
298 PortForwardingController::Registry
* registry_
;
299 scoped_refptr
<DevToolsAndroidBridge::RemoteDevice
> device_
;
300 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser_
;
301 scoped_refptr
<DevToolsAndroidBridge::AndroidWebSocket
> web_socket_
;
304 ForwardingMap forwarding_map_
;
305 CommandCallbackMap pending_responses_
;
306 PortStatusMap port_status_
;
308 DISALLOW_COPY_AND_ASSIGN(Connection
);
311 PortForwardingController::Connection::Connection(
313 scoped_refptr
<DevToolsAndroidBridge::RemoteDevice
> device
,
314 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
,
315 const ForwardingMap
& forwarding_map
)
316 : registry_(registry
),
321 forwarding_map_(forwarding_map
) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
323 (*registry_
)[device_
->serial()] = this;
324 web_socket_
= browser
->CreateWebSocket(kDevToolsRemoteBrowserTarget
, this);
325 AddRef(); // Balanced in OnSocketClosed();
328 void PortForwardingController::Connection::Shutdown() {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
331 // This will have no effect if the socket is not connected yet.
332 web_socket_
->Disconnect();
335 PortForwardingController::Connection::~Connection() {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
338 DCHECK(registry_
->find(device_
->serial()) != registry_
->end());
339 registry_
->erase(device_
->serial());
343 void PortForwardingController::Connection::UpdateForwardingMap(
344 const ForwardingMap
& new_forwarding_map
) {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
347 SerializeChanges(kTetheringUnbind
, new_forwarding_map
, forwarding_map_
);
348 SerializeChanges(kTetheringBind
, forwarding_map_
, new_forwarding_map
);
350 forwarding_map_
= new_forwarding_map
;
353 void PortForwardingController::Connection::SerializeChanges(
354 const std::string
& method
,
355 const ForwardingMap
& old_map
,
356 const ForwardingMap
& new_map
) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
358 for (ForwardingMap::const_iterator
new_it(new_map
.begin());
359 new_it
!= new_map
.end(); ++new_it
) {
360 int port
= new_it
->first
;
361 const std::string
& location
= new_it
->second
;
362 ForwardingMap::const_iterator old_it
= old_map
.find(port
);
363 if (old_it
!= old_map
.end() && old_it
->second
== location
)
364 continue; // The port points to the same location in both configs, skip.
366 SendCommand(method
, port
);
370 void PortForwardingController::Connection::SendCommand(
371 const std::string
& method
, int port
) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
373 base::DictionaryValue params
;
374 params
.SetInteger(kPortAttribute
, port
);
375 DevToolsProtocol::Command
command(++command_id_
, method
, ¶ms
);
377 if (method
== kTetheringBind
) {
378 pending_responses_
[command
.id()] =
379 base::Bind(&Connection::ProcessBindResponse
,
380 base::Unretained(this), port
);
381 #if defined(DEBUG_DEVTOOLS)
382 port_status_
[port
] = kStatusConnecting
;
383 #endif // defined(DEBUG_DEVTOOLS)
385 DCHECK_EQ(kTetheringUnbind
, method
);
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
);
437 void PortForwardingController::Connection::UpdateSocketCountOnHandlerThread(
438 int port
, int increment
) {
439 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
440 base::Bind(&Connection::UpdateSocketCount
, this, port
, increment
));
443 void PortForwardingController::Connection::UpdateSocketCount(
444 int port
, int increment
) {
445 #if defined(DEBUG_DEVTOOLS)
446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
447 PortStatusMap::iterator it
= port_status_
.find(port
);
448 if (it
== port_status_
.end())
450 if (it
->second
< 0 || (it
->second
== 0 && increment
< 0))
452 it
->second
+= increment
;
453 #endif // defined(DEBUG_DEVTOOLS)
456 const PortForwardingController::PortStatusMap
&
457 PortForwardingController::Connection::GetPortStatusMap() {
458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
462 void PortForwardingController::Connection::OnSocketOpened() {
463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
465 // Socket was created after Shutdown was called. Disconnect immediately.
466 web_socket_
->Disconnect();
470 SerializeChanges(kTetheringBind
, ForwardingMap(), forwarding_map_
);
473 void PortForwardingController::Connection::OnSocketClosed(
474 bool closed_by_device
) {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
476 Release(); // Balanced in the constructor.
479 void PortForwardingController::Connection::OnFrameRead(
480 const std::string
& message
) {
481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
482 if (ProcessResponse(message
))
485 scoped_ptr
<DevToolsProtocol::Notification
> notification(
486 DevToolsProtocol::ParseNotification(message
));
490 if (notification
->method() != kTetheringAccepted
)
493 base::DictionaryValue
* params
= notification
->params();
498 std::string connection_id
;
499 if (!params
->GetInteger(kPortAttribute
, &port
) ||
500 !params
->GetString(kConnectionIdAttribute
, &connection_id
))
503 std::map
<int, std::string
>::iterator it
= forwarding_map_
.find(port
);
504 if (it
== forwarding_map_
.end())
507 std::string location
= it
->second
;
508 std::vector
<std::string
> tokens
;
509 Tokenize(location
, ":", &tokens
);
510 int destination_port
= 0;
511 if (tokens
.size() != 2 || !base::StringToInt(tokens
[1], &destination_port
))
513 std::string destination_host
= tokens
[0];
515 SocketTunnel::CounterCallback callback
=
516 base::Bind(&Connection::UpdateSocketCountOnHandlerThread
, this, port
);
519 connection_id
.c_str(),
520 base::Bind(&SocketTunnel::StartTunnel
,
526 PortForwardingController::PortForwardingController(Profile
* profile
)
528 pref_service_(profile
->GetPrefs()),
530 pref_change_registrar_
.Init(pref_service_
);
531 base::Closure callback
= base::Bind(
532 &PortForwardingController::OnPrefsChange
, base::Unretained(this));
533 pref_change_registrar_
.Add(prefs::kDevToolsPortForwardingEnabled
, callback
);
534 pref_change_registrar_
.Add(prefs::kDevToolsPortForwardingConfig
, callback
);
539 PortForwardingController::~PortForwardingController() {}
541 void PortForwardingController::Shutdown() {
542 // Existing connection will not be shut down. This might be confusing for
543 // some users, but the opposite is more confusing.
547 void PortForwardingController::AddListener(Listener
* listener
) {
548 listeners_
.push_back(listener
);
551 void PortForwardingController::RemoveListener(Listener
* listener
) {
552 Listeners::iterator it
=
553 std::find(listeners_
.begin(), listeners_
.end(), listener
);
554 DCHECK(it
!= listeners_
.end());
555 listeners_
.erase(it
);
558 void PortForwardingController::DeviceListChanged(
559 const DevToolsAndroidBridge::RemoteDevices
& devices
) {
560 DevicesStatus status
;
562 for (DevToolsAndroidBridge::RemoteDevices::const_iterator it
=
563 devices
.begin(); it
!= devices
.end(); ++it
) {
564 scoped_refptr
<DevToolsAndroidBridge::RemoteDevice
> device
= *it
;
565 if (!device
->is_connected())
567 Registry::iterator rit
= registry_
.find(device
->serial());
568 if (rit
== registry_
.end()) {
569 scoped_refptr
<DevToolsAndroidBridge::RemoteBrowser
> browser
=
570 FindBestBrowserForTethering(device
->browsers());
572 new Connection(®istry_
, device
, browser
, forwarding_map_
);
575 status
[device
->serial()] = (*rit
).second
->GetPortStatusMap();
579 NotifyListeners(status
);
582 void PortForwardingController::OnPrefsChange() {
583 forwarding_map_
.clear();
585 if (pref_service_
->GetBoolean(prefs::kDevToolsPortForwardingEnabled
)) {
586 const base::DictionaryValue
* dict
=
587 pref_service_
->GetDictionary(prefs::kDevToolsPortForwardingConfig
);
588 for (base::DictionaryValue::Iterator
it(*dict
);
589 !it
.IsAtEnd(); it
.Advance()) {
591 std::string location
;
592 if (base::StringToInt(it
.key(), &port_num
) &&
593 dict
->GetString(it
.key(), &location
))
594 forwarding_map_
[port_num
] = location
;
598 if (!forwarding_map_
.empty()) {
603 ShutdownConnections();
604 NotifyListeners(DevicesStatus());
608 void PortForwardingController::StartListening() {
612 DevToolsAndroidBridge
* android_bridge
=
613 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
615 android_bridge
->AddDeviceListListener(this);
619 void PortForwardingController::StopListening() {
623 DevToolsAndroidBridge
* android_bridge
=
624 DevToolsAndroidBridge::Factory::GetForProfile(profile_
);
626 android_bridge
->RemoveDeviceListListener(this);
629 void PortForwardingController::UpdateConnections() {
630 for (Registry::iterator it
= registry_
.begin(); it
!= registry_
.end(); ++it
)
631 it
->second
->UpdateForwardingMap(forwarding_map_
);
634 void PortForwardingController::ShutdownConnections() {
635 for (Registry::iterator it
= registry_
.begin(); it
!= registry_
.end(); ++it
)
636 it
->second
->Shutdown();
640 void PortForwardingController::NotifyListeners(
641 const DevicesStatus
& status
) const {
642 Listeners
copy(listeners_
); // Iterate over copy.
643 for (Listeners::const_iterator it
= copy
.begin(); it
!= copy
.end(); ++it
)
644 (*it
)->PortStatusChanged(status
);
648 PortForwardingController::Factory
*
649 PortForwardingController::Factory::GetInstance() {
650 return Singleton
<PortForwardingController::Factory
>::get();
654 PortForwardingController
* PortForwardingController::Factory::GetForProfile(
656 return static_cast<PortForwardingController
*>(GetInstance()->
657 GetServiceForBrowserContext(profile
, true));
660 PortForwardingController::Factory::Factory()
661 : BrowserContextKeyedServiceFactory(
662 "PortForwardingController",
663 BrowserContextDependencyManager::GetInstance()) {}
665 PortForwardingController::Factory::~Factory() {}
667 KeyedService
* PortForwardingController::Factory::BuildServiceInstanceFor(
668 content::BrowserContext
* context
) const {
669 Profile
* profile
= Profile::FromBrowserContext(context
);
670 return new PortForwardingController(profile
);