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/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
;
36 const int kBufferSize
= 16 * 1024;
40 kStatusDisconnecting
= -2,
41 kStatusConnecting
= -1,
45 namespace tethering
= ::chrome::devtools::Tethering
;
47 static const char kDevToolsRemoteBrowserTarget
[] = "/devtools/browser";
49 class SocketTunnel
: public base::NonThreadSafe
{
51 static void StartTunnel(const std::string
& host
,
54 scoped_ptr
<net::StreamSocket
> socket
) {
55 if (result
== net::OK
)
56 new SocketTunnel(socket
.Pass(), host
, port
);
60 SocketTunnel(scoped_ptr
<net::StreamSocket
> socket
,
61 const std::string
& host
,
63 : remote_socket_(socket
.Pass()),
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(
70 net::DEFAULT_PRIORITY
,
72 base::Bind(&SocketTunnel::OnResolved
, base::Unretained(this)),
75 if (result
!= net::ERR_IO_PENDING
)
79 void OnResolved(int result
) {
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
)
93 void OnConnected(int result
) {
99 ++pending_writes_
; // avoid SelfDestruct in first Pump
100 Pump(host_socket_
.get(), remote_socket_
.get());
102 if (pending_destruction_
) {
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(
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
,
130 scoped_refptr
<net::DrainableIOBuffer
> drainable
=
131 new net::DrainableIOBuffer(buffer
.get(), total
);
134 result
= to
->Write(drainable
.get(),
136 base::Bind(&SocketTunnel::OnWritten
,
137 base::Unretained(this),
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
,
155 drainable
->DidConsume(result
);
156 if (drainable
->BytesRemaining() > 0) {
158 result
= to
->Write(drainable
.get(),
159 drainable
->BytesRemaining(),
160 base::Bind(&SocketTunnel::OnWritten
,
161 base::Unretained(this),
165 if (result
!= net::ERR_IO_PENDING
)
166 OnWritten(drainable
, from
, to
, result
);
170 if (pending_destruction_
) {
177 void SelfDestruct() {
178 if (pending_writes_
> 0) {
179 pending_destruction_
= true;
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_
;
190 bool pending_destruction_
;
195 class PortForwardingController::Connection
196 : public AndroidDeviceManager::AndroidWebSocket::Delegate
{
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() {
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_
;
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
),
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());
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
);
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
);
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)
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
);
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
) {
345 if (!DevToolsProtocol::ParseResponse(message
, &id
, &error_code
))
348 CommandCallbackMap::iterator it
= pending_responses_
.find(id
);
349 if (it
== pending_responses_
.end())
352 it
->second
.Run(error_code
? kStatusError
: kStatusOK
);
353 pending_responses_
.erase(it
);
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())
367 if (status
== kStatusError
)
370 port_status_
.erase(it
);
373 const PortForwardingController::PortStatusMap
&
374 PortForwardingController::Connection::GetPortStatusMap() {
375 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
379 void PortForwardingController::Connection::OnSocketOpened() {
380 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
382 SerializeChanges(tethering::bind::kName
, ForwardingMap(), forwarding_map_
);
385 void PortForwardingController::Connection::OnSocketClosed() {
389 void PortForwardingController::Connection::OnFrameRead(
390 const std::string
& message
) {
391 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
392 if (ProcessResponse(message
))
396 scoped_ptr
<base::DictionaryValue
> params
;
397 if (!DevToolsProtocol::ParseNotification(message
, &method
, ¶ms
))
400 if (method
!= tethering::accepted::kName
|| !params
)
404 std::string connection_id
;
405 if (!params
->GetInteger(tethering::accepted::kParamPort
, &port
) ||
406 !params
->GetString(tethering::accepted::kParamConnectionId
,
410 std::map
<int, std::string
>::iterator it
= forwarding_map_
.find(port
);
411 if (it
== forwarding_map_
.end())
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
))
420 std::string destination_host
= tokens
[0];
422 scoped_refptr
<AndroidDeviceManager::Device
> device(
423 controller_
->bridge_
->FindDevice(browser_
->serial()));
424 DCHECK(device
.get());
426 connection_id
.c_str(),
427 base::Bind(&SocketTunnel::StartTunnel
,
432 PortForwardingController::PortForwardingController(
434 DevToolsAndroidBridge
* 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
);
445 PortForwardingController::~PortForwardingController() {}
447 PortForwardingController::ForwardingStatus
448 PortForwardingController::DeviceListChanged(
449 const DevToolsAndroidBridge::RemoteDevices
& devices
) {
450 ForwardingStatus status
;
451 if (forwarding_map_
.empty())
454 for (const auto& device
: devices
) {
455 if (!device
->is_connected())
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_
);
462 status
.push_back(std::make_pair(rit
->second
->browser(),
463 rit
->second
->GetPortStatusMap()));
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()) {
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()) {
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(®istry_copy
);
497 void PortForwardingController::UpdateConnections() {
498 for (Registry::iterator it
= registry_
.begin(); it
!= registry_
.end(); ++it
)
499 it
->second
->UpdateForwardingMap(forwarding_map_
);