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_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 "chrome/browser/devtools/adb_client_socket.h"
19 #include "chrome/browser/devtools/adb_web_socket.h"
20 #include "chrome/browser/devtools/devtools_protocol.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/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;
56 typedef base::Callback<void(int)> CounterCallback;
58 SocketTunnel(const std::string& location, const CounterCallback& callback)
59 : location_(location),
61 pending_destruction_(false),
63 about_to_destroy_(false) {
67 void Start(int result, net::StreamSocket* socket) {
72 remote_socket_.reset(socket);
74 std::vector<std::string> tokens;
75 Tokenize(location_, ":", &tokens);
77 if (tokens.size() != 2 || !base::StringToInt(tokens[1], &port)) {
82 host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
83 net::HostResolver::RequestInfo request_info(
84 net::HostPortPair(tokens[0], port));
85 result = host_resolver_->Resolve(
87 net::DEFAULT_PRIORITY,
89 base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
92 if (result != net::ERR_IO_PENDING)
97 void OnResolved(int result) {
103 host_socket_.reset(new net::TCPClientSocket(address_list_, NULL,
104 net::NetLog::Source()));
105 result = host_socket_->Connect(base::Bind(&SocketTunnel::OnConnected,
106 base::Unretained(this)));
107 if (result != net::ERR_IO_PENDING)
112 about_to_destroy_ = true;
114 host_socket_->Disconnect();
116 remote_socket_->Disconnect();
120 void OnConnected(int result) {
126 Pump(host_socket_.get(), remote_socket_.get());
127 Pump(remote_socket_.get(), host_socket_.get());
130 void Pump(net::StreamSocket* from, net::StreamSocket* to) {
131 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
132 int result = from->Read(
136 &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
137 if (result != net::ERR_IO_PENDING)
138 OnRead(from, to, buffer, result);
141 void OnRead(net::StreamSocket* from,
142 net::StreamSocket* to,
143 scoped_refptr<net::IOBuffer> buffer,
151 scoped_refptr<net::DrainableIOBuffer> drainable =
152 new net::DrainableIOBuffer(buffer.get(), total);
155 result = to->Write(drainable.get(),
157 base::Bind(&SocketTunnel::OnWritten,
158 base::Unretained(this),
162 if (result != net::ERR_IO_PENDING)
163 OnWritten(drainable, from, to, result);
166 void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
167 net::StreamSocket* from,
168 net::StreamSocket* to,
176 drainable->DidConsume(result);
177 if (drainable->BytesRemaining() > 0) {
179 result = to->Write(drainable.get(),
180 drainable->BytesRemaining(),
181 base::Bind(&SocketTunnel::OnWritten,
182 base::Unretained(this),
186 if (result != net::ERR_IO_PENDING)
187 OnWritten(drainable, from, to, result);
191 if (pending_destruction_) {
198 void SelfDestruct() {
199 // In case one of the connections closes, we could get here
200 // from another one due to Disconnect firing back on all
202 if (about_to_destroy_)
204 if (pending_writes_ > 0) {
205 pending_destruction_ = true;
211 std::string location_;
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 DevToolsAdbBridge::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 std::string FindBestSocketForTethering(
235 const DevToolsAdbBridge::RemoteBrowsers browsers) {
237 ParsedVersion newest_version;
238 for (DevToolsAdbBridge::RemoteBrowsers::const_iterator it = browsers.begin();
239 it != browsers.end(); ++it) {
240 scoped_refptr<DevToolsAdbBridge::RemoteBrowser> browser = *it;
241 ParsedVersion current_version = browser->GetParsedVersion();
242 if (browser->IsChrome() &&
243 IsPortForwardingSupported(current_version) &&
244 IsVersionLower(newest_version, current_version)) {
245 socket = browser->socket();
246 newest_version = current_version;
254 class PortForwardingController::Connection
255 : public AdbWebSocket::Delegate,
256 public base::RefCountedThreadSafe<
258 content::BrowserThread::DeleteOnUIThread> {
260 Connection(Registry* registry,
261 scoped_refptr<AndroidDevice> device,
262 const std::string& socket,
263 scoped_refptr<RefCountedAdbThread> adb_thread,
264 PrefService* pref_service);
266 const PortStatusMap& GetPortStatusMap();
271 friend struct content::BrowserThread::DeleteOnThread<
272 content::BrowserThread::UI>;
273 friend class base::DeleteHelper<Connection>;
275 virtual ~Connection();
277 typedef std::map<int, std::string> ForwardingMap;
279 typedef base::Callback<void(PortStatus)> CommandCallback;
280 typedef std::map<int, CommandCallback> CommandCallbackMap;
282 void OnPrefsChange();
284 void ChangeForwardingMap(ForwardingMap map);
286 void SerializeChanges(const std::string& method,
287 const ForwardingMap& old_map,
288 const ForwardingMap& new_map);
290 void SendCommand(const std::string& method, int port);
291 bool ProcessResponse(const std::string& json);
293 void ProcessBindResponse(int port, PortStatus status);
294 void ProcessUnbindResponse(int port, PortStatus status);
295 void UpdateSocketCount(int port, int increment);
296 void UpdatePortStatusMap();
297 void UpdatePortStatusMapOnUIThread(const PortStatusMap& status_map);
299 // AdbWebSocket::Delegate implementation:
300 virtual void OnSocketOpened() OVERRIDE;
301 virtual void OnFrameRead(const std::string& message) OVERRIDE;
302 virtual void OnSocketClosed(bool closed_by_device) OVERRIDE;
303 virtual bool ProcessIncomingMessage(const std::string& message) OVERRIDE;
305 PortForwardingController::Registry* registry_;
306 scoped_refptr<AndroidDevice> device_;
307 scoped_refptr<RefCountedAdbThread> adb_thread_;
308 PrefChangeRegistrar pref_change_registrar_;
309 scoped_refptr<AdbWebSocket> web_socket_;
311 ForwardingMap forwarding_map_;
312 CommandCallbackMap pending_responses_;
313 PortStatusMap port_status_;
314 PortStatusMap port_status_on_ui_thread_;
316 DISALLOW_COPY_AND_ASSIGN(Connection);
319 PortForwardingController::Connection::Connection(
321 scoped_refptr<AndroidDevice> device,
322 const std::string& socket,
323 scoped_refptr<RefCountedAdbThread> adb_thread,
324 PrefService* pref_service)
325 : registry_(registry),
327 adb_thread_(adb_thread),
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330 pref_change_registrar_.Init(pref_service);
331 (*registry_)[device_->serial()] = this;
332 web_socket_ = new AdbWebSocket(
333 device, socket, kDevToolsRemoteBrowserTarget,
334 adb_thread_->message_loop(), this);
335 AddRef(); // Balanced in OnSocketClosed();
338 void PortForwardingController::Connection::Shutdown() {
340 // This will have no effect if the socket is not connected yet.
341 web_socket_->Disconnect();
344 PortForwardingController::Connection::~Connection() {
346 DCHECK(registry_->find(device_->serial()) != registry_->end());
347 registry_->erase(device_->serial());
351 void PortForwardingController::Connection::OnPrefsChange() {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 ForwardingMap new_forwarding_map;
356 PrefService* pref_service = pref_change_registrar_.prefs();
357 const base::DictionaryValue* dict =
358 pref_service->GetDictionary(prefs::kDevToolsPortForwardingConfig);
359 for (base::DictionaryValue::Iterator it(*dict);
360 !it.IsAtEnd(); it.Advance()) {
362 std::string location;
363 if (base::StringToInt(it.key(), &port_num) &&
364 dict->GetString(it.key(), &location))
365 new_forwarding_map[port_num] = location;
368 adb_thread_->message_loop()->PostTask(
370 base::Bind(&Connection::ChangeForwardingMap,
371 this, new_forwarding_map));
374 void PortForwardingController::Connection::ChangeForwardingMap(
375 ForwardingMap new_forwarding_map) {
376 DCHECK_EQ(base::MessageLoop::current(), adb_thread_->message_loop());
378 SerializeChanges(kTetheringUnbind, new_forwarding_map, forwarding_map_);
379 SerializeChanges(kTetheringBind, forwarding_map_, new_forwarding_map);
380 forwarding_map_ = new_forwarding_map;
383 void PortForwardingController::Connection::SerializeChanges(
384 const std::string& method,
385 const ForwardingMap& old_map,
386 const ForwardingMap& new_map) {
387 for (ForwardingMap::const_iterator new_it(new_map.begin());
388 new_it != new_map.end(); ++new_it) {
389 int port = new_it->first;
390 const std::string& location = new_it->second;
391 ForwardingMap::const_iterator old_it = old_map.find(port);
392 if (old_it != old_map.end() && old_it->second == location)
393 continue; // The port points to the same location in both configs, skip.
395 SendCommand(method, port);
399 void PortForwardingController::Connection::SendCommand(
400 const std::string& method, int port) {
401 base::DictionaryValue params;
402 params.SetInteger(kPortAttribute, port);
403 DevToolsProtocol::Command command(++command_id_, method, ¶ms);
405 if (method == kTetheringBind) {
406 pending_responses_[command.id()] =
407 base::Bind(&Connection::ProcessBindResponse,
408 base::Unretained(this), port);
409 #if defined(DEBUG_DEVTOOLS)
410 port_status_[port] = kStatusConnecting;
411 UpdatePortStatusMap();
412 #endif // defined(DEBUG_DEVTOOLS)
414 DCHECK_EQ(kTetheringUnbind, method);
416 PortStatusMap::iterator it = port_status_.find(port);
417 if (it != port_status_.end() && it->second == kStatusError) {
418 // The bind command failed on this port, do not attempt unbind.
419 port_status_.erase(it);
420 UpdatePortStatusMap();
424 pending_responses_[command.id()] =
425 base::Bind(&Connection::ProcessUnbindResponse,
426 base::Unretained(this), port);
427 #if defined(DEBUG_DEVTOOLS)
428 port_status_[port] = kStatusDisconnecting;
429 UpdatePortStatusMap();
430 #endif // defined(DEBUG_DEVTOOLS)
433 web_socket_->SendFrameOnHandlerThread(command.Serialize());
436 bool PortForwardingController::Connection::ProcessResponse(
437 const std::string& message) {
438 scoped_ptr<DevToolsProtocol::Response> response(
439 DevToolsProtocol::ParseResponse(message));
443 CommandCallbackMap::iterator it = pending_responses_.find(response->id());
444 if (it == pending_responses_.end())
447 it->second.Run(response->error_code() ? kStatusError : kStatusOK);
448 pending_responses_.erase(it);
452 void PortForwardingController::Connection::ProcessBindResponse(
453 int port, PortStatus status) {
454 port_status_[port] = status;
455 UpdatePortStatusMap();
458 void PortForwardingController::Connection::ProcessUnbindResponse(
459 int port, PortStatus status) {
460 PortStatusMap::iterator it = port_status_.find(port);
461 if (it == port_status_.end())
463 if (status == kStatusError)
466 port_status_.erase(it);
467 UpdatePortStatusMap();
470 void PortForwardingController::Connection::UpdateSocketCount(
471 int port, int increment) {
472 #if defined(DEBUG_DEVTOOLS)
473 PortStatusMap::iterator it = port_status_.find(port);
474 if (it == port_status_.end())
476 if (it->second < 0 || (it->second == 0 && increment < 0))
478 it->second += increment;
479 UpdatePortStatusMap();
480 #endif // defined(DEBUG_DEVTOOLS)
483 void PortForwardingController::Connection::UpdatePortStatusMap() {
484 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
485 base::Bind(&Connection::UpdatePortStatusMapOnUIThread,
486 this, port_status_));
489 void PortForwardingController::Connection::UpdatePortStatusMapOnUIThread(
490 const PortStatusMap& status_map) {
491 port_status_on_ui_thread_ = status_map;
494 const PortForwardingController::PortStatusMap&
495 PortForwardingController::Connection::GetPortStatusMap() {
496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
497 return port_status_on_ui_thread_;
500 void PortForwardingController::Connection::OnSocketOpened() {
501 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
503 // Socket was created after Shutdown was called. Disconnect immediately.
504 web_socket_->Disconnect();
508 pref_change_registrar_.Add(
509 prefs::kDevToolsPortForwardingConfig,
510 base::Bind(&Connection::OnPrefsChange, base::Unretained(this)));
513 void PortForwardingController::Connection::OnFrameRead(
514 const std::string& message) {
517 void PortForwardingController::Connection::OnSocketClosed(
518 bool closed_by_device) {
519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
520 Release(); // Balanced in the constructor.
523 bool PortForwardingController::Connection::ProcessIncomingMessage(
524 const std::string& message) {
525 DCHECK_EQ(base::MessageLoop::current(), adb_thread_->message_loop());
526 if (ProcessResponse(message))
529 scoped_ptr<DevToolsProtocol::Notification> notification(
530 DevToolsProtocol::ParseNotification(message));
534 if (notification->method() != kTetheringAccepted)
537 base::DictionaryValue* params = notification->params();
542 std::string connection_id;
543 if (!params->GetInteger(kPortAttribute, &port) ||
544 !params->GetString(kConnectionIdAttribute, &connection_id))
547 std::map<int, std::string>::iterator it = forwarding_map_.find(port);
548 if (it == forwarding_map_.end())
551 std::string location = it->second;
553 SocketTunnel* tunnel = new SocketTunnel(location,
554 base::Bind(&Connection::UpdateSocketCount, this, port));
556 device_->OpenSocket(connection_id.c_str(),
557 base::Bind(&SocketTunnel::Start, base::Unretained(tunnel)));
561 PortForwardingController::PortForwardingController(PrefService* pref_service)
562 : adb_thread_(RefCountedAdbThread::GetInstance()),
563 pref_service_(pref_service) {
564 pref_change_registrar_.Init(pref_service);
565 base::Closure callback = base::Bind(
566 &PortForwardingController::OnPrefsChange, base::Unretained(this));
567 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, callback);
568 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, callback);
571 PortForwardingController::~PortForwardingController() {
572 ShutdownConnections();
575 PortForwardingController::DevicesStatus
576 PortForwardingController::UpdateDeviceList(
577 const DevToolsAdbBridge::RemoteDevices& devices) {
578 DevicesStatus status;
579 if (!ShouldCreateConnections())
582 for (DevToolsAdbBridge::RemoteDevices::const_iterator it = devices.begin();
583 it != devices.end(); ++it) {
584 scoped_refptr<DevToolsAdbBridge::RemoteDevice> device = *it;
585 if (!device->IsConnected())
587 Registry::iterator rit = registry_.find(device->GetSerial());
588 if (rit == registry_.end()) {
589 std::string socket = FindBestSocketForTethering(device->browsers());
590 if (!socket.empty()) {
592 ®istry_, device->device(), socket, adb_thread_, pref_service_);
595 status[device->GetSerial()] = (*rit).second->GetPortStatusMap();
601 void PortForwardingController::OnPrefsChange() {
602 if (!ShouldCreateConnections())
603 ShutdownConnections();
606 bool PortForwardingController::ShouldCreateConnections() {
607 if (!pref_service_->GetBoolean(prefs::kDevToolsPortForwardingEnabled))
610 const base::DictionaryValue* dict =
611 pref_service_->GetDictionary(prefs::kDevToolsPortForwardingConfig);
612 return !base::DictionaryValue::Iterator(*dict).IsAtEnd();
615 void PortForwardingController::ShutdownConnections() {
616 for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
617 it->second->Shutdown();
622 PortForwardingController::Factory*
623 PortForwardingController::Factory::GetInstance() {
624 return Singleton<PortForwardingController::Factory>::get();
628 PortForwardingController* PortForwardingController::Factory::GetForProfile(
630 return static_cast<PortForwardingController*>(GetInstance()->
631 GetServiceForBrowserContext(profile, true));
634 PortForwardingController::Factory::Factory()
635 : BrowserContextKeyedServiceFactory(
636 "PortForwardingController",
637 BrowserContextDependencyManager::GetInstance()) {}
639 PortForwardingController::Factory::~Factory() {}
641 KeyedService* PortForwardingController::Factory::BuildServiceInstanceFor(
642 content::BrowserContext* context) const {
643 Profile* profile = Profile::FromBrowserContext(context);
644 return new PortForwardingController(profile->GetPrefs());