Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / devtools / device / port_forwarding_controller.cc
blob937aa3477ff286dbb5e659aa860d33f0640dc07c
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"
7 #include <algorithm>
8 #include <map>
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;
33 namespace {
35 const int kBufferSize = 16 * 1024;
37 enum {
38 kStatusError = -3,
39 kStatusDisconnecting = -2,
40 kStatusConnecting = -1,
41 kStatusOK = 0,
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 {
55 public:
56 typedef base::Callback<void(int)> CounterCallback;
58 static void StartTunnel(const std::string& host,
59 int port,
60 const CounterCallback& callback,
61 int result,
62 net::StreamSocket* socket) {
63 if (result < 0)
64 return;
65 SocketTunnel* tunnel = new SocketTunnel(callback);
66 tunnel->Start(socket, host, port);
69 private:
70 explicit SocketTunnel(const CounterCallback& callback)
71 : pending_writes_(0),
72 pending_destruction_(false),
73 callback_(callback),
74 about_to_destroy_(false) {
75 callback_.Run(1);
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(
84 request_info,
85 net::DEFAULT_PRIORITY,
86 &address_list_,
87 base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
88 NULL,
89 net::BoundNetLog());
90 if (result != net::ERR_IO_PENDING)
91 OnResolved(result);
94 void OnResolved(int result) {
95 if (result < 0) {
96 SelfDestruct();
97 return;
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)
105 OnConnected(result);
108 ~SocketTunnel() {
109 about_to_destroy_ = true;
110 if (host_socket_)
111 host_socket_->Disconnect();
112 if (remote_socket_)
113 remote_socket_->Disconnect();
114 callback_.Run(-1);
117 void OnConnected(int result) {
118 if (result < 0) {
119 SelfDestruct();
120 return;
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(
130 buffer.get(),
131 kBufferSize,
132 base::Bind(
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,
141 int result) {
142 if (result <= 0) {
143 SelfDestruct();
144 return;
147 int total = result;
148 scoped_refptr<net::DrainableIOBuffer> drainable =
149 new net::DrainableIOBuffer(buffer.get(), total);
151 ++pending_writes_;
152 result = to->Write(drainable.get(),
153 total,
154 base::Bind(&SocketTunnel::OnWritten,
155 base::Unretained(this),
156 drainable,
157 from,
158 to));
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,
166 int result) {
167 --pending_writes_;
168 if (result < 0) {
169 SelfDestruct();
170 return;
173 drainable->DidConsume(result);
174 if (drainable->BytesRemaining() > 0) {
175 ++pending_writes_;
176 result = to->Write(drainable.get(),
177 drainable->BytesRemaining(),
178 base::Bind(&SocketTunnel::OnWritten,
179 base::Unretained(this),
180 drainable,
181 from,
182 to));
183 if (result != net::ERR_IO_PENDING)
184 OnWritten(drainable, from, to, result);
185 return;
188 if (pending_destruction_) {
189 SelfDestruct();
190 return;
192 Pump(from, to);
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
198 // read callbacks.
199 if (about_to_destroy_)
200 return;
201 if (pending_writes_ > 0) {
202 pending_destruction_ = true;
203 return;
205 delete this;
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_;
212 int pending_writes_;
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;
246 return best_browser;
249 } // namespace
251 class PortForwardingController::Connection
252 : public DevToolsAndroidBridge::AndroidWebSocket::Delegate,
253 public base::RefCountedThreadSafe<
254 Connection,
255 content::BrowserThread::DeleteOnUIThread> {
256 public:
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);
266 void Shutdown();
268 private:
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_;
302 int command_id_;
303 bool connected_;
304 ForwardingMap forwarding_map_;
305 CommandCallbackMap pending_responses_;
306 PortStatusMap port_status_;
308 DISALLOW_COPY_AND_ASSIGN(Connection);
311 PortForwardingController::Connection::Connection(
312 Registry* registry,
313 scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
314 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
315 const ForwardingMap& forwarding_map)
316 : registry_(registry),
317 device_(device),
318 browser_(browser),
319 command_id_(0),
320 connected_(false),
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));
330 registry_ = NULL;
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));
337 if (registry_) {
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));
346 if (connected_) {
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, &params);
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)
384 } else {
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);
391 return;
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));
409 if (!response)
410 return false;
412 CommandCallbackMap::iterator it = pending_responses_.find(response->id());
413 if (it == pending_responses_.end())
414 return false;
416 it->second.Run(response->error_code() ? kStatusError : kStatusOK);
417 pending_responses_.erase(it);
418 return true;
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())
430 return;
431 if (status == kStatusError)
432 it->second = status;
433 else
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())
449 return;
450 if (it->second < 0 || (it->second == 0 && increment < 0))
451 return;
452 it->second += increment;
453 #endif // defined(DEBUG_DEVTOOLS)
456 const PortForwardingController::PortStatusMap&
457 PortForwardingController::Connection::GetPortStatusMap() {
458 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
459 return port_status_;
462 void PortForwardingController::Connection::OnSocketOpened() {
463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
464 if (!registry_) {
465 // Socket was created after Shutdown was called. Disconnect immediately.
466 web_socket_->Disconnect();
467 return;
469 connected_ = true;
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))
483 return;
485 scoped_ptr<DevToolsProtocol::Notification> notification(
486 DevToolsProtocol::ParseNotification(message));
487 if (!notification)
488 return;
490 if (notification->method() != kTetheringAccepted)
491 return;
493 base::DictionaryValue* params = notification->params();
494 if (!params)
495 return;
497 int port;
498 std::string connection_id;
499 if (!params->GetInteger(kPortAttribute, &port) ||
500 !params->GetString(kConnectionIdAttribute, &connection_id))
501 return;
503 std::map<int, std::string>::iterator it = forwarding_map_.find(port);
504 if (it == forwarding_map_.end())
505 return;
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))
512 return;
513 std::string destination_host = tokens[0];
515 SocketTunnel::CounterCallback callback =
516 base::Bind(&Connection::UpdateSocketCountOnHandlerThread, this, port);
518 device_->OpenSocket(
519 connection_id.c_str(),
520 base::Bind(&SocketTunnel::StartTunnel,
521 destination_host,
522 destination_port,
523 callback));
526 PortForwardingController::PortForwardingController(Profile* profile)
527 : profile_(profile),
528 pref_service_(profile->GetPrefs()),
529 listening_(false) {
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);
535 OnPrefsChange();
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.
544 StopListening();
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())
566 continue;
567 Registry::iterator rit = registry_.find(device->serial());
568 if (rit == registry_.end()) {
569 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser =
570 FindBestBrowserForTethering(device->browsers());
571 if (browser) {
572 new Connection(&registry_, device, browser, forwarding_map_);
574 } else {
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()) {
590 int port_num;
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()) {
599 StartListening();
600 UpdateConnections();
601 } else {
602 StopListening();
603 ShutdownConnections();
604 NotifyListeners(DevicesStatus());
608 void PortForwardingController::StartListening() {
609 if (listening_)
610 return;
611 listening_ = true;
612 DevToolsAndroidBridge* android_bridge =
613 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
614 if (android_bridge)
615 android_bridge->AddDeviceListListener(this);
619 void PortForwardingController::StopListening() {
620 if (!listening_)
621 return;
622 listening_ = false;
623 DevToolsAndroidBridge* android_bridge =
624 DevToolsAndroidBridge::Factory::GetForProfile(profile_);
625 if (android_bridge)
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();
637 registry_.clear();
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);
647 // static
648 PortForwardingController::Factory*
649 PortForwardingController::Factory::GetInstance() {
650 return Singleton<PortForwardingController::Factory>::get();
653 // static
654 PortForwardingController* PortForwardingController::Factory::GetForProfile(
655 Profile* profile) {
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);