Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / devtools / port_forwarding_controller.cc
blob7d4255e356e0f51efdf5700e769b0994a5f695c7
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"
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_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;
34 namespace {
36 const int kBufferSize = 16 * 1024;
38 enum {
39 kStatusError = -3,
40 kStatusDisconnecting = -2,
41 kStatusConnecting = -1,
42 kStatusOK = 0,
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;
55 class SocketTunnel {
56 public:
57 typedef base::Callback<void(int)> CounterCallback;
59 SocketTunnel(const std::string& location, const CounterCallback& callback)
60 : location_(location),
61 pending_writes_(0),
62 pending_destruction_(false),
63 callback_(callback),
64 about_to_destroy_(false) {
65 callback_.Run(1);
68 void Start(int result, net::StreamSocket* socket) {
69 if (result < 0) {
70 SelfDestruct();
71 return;
73 remote_socket_.reset(socket);
75 std::vector<std::string> tokens;
76 Tokenize(location_, ":", &tokens);
77 int port = 0;
78 if (tokens.size() != 2 || !base::StringToInt(tokens[1], &port)) {
79 SelfDestruct();
80 return;
83 host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
84 net::HostResolver::RequestInfo request_info(
85 net::HostPortPair(tokens[0], port));
86 result = host_resolver_->Resolve(
87 request_info,
88 net::DEFAULT_PRIORITY,
89 &address_list_,
90 base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
91 NULL,
92 net::BoundNetLog());
93 if (result != net::ERR_IO_PENDING)
94 OnResolved(result);
97 private:
98 void OnResolved(int result) {
99 if (result < 0) {
100 SelfDestruct();
101 return;
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)
109 OnConnected(result);
112 ~SocketTunnel() {
113 about_to_destroy_ = true;
114 if (host_socket_)
115 host_socket_->Disconnect();
116 if (remote_socket_)
117 remote_socket_->Disconnect();
118 callback_.Run(-1);
121 void OnConnected(int result) {
122 if (result < 0) {
123 SelfDestruct();
124 return;
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(
134 buffer.get(),
135 kBufferSize,
136 base::Bind(
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,
145 int result) {
146 if (result <= 0) {
147 SelfDestruct();
148 return;
151 int total = result;
152 scoped_refptr<net::DrainableIOBuffer> drainable =
153 new net::DrainableIOBuffer(buffer.get(), total);
155 ++pending_writes_;
156 result = to->Write(drainable.get(),
157 total,
158 base::Bind(&SocketTunnel::OnWritten,
159 base::Unretained(this),
160 drainable,
161 from,
162 to));
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,
170 int result) {
171 --pending_writes_;
172 if (result < 0) {
173 SelfDestruct();
174 return;
177 drainable->DidConsume(result);
178 if (drainable->BytesRemaining() > 0) {
179 ++pending_writes_;
180 result = to->Write(drainable.get(),
181 drainable->BytesRemaining(),
182 base::Bind(&SocketTunnel::OnWritten,
183 base::Unretained(this),
184 drainable,
185 from,
186 to));
187 if (result != net::ERR_IO_PENDING)
188 OnWritten(drainable, from, to, result);
189 return;
192 if (pending_destruction_) {
193 SelfDestruct();
194 return;
196 Pump(from, to);
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
202 // read callbacks.
203 if (about_to_destroy_)
204 return;
205 if (pending_writes_ > 0) {
206 pending_destruction_ = true;
207 return;
209 delete this;
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_;
217 int pending_writes_;
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) {
237 std::string socket;
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;
250 return socket;
253 } // namespace
255 class PortForwardingController::Connection
256 : public AdbWebSocket::Delegate,
257 public base::RefCountedThreadSafe<
258 Connection,
259 content::BrowserThread::DeleteOnUIThread> {
260 public:
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();
269 void Shutdown();
271 private:
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_;
311 int command_id_;
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(
321 Registry* registry,
322 scoped_refptr<AndroidDevice> device,
323 const std::string& socket,
324 scoped_refptr<RefCountedAdbThread> adb_thread,
325 PrefService* pref_service)
326 : registry_(registry),
327 device_(device),
328 adb_thread_(adb_thread),
329 command_id_(0) {
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() {
340 registry_ = NULL;
341 // This will have no effect if the socket is not connected yet.
342 web_socket_->Disconnect();
345 PortForwardingController::Connection::~Connection() {
346 if (registry_) {
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();
358 bool enabled =
359 pref_service->GetBoolean(prefs::kDevToolsPortForwardingEnabled);
360 if (enabled) {
361 const base::DictionaryValue* dict =
362 pref_service->GetDictionary(prefs::kDevToolsPortForwardingConfig);
363 for (base::DictionaryValue::Iterator it(*dict);
364 !it.IsAtEnd(); it.Advance()) {
365 int port_num;
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(
374 FROM_HERE,
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, &params);
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)
418 } else {
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();
426 return;
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));
445 if (!response)
446 return false;
448 CommandCallbackMap::iterator it = pending_responses_.find(response->id());
449 if (it == pending_responses_.end())
450 return false;
452 it->second.Run(response->error_code() ? kStatusError : kStatusOK);
453 pending_responses_.erase(it);
454 return true;
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())
467 return;
468 if (status == kStatusError)
469 it->second = status;
470 else
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())
480 return;
481 if (it->second < 0 || (it->second == 0 && increment < 0))
482 return;
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));
507 if (!registry_) {
508 // Socket was created after Shutdown was called. Disconnect immediately.
509 web_socket_->Disconnect();
510 return;
512 OnPrefsChange();
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))
535 return true;
537 scoped_ptr<DevToolsProtocol::Notification> notification(
538 DevToolsProtocol::ParseNotification(message));
539 if (!notification)
540 return false;
542 if (notification->method() != kTetheringAccepted)
543 return false;
545 base::DictionaryValue* params = notification->params();
546 if (!params)
547 return false;
549 int port;
550 std::string connection_id;
551 if (!params->GetInteger(kPortAttribute, &port) ||
552 !params->GetString(kConnectionIdAttribute, &connection_id))
553 return false;
555 std::map<int, std::string>::iterator it = forwarding_map_.find(port);
556 if (it == forwarding_map_.end())
557 return false;
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)));
566 return true;
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())
587 continue;
588 Registry::iterator rit = registry_.find(device->GetSerial());
589 if (rit == registry_.end()) {
590 std::string socket = FindBestSocketForTethering(device->browsers());
591 if (!socket.empty()) {
592 new Connection(
593 &registry_, device->device(), socket, adb_thread_, pref_service_);
595 } else {
596 status[device->GetSerial()] = (*rit).second->GetPortStatusMap();
599 return status;
602 // static
603 PortForwardingController::Factory*
604 PortForwardingController::Factory::GetInstance() {
605 return Singleton<PortForwardingController::Factory>::get();
608 // static
609 PortForwardingController* PortForwardingController::Factory::GetForProfile(
610 Profile* profile) {
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());