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 ++pending_writes_; // avoid SelfDestruct in first Pump
124 Pump(host_socket_.get(), remote_socket_.get());
126 if (pending_destruction_) {
129 Pump(remote_socket_.get(), host_socket_.get());
133 void Pump(net::StreamSocket* from, net::StreamSocket* to) {
134 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
135 int result = from->Read(
139 &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
140 if (result != net::ERR_IO_PENDING)
141 OnRead(from, to, buffer, result);
144 void OnRead(net::StreamSocket* from,
145 net::StreamSocket* to,
146 scoped_refptr<net::IOBuffer> buffer,
154 scoped_refptr<net::DrainableIOBuffer> drainable =
155 new net::DrainableIOBuffer(buffer.get(), total);
158 result = to->Write(drainable.get(),
160 base::Bind(&SocketTunnel::OnWritten,
161 base::Unretained(this),
165 if (result != net::ERR_IO_PENDING)
166 OnWritten(drainable, from, to, result);
169 void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
170 net::StreamSocket* from,
171 net::StreamSocket* to,
179 drainable->DidConsume(result);
180 if (drainable->BytesRemaining() > 0) {
182 result = to->Write(drainable.get(),
183 drainable->BytesRemaining(),
184 base::Bind(&SocketTunnel::OnWritten,
185 base::Unretained(this),
189 if (result != net::ERR_IO_PENDING)
190 OnWritten(drainable, from, to, result);
194 if (pending_destruction_) {
201 void SelfDestruct() {
202 // In case one of the connections closes, we could get here
203 // from another one due to Disconnect firing back on all
205 if (about_to_destroy_)
207 if (pending_writes_ > 0) {
208 pending_destruction_ = true;
214 scoped_ptr<net::StreamSocket> remote_socket_;
215 scoped_ptr<net::StreamSocket> host_socket_;
216 scoped_ptr<net::HostResolver> host_resolver_;
217 net::AddressList address_list_;
219 bool pending_destruction_;
220 CounterCallback callback_;
221 bool about_to_destroy_;
224 typedef DevToolsAndroidBridge::RemoteBrowser::ParsedVersion ParsedVersion;
226 static bool IsVersionLower(const ParsedVersion& left,
227 const ParsedVersion& right) {
228 return std::lexicographical_compare(
229 left.begin(), left.end(), right.begin(), right.end());
232 static bool IsPortForwardingSupported(const ParsedVersion& version) {
233 return !version.empty() && version[0] >= kMinVersionPortForwarding;
236 static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser>
237 FindBestBrowserForTethering(
238 const DevToolsAndroidBridge::RemoteBrowsers browsers) {
239 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> best_browser;
240 ParsedVersion newest_version;
241 for (DevToolsAndroidBridge::RemoteBrowsers::const_iterator it =
242 browsers.begin(); it != browsers.end(); ++it) {
243 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser = *it;
244 ParsedVersion current_version = browser->GetParsedVersion();
245 if (IsPortForwardingSupported(current_version) &&
246 IsVersionLower(newest_version, current_version)) {
247 best_browser = browser;
248 newest_version = current_version;
256 class PortForwardingController::Connection
257 : public DevToolsAndroidBridge::AndroidWebSocket::Delegate,
258 public base::RefCountedThreadSafe<
260 content::BrowserThread::DeleteOnUIThread> {
262 Connection(Registry* registry,
263 scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
264 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
265 const ForwardingMap& forwarding_map);
267 const PortStatusMap& GetPortStatusMap();
269 void UpdateForwardingMap(const ForwardingMap& new_forwarding_map);
274 friend struct content::BrowserThread::DeleteOnThread<
275 content::BrowserThread::UI>;
276 friend class base::DeleteHelper<Connection>;
278 virtual ~Connection();
280 typedef std::map<int, std::string> ForwardingMap;
282 typedef base::Callback<void(PortStatus)> CommandCallback;
283 typedef std::map<int, CommandCallback> CommandCallbackMap;
285 void SerializeChanges(const std::string& method,
286 const ForwardingMap& old_map,
287 const ForwardingMap& new_map);
289 void SendCommand(const std::string& method, int port);
290 bool ProcessResponse(const std::string& json);
292 void ProcessBindResponse(int port, PortStatus status);
293 void ProcessUnbindResponse(int port, PortStatus status);
295 void UpdateSocketCountOnHandlerThread(int port, int increment);
296 void UpdateSocketCount(int port, int increment);
298 // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
299 virtual void OnSocketOpened() OVERRIDE;
300 virtual void OnFrameRead(const std::string& message) OVERRIDE;
301 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
303 PortForwardingController::Registry* registry_;
304 scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device_;
305 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
306 scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
309 ForwardingMap forwarding_map_;
310 CommandCallbackMap pending_responses_;
311 PortStatusMap port_status_;
313 DISALLOW_COPY_AND_ASSIGN(Connection);
316 PortForwardingController::Connection::Connection(
318 scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
319 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
320 const ForwardingMap& forwarding_map)
321 : registry_(registry),
326 forwarding_map_(forwarding_map) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328 (*registry_)[device_->serial()] = this;
329 web_socket_ = browser->CreateWebSocket(kDevToolsRemoteBrowserTarget, this);
330 web_socket_->Connect();
331 AddRef(); // Balanced in OnSocketClosed();
334 void PortForwardingController::Connection::Shutdown() {
335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 // This will have no effect if the socket is not connected yet.
338 web_socket_->Disconnect();
341 PortForwardingController::Connection::~Connection() {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
344 DCHECK(registry_->find(device_->serial()) != registry_->end());
345 registry_->erase(device_->serial());
349 void PortForwardingController::Connection::UpdateForwardingMap(
350 const ForwardingMap& new_forwarding_map) {
351 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353 SerializeChanges(kTetheringUnbind, new_forwarding_map, forwarding_map_);
354 SerializeChanges(kTetheringBind, forwarding_map_, new_forwarding_map);
356 forwarding_map_ = new_forwarding_map;
359 void PortForwardingController::Connection::SerializeChanges(
360 const std::string& method,
361 const ForwardingMap& old_map,
362 const ForwardingMap& new_map) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364 for (ForwardingMap::const_iterator new_it(new_map.begin());
365 new_it != new_map.end(); ++new_it) {
366 int port = new_it->first;
367 const std::string& location = new_it->second;
368 ForwardingMap::const_iterator old_it = old_map.find(port);
369 if (old_it != old_map.end() && old_it->second == location)
370 continue; // The port points to the same location in both configs, skip.
372 SendCommand(method, port);
376 void PortForwardingController::Connection::SendCommand(
377 const std::string& method, int port) {
378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
379 base::DictionaryValue params;
380 params.SetInteger(kPortAttribute, port);
381 DevToolsProtocol::Command command(++command_id_, method, ¶ms);
383 if (method == kTetheringBind) {
384 pending_responses_[command.id()] =
385 base::Bind(&Connection::ProcessBindResponse,
386 base::Unretained(this), port);
387 #if defined(DEBUG_DEVTOOLS)
388 port_status_[port] = kStatusConnecting;
389 #endif // defined(DEBUG_DEVTOOLS)
391 DCHECK_EQ(kTetheringUnbind, method);
393 PortStatusMap::iterator it = port_status_.find(port);
394 if (it != port_status_.end() && it->second == kStatusError) {
395 // The bind command failed on this port, do not attempt unbind.
396 port_status_.erase(it);
400 pending_responses_[command.id()] =
401 base::Bind(&Connection::ProcessUnbindResponse,
402 base::Unretained(this), port);
403 #if defined(DEBUG_DEVTOOLS)
404 port_status_[port] = kStatusDisconnecting;
405 #endif // defined(DEBUG_DEVTOOLS)
408 web_socket_->SendFrame(command.Serialize());
411 bool PortForwardingController::Connection::ProcessResponse(
412 const std::string& message) {
413 scoped_ptr<DevToolsProtocol::Response> response(
414 DevToolsProtocol::ParseResponse(message));
418 CommandCallbackMap::iterator it = pending_responses_.find(response->id());
419 if (it == pending_responses_.end())
422 it->second.Run(response->error_code() ? kStatusError : kStatusOK);
423 pending_responses_.erase(it);
427 void PortForwardingController::Connection::ProcessBindResponse(
428 int port, PortStatus status) {
429 port_status_[port] = status;
432 void PortForwardingController::Connection::ProcessUnbindResponse(
433 int port, PortStatus status) {
434 PortStatusMap::iterator it = port_status_.find(port);
435 if (it == port_status_.end())
437 if (status == kStatusError)
440 port_status_.erase(it);
443 void PortForwardingController::Connection::UpdateSocketCountOnHandlerThread(
444 int port, int increment) {
445 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
446 base::Bind(&Connection::UpdateSocketCount, this, port, increment));
449 void PortForwardingController::Connection::UpdateSocketCount(
450 int port, int increment) {
451 #if defined(DEBUG_DEVTOOLS)
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453 PortStatusMap::iterator it = port_status_.find(port);
454 if (it == port_status_.end())
456 if (it->second < 0 || (it->second == 0 && increment < 0))
458 it->second += increment;
459 #endif // defined(DEBUG_DEVTOOLS)
462 const PortForwardingController::PortStatusMap&
463 PortForwardingController::Connection::GetPortStatusMap() {
464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468 void PortForwardingController::Connection::OnSocketOpened() {
469 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
471 // Socket was created after Shutdown was called. Disconnect immediately.
472 web_socket_->Disconnect();
476 SerializeChanges(kTetheringBind, ForwardingMap(), forwarding_map_);
479 void PortForwardingController::Connection::OnSocketClosed(
480 bool closed_by_device) {
481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
482 Release(); // Balanced in the constructor.
485 void PortForwardingController::Connection::OnFrameRead(
486 const std::string& message) {
487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488 if (ProcessResponse(message))
491 scoped_ptr<DevToolsProtocol::Notification> notification(
492 DevToolsProtocol::ParseNotification(message));
496 if (notification->method() != kTetheringAccepted)
499 base::DictionaryValue* params = notification->params();
504 std::string connection_id;
505 if (!params->GetInteger(kPortAttribute, &port) ||
506 !params->GetString(kConnectionIdAttribute, &connection_id))
509 std::map<int, std::string>::iterator it = forwarding_map_.find(port);
510 if (it == forwarding_map_.end())
513 std::string location = it->second;
514 std::vector<std::string> tokens;
515 Tokenize(location, ":", &tokens);
516 int destination_port = 0;
517 if (tokens.size() != 2 || !base::StringToInt(tokens[1], &destination_port))
519 std::string destination_host = tokens[0];
521 SocketTunnel::CounterCallback callback =
522 base::Bind(&Connection::UpdateSocketCountOnHandlerThread, this, port);
525 connection_id.c_str(),
526 base::Bind(&SocketTunnel::StartTunnel,
532 PortForwardingController::PortForwardingController(Profile* profile)
534 pref_service_(profile->GetPrefs()),
536 pref_change_registrar_.Init(pref_service_);
537 base::Closure callback = base::Bind(
538 &PortForwardingController::OnPrefsChange, base::Unretained(this));
539 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, callback);
540 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, callback);
545 PortForwardingController::~PortForwardingController() {}
547 void PortForwardingController::Shutdown() {
548 // Existing connection will not be shut down. This might be confusing for
549 // some users, but the opposite is more confusing.
553 void PortForwardingController::AddListener(Listener* listener) {
554 listeners_.push_back(listener);
557 void PortForwardingController::RemoveListener(Listener* listener) {
558 Listeners::iterator it =
559 std::find(listeners_.begin(), listeners_.end(), listener);
560 DCHECK(it != listeners_.end());
561 listeners_.erase(it);
564 void PortForwardingController::DeviceListChanged(
565 const DevToolsAndroidBridge::RemoteDevices& devices) {
566 DevicesStatus status;
568 for (DevToolsAndroidBridge::RemoteDevices::const_iterator it =
569 devices.begin(); it != devices.end(); ++it) {
570 scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device = *it;
571 if (!device->is_connected())
573 Registry::iterator rit = registry_.find(device->serial());
574 if (rit == registry_.end()) {
575 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser =
576 FindBestBrowserForTethering(device->browsers());
578 new Connection(®istry_, device, browser, forwarding_map_);
581 status[device->serial()] = (*rit).second->GetPortStatusMap();
585 NotifyListeners(status);
588 void PortForwardingController::OnPrefsChange() {
589 forwarding_map_.clear();
591 if (pref_service_->GetBoolean(prefs::kDevToolsPortForwardingEnabled)) {
592 const base::DictionaryValue* dict =
593 pref_service_->GetDictionary(prefs::kDevToolsPortForwardingConfig);
594 for (base::DictionaryValue::Iterator it(*dict);
595 !it.IsAtEnd(); it.Advance()) {
597 std::string location;
598 if (base::StringToInt(it.key(), &port_num) &&
599 dict->GetString(it.key(), &location))
600 forwarding_map_[port_num] = location;
604 if (!forwarding_map_.empty()) {
609 ShutdownConnections();
610 NotifyListeners(DevicesStatus());
614 void PortForwardingController::StartListening() {
618 DevToolsAndroidBridge* android_bridge =
619 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
621 android_bridge->AddDeviceListListener(this);
625 void PortForwardingController::StopListening() {
629 DevToolsAndroidBridge* android_bridge =
630 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
632 android_bridge->RemoveDeviceListListener(this);
635 void PortForwardingController::UpdateConnections() {
636 for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
637 it->second->UpdateForwardingMap(forwarding_map_);
640 void PortForwardingController::ShutdownConnections() {
641 for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
642 it->second->Shutdown();
646 void PortForwardingController::NotifyListeners(
647 const DevicesStatus& status) const {
648 Listeners copy(listeners_); // Iterate over copy.
649 for (Listeners::const_iterator it = copy.begin(); it != copy.end(); ++it)
650 (*it)->PortStatusChanged(status);
654 PortForwardingController::Factory*
655 PortForwardingController::Factory::GetInstance() {
656 return Singleton<PortForwardingController::Factory>::get();
660 PortForwardingController* PortForwardingController::Factory::GetForProfile(
662 return static_cast<PortForwardingController*>(GetInstance()->
663 GetServiceForBrowserContext(profile, true));
666 PortForwardingController::Factory::Factory()
667 : BrowserContextKeyedServiceFactory(
668 "PortForwardingController",
669 BrowserContextDependencyManager::GetInstance()) {}
671 PortForwardingController::Factory::~Factory() {}
673 KeyedService* PortForwardingController::Factory::BuildServiceInstanceFor(
674 content::BrowserContext* context) const {
675 Profile* profile = Profile::FromBrowserContext(context);
676 return new PortForwardingController(profile);