1 // Copyright 2013 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/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_change_registrar.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "chrome/browser/devtools/adb_client_socket.h"
20 #include "chrome/browser/devtools/adb_web_socket.h"
21 #include "chrome/browser/devtools/devtools_protocol.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/pref_names.h"
24 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "net/base/address_list.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 static const char kPortAttribute
[] = "port";
47 static const char kConnectionIdAttribute
[] = "connectionId";
48 static const char kTetheringAccepted
[] = "Tethering.accepted";
49 static const char kTetheringBind
[] = "Tethering.bind";
50 static const char kTetheringUnbind
[] = "Tethering.unbind";
52 static const char kDevToolsRemoteBrowserTarget
[] = "/devtools/browser";
53 const int kMinVersionPortForwarding
= 28;
57 typedef base::Callback
<void(int)> CounterCallback
;
59 SocketTunnel(const std::string
& location
, const CounterCallback
& callback
)
60 : location_(location
),
62 pending_destruction_(false),
64 about_to_destroy_(false) {
68 void Start(int result
, net::StreamSocket
* socket
) {
73 remote_socket_
.reset(socket
);
75 std::vector
<std::string
> tokens
;
76 Tokenize(location_
, ":", &tokens
);
78 if (tokens
.size() != 2 || !base::StringToInt(tokens
[1], &port
)) {
83 host_resolver_
= net::HostResolver::CreateDefaultResolver(NULL
);
84 net::HostResolver::RequestInfo
request_info(
85 net::HostPortPair(tokens
[0], port
));
86 result
= host_resolver_
->Resolve(
88 net::DEFAULT_PRIORITY
,
90 base::Bind(&SocketTunnel::OnResolved
, base::Unretained(this)),
93 if (result
!= net::ERR_IO_PENDING
)
98 void OnResolved(int result
) {
104 host_socket_
.reset(new net::TCPClientSocket(address_list_
, NULL
,
105 net::NetLog::Source()));
106 result
= host_socket_
->Connect(base::Bind(&SocketTunnel::OnConnected
,
107 base::Unretained(this)));
108 if (result
!= net::ERR_IO_PENDING
)
113 about_to_destroy_
= true;
115 host_socket_
->Disconnect();
117 remote_socket_
->Disconnect();
121 void OnConnected(int result
) {
127 Pump(host_socket_
.get(), remote_socket_
.get());
128 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 std::string location_
;
213 scoped_ptr
<net::StreamSocket
> remote_socket_
;
214 scoped_ptr
<net::StreamSocket
> host_socket_
;
215 scoped_ptr
<net::HostResolver
> host_resolver_
;
216 net::AddressList address_list_
;
218 bool pending_destruction_
;
219 CounterCallback callback_
;
220 bool about_to_destroy_
;
223 typedef DevToolsAdbBridge::RemoteBrowser::ParsedVersion ParsedVersion
;
225 static bool IsVersionLower(const ParsedVersion
& left
,
226 const ParsedVersion
& right
) {
227 return std::lexicographical_compare(
228 left
.begin(), left
.end(), right
.begin(), right
.end());
231 static bool IsPortForwardingSupported(const ParsedVersion
& version
) {
232 return !version
.empty() && version
[0] >= kMinVersionPortForwarding
;
235 static std::string
FindBestSocketForTethering(
236 const DevToolsAdbBridge::RemoteBrowsers browsers
) {
238 ParsedVersion newest_version
;
239 for (DevToolsAdbBridge::RemoteBrowsers::const_iterator it
= browsers
.begin();
240 it
!= browsers
.end(); ++it
) {
241 scoped_refptr
<DevToolsAdbBridge::RemoteBrowser
> browser
= *it
;
242 ParsedVersion current_version
= browser
->GetParsedVersion();
243 if (browser
->IsChrome() &&
244 IsPortForwardingSupported(current_version
) &&
245 IsVersionLower(newest_version
, current_version
)) {
246 socket
= browser
->socket();
247 newest_version
= current_version
;
255 class PortForwardingController::Connection
256 : public AdbWebSocket::Delegate
,
257 public base::RefCountedThreadSafe
<
259 content::BrowserThread::DeleteOnUIThread
> {
261 Connection(Registry
* registry
,
262 scoped_refptr
<AndroidDevice
> device
,
263 const std::string
& socket
,
264 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
265 PrefService
* pref_service
);
267 const PortStatusMap
& GetPortStatusMap();
272 friend struct content::BrowserThread::DeleteOnThread
<
273 content::BrowserThread::UI
>;
274 friend class base::DeleteHelper
<Connection
>;
276 virtual ~Connection();
278 typedef std::map
<int, std::string
> ForwardingMap
;
280 typedef base::Callback
<void(PortStatus
)> CommandCallback
;
281 typedef std::map
<int, CommandCallback
> CommandCallbackMap
;
283 void OnPrefsChange();
285 void ChangeForwardingMap(ForwardingMap map
);
287 void SerializeChanges(const std::string
& method
,
288 const ForwardingMap
& old_map
,
289 const ForwardingMap
& new_map
);
291 void SendCommand(const std::string
& method
, int port
);
292 bool ProcessResponse(const std::string
& json
);
294 void ProcessBindResponse(int port
, PortStatus status
);
295 void ProcessUnbindResponse(int port
, PortStatus status
);
296 void UpdateSocketCount(int port
, int increment
);
297 void UpdatePortStatusMap();
298 void UpdatePortStatusMapOnUIThread(const PortStatusMap
& status_map
);
300 // AdbWebSocket::Delegate implementation:
301 virtual void OnSocketOpened() OVERRIDE
;
302 virtual void OnFrameRead(const std::string
& message
) OVERRIDE
;
303 virtual void OnSocketClosed(bool closed_by_device
) OVERRIDE
;
304 virtual bool ProcessIncomingMessage(const std::string
& message
) OVERRIDE
;
306 PortForwardingController::Registry
* registry_
;
307 scoped_refptr
<AndroidDevice
> device_
;
308 scoped_refptr
<RefCountedAdbThread
> adb_thread_
;
309 PrefChangeRegistrar pref_change_registrar_
;
310 scoped_refptr
<AdbWebSocket
> web_socket_
;
312 ForwardingMap forwarding_map_
;
313 CommandCallbackMap pending_responses_
;
314 PortStatusMap port_status_
;
315 PortStatusMap port_status_on_ui_thread_
;
317 DISALLOW_COPY_AND_ASSIGN(Connection
);
320 PortForwardingController::Connection::Connection(
322 scoped_refptr
<AndroidDevice
> device
,
323 const std::string
& socket
,
324 scoped_refptr
<RefCountedAdbThread
> adb_thread
,
325 PrefService
* pref_service
)
326 : registry_(registry
),
328 adb_thread_(adb_thread
),
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
331 pref_change_registrar_
.Init(pref_service
);
332 (*registry_
)[device_
->serial()] = this;
333 web_socket_
= new AdbWebSocket(
334 device
, socket
, kDevToolsRemoteBrowserTarget
,
335 adb_thread_
->message_loop(), this);
336 AddRef(); // Balanced in OnSocketClosed();
339 void PortForwardingController::Connection::Shutdown() {
341 // This will have no effect if the socket is not connected yet.
342 web_socket_
->Disconnect();
345 PortForwardingController::Connection::~Connection() {
347 DCHECK(registry_
->find(device_
->serial()) != registry_
->end());
348 registry_
->erase(device_
->serial());
352 void PortForwardingController::Connection::OnPrefsChange() {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
355 ForwardingMap new_forwarding_map
;
357 PrefService
* pref_service
= pref_change_registrar_
.prefs();
359 pref_service
->GetBoolean(prefs::kDevToolsPortForwardingEnabled
);
361 const base::DictionaryValue
* dict
=
362 pref_service
->GetDictionary(prefs::kDevToolsPortForwardingConfig
);
363 for (base::DictionaryValue::Iterator
it(*dict
);
364 !it
.IsAtEnd(); it
.Advance()) {
366 std::string location
;
367 if (base::StringToInt(it
.key(), &port_num
) &&
368 dict
->GetString(it
.key(), &location
))
369 new_forwarding_map
[port_num
] = location
;
373 adb_thread_
->message_loop()->PostTask(
375 base::Bind(&Connection::ChangeForwardingMap
,
376 this, new_forwarding_map
));
379 void PortForwardingController::Connection::ChangeForwardingMap(
380 ForwardingMap new_forwarding_map
) {
381 DCHECK_EQ(base::MessageLoop::current(), adb_thread_
->message_loop());
383 SerializeChanges(kTetheringUnbind
, new_forwarding_map
, forwarding_map_
);
384 SerializeChanges(kTetheringBind
, forwarding_map_
, new_forwarding_map
);
385 forwarding_map_
= new_forwarding_map
;
388 void PortForwardingController::Connection::SerializeChanges(
389 const std::string
& method
,
390 const ForwardingMap
& old_map
,
391 const ForwardingMap
& new_map
) {
392 for (ForwardingMap::const_iterator
new_it(new_map
.begin());
393 new_it
!= new_map
.end(); ++new_it
) {
394 int port
= new_it
->first
;
395 const std::string
& location
= new_it
->second
;
396 ForwardingMap::const_iterator old_it
= old_map
.find(port
);
397 if (old_it
!= old_map
.end() && old_it
->second
== location
)
398 continue; // The port points to the same location in both configs, skip.
400 SendCommand(method
, port
);
404 void PortForwardingController::Connection::SendCommand(
405 const std::string
& method
, int port
) {
406 base::DictionaryValue params
;
407 params
.SetInteger(kPortAttribute
, port
);
408 DevToolsProtocol::Command
command(++command_id_
, method
, ¶ms
);
410 if (method
== kTetheringBind
) {
411 pending_responses_
[command
.id()] =
412 base::Bind(&Connection::ProcessBindResponse
,
413 base::Unretained(this), port
);
414 #if defined(DEBUG_DEVTOOLS)
415 port_status_
[port
] = kStatusConnecting
;
416 UpdatePortStatusMap();
417 #endif // defined(DEBUG_DEVTOOLS)
419 DCHECK_EQ(kTetheringUnbind
, method
);
421 PortStatusMap::iterator it
= port_status_
.find(port
);
422 if (it
!= port_status_
.end() && it
->second
== kStatusError
) {
423 // The bind command failed on this port, do not attempt unbind.
424 port_status_
.erase(it
);
425 UpdatePortStatusMap();
429 pending_responses_
[command
.id()] =
430 base::Bind(&Connection::ProcessUnbindResponse
,
431 base::Unretained(this), port
);
432 #if defined(DEBUG_DEVTOOLS)
433 port_status_
[port
] = kStatusDisconnecting
;
434 UpdatePortStatusMap();
435 #endif // defined(DEBUG_DEVTOOLS)
438 web_socket_
->SendFrameOnHandlerThread(command
.Serialize());
441 bool PortForwardingController::Connection::ProcessResponse(
442 const std::string
& message
) {
443 scoped_ptr
<DevToolsProtocol::Response
> response(
444 DevToolsProtocol::ParseResponse(message
));
448 CommandCallbackMap::iterator it
= pending_responses_
.find(response
->id());
449 if (it
== pending_responses_
.end())
452 it
->second
.Run(response
->error_code() ? kStatusError
: kStatusOK
);
453 pending_responses_
.erase(it
);
457 void PortForwardingController::Connection::ProcessBindResponse(
458 int port
, PortStatus status
) {
459 port_status_
[port
] = status
;
460 UpdatePortStatusMap();
463 void PortForwardingController::Connection::ProcessUnbindResponse(
464 int port
, PortStatus status
) {
465 PortStatusMap::iterator it
= port_status_
.find(port
);
466 if (it
== port_status_
.end())
468 if (status
== kStatusError
)
471 port_status_
.erase(it
);
472 UpdatePortStatusMap();
475 void PortForwardingController::Connection::UpdateSocketCount(
476 int port
, int increment
) {
477 #if defined(DEBUG_DEVTOOLS)
478 PortStatusMap::iterator it
= port_status_
.find(port
);
479 if (it
== port_status_
.end())
481 if (it
->second
< 0 || (it
->second
== 0 && increment
< 0))
483 it
->second
+= increment
;
484 UpdatePortStatusMap();
485 #endif // defined(DEBUG_DEVTOOLS)
488 void PortForwardingController::Connection::UpdatePortStatusMap() {
489 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
490 base::Bind(&Connection::UpdatePortStatusMapOnUIThread
,
491 this, port_status_
));
494 void PortForwardingController::Connection::UpdatePortStatusMapOnUIThread(
495 const PortStatusMap
& status_map
) {
496 port_status_on_ui_thread_
= status_map
;
499 const PortForwardingController::PortStatusMap
&
500 PortForwardingController::Connection::GetPortStatusMap() {
501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
502 return port_status_on_ui_thread_
;
505 void PortForwardingController::Connection::OnSocketOpened() {
506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
508 // Socket was created after Shutdown was called. Disconnect immediately.
509 web_socket_
->Disconnect();
513 base::Closure pref_callback
= base::Bind(
514 &Connection::OnPrefsChange
, base::Unretained(this));
515 pref_change_registrar_
.Add(
516 prefs::kDevToolsPortForwardingEnabled
, pref_callback
);
517 pref_change_registrar_
.Add(
518 prefs::kDevToolsPortForwardingConfig
, pref_callback
);
521 void PortForwardingController::Connection::OnFrameRead(
522 const std::string
& message
) {
525 void PortForwardingController::Connection::OnSocketClosed(
526 bool closed_by_device
) {
527 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
528 Release(); // Balanced in the constructor.
531 bool PortForwardingController::Connection::ProcessIncomingMessage(
532 const std::string
& message
) {
533 DCHECK_EQ(base::MessageLoop::current(), adb_thread_
->message_loop());
534 if (ProcessResponse(message
))
537 scoped_ptr
<DevToolsProtocol::Notification
> notification(
538 DevToolsProtocol::ParseNotification(message
));
542 if (notification
->method() != kTetheringAccepted
)
545 base::DictionaryValue
* params
= notification
->params();
550 std::string connection_id
;
551 if (!params
->GetInteger(kPortAttribute
, &port
) ||
552 !params
->GetString(kConnectionIdAttribute
, &connection_id
))
555 std::map
<int, std::string
>::iterator it
= forwarding_map_
.find(port
);
556 if (it
== forwarding_map_
.end())
559 std::string location
= it
->second
;
561 SocketTunnel
* tunnel
= new SocketTunnel(location
,
562 base::Bind(&Connection::UpdateSocketCount
, this, port
));
564 device_
->OpenSocket(connection_id
.c_str(),
565 base::Bind(&SocketTunnel::Start
, base::Unretained(tunnel
)));
569 PortForwardingController::PortForwardingController(PrefService
* pref_service
)
570 : adb_thread_(RefCountedAdbThread::GetInstance()),
571 pref_service_(pref_service
) {
574 PortForwardingController::~PortForwardingController() {
575 for (Registry::iterator it
= registry_
.begin(); it
!= registry_
.end(); ++it
)
576 it
->second
->Shutdown();
579 PortForwardingController::DevicesStatus
580 PortForwardingController::UpdateDeviceList(
581 const DevToolsAdbBridge::RemoteDevices
& devices
) {
582 DevicesStatus status
;
583 for (DevToolsAdbBridge::RemoteDevices::const_iterator it
= devices
.begin();
584 it
!= devices
.end(); ++it
) {
585 scoped_refptr
<DevToolsAdbBridge::RemoteDevice
> device
= *it
;
586 if (!device
->IsConnected())
588 Registry::iterator rit
= registry_
.find(device
->GetSerial());
589 if (rit
== registry_
.end()) {
590 std::string socket
= FindBestSocketForTethering(device
->browsers());
591 if (!socket
.empty()) {
593 ®istry_
, device
->device(), socket
, adb_thread_
, pref_service_
);
596 status
[device
->GetSerial()] = (*rit
).second
->GetPortStatusMap();
603 PortForwardingController::Factory
*
604 PortForwardingController::Factory::GetInstance() {
605 return Singleton
<PortForwardingController::Factory
>::get();
609 PortForwardingController
* PortForwardingController::Factory::GetForProfile(
611 return static_cast<PortForwardingController
*>(GetInstance()->
612 GetServiceForBrowserContext(profile
, true));
615 PortForwardingController::Factory::Factory()
616 : BrowserContextKeyedServiceFactory(
617 "PortForwardingController",
618 BrowserContextDependencyManager::GetInstance()) {}
620 PortForwardingController::Factory::~Factory() {}
622 BrowserContextKeyedService
*
623 PortForwardingController::Factory::BuildServiceInstanceFor(
624 content::BrowserContext
* context
) const {
625 Profile
* profile
= Profile::FromBrowserContext(context
);
626 return new PortForwardingController(profile
->GetPrefs());