Upstream version 10.38.208.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / devtools / device / port_forwarding_controller.cc
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.
4
5 #include "chrome/browser/devtools/device/port_forwarding_controller.h"
6
7 #include <algorithm>
8 #include <map>
9
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"
30
31 using content::BrowserThread;
32
33 namespace {
34
35 const int kBufferSize = 16 * 1024;
36
37 enum {
38   kStatusError = -3,
39   kStatusDisconnecting = -2,
40   kStatusConnecting = -1,
41   kStatusOK = 0,
42   // Positive values are used to count open connections.
43 };
44
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";
50
51 static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
52 const int kMinVersionPortForwarding = 28;
53
54 class SocketTunnel : public base::NonThreadSafe {
55  public:
56   typedef base::Callback<void(int)> CounterCallback;
57
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);
67   }
68
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);
76   }
77
78   void Start(net::StreamSocket* socket, const std::string& host, int port) {
79     remote_socket_.reset(socket);
80
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);
92   }
93
94   void OnResolved(int result) {
95     if (result < 0) {
96       SelfDestruct();
97       return;
98     }
99
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);
106   }
107
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);
115   }
116
117   void OnConnected(int result) {
118     if (result < 0) {
119       SelfDestruct();
120       return;
121     }
122
123     ++pending_writes_; // avoid SelfDestruct in first Pump
124     Pump(host_socket_.get(), remote_socket_.get());
125     --pending_writes_;
126     if (pending_destruction_) {
127       SelfDestruct();
128     } else {
129       Pump(remote_socket_.get(), host_socket_.get());
130     }
131   }
132
133   void Pump(net::StreamSocket* from, net::StreamSocket* to) {
134     scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
135     int result = from->Read(
136         buffer.get(),
137         kBufferSize,
138         base::Bind(
139             &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
140     if (result != net::ERR_IO_PENDING)
141       OnRead(from, to, buffer, result);
142   }
143
144   void OnRead(net::StreamSocket* from,
145               net::StreamSocket* to,
146               scoped_refptr<net::IOBuffer> buffer,
147               int result) {
148     if (result <= 0) {
149       SelfDestruct();
150       return;
151     }
152
153     int total = result;
154     scoped_refptr<net::DrainableIOBuffer> drainable =
155         new net::DrainableIOBuffer(buffer.get(), total);
156
157     ++pending_writes_;
158     result = to->Write(drainable.get(),
159                        total,
160                        base::Bind(&SocketTunnel::OnWritten,
161                                   base::Unretained(this),
162                                   drainable,
163                                   from,
164                                   to));
165     if (result != net::ERR_IO_PENDING)
166       OnWritten(drainable, from, to, result);
167   }
168
169   void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
170                  net::StreamSocket* from,
171                  net::StreamSocket* to,
172                  int result) {
173     --pending_writes_;
174     if (result < 0) {
175       SelfDestruct();
176       return;
177     }
178
179     drainable->DidConsume(result);
180     if (drainable->BytesRemaining() > 0) {
181       ++pending_writes_;
182       result = to->Write(drainable.get(),
183                          drainable->BytesRemaining(),
184                          base::Bind(&SocketTunnel::OnWritten,
185                                     base::Unretained(this),
186                                     drainable,
187                                     from,
188                                     to));
189       if (result != net::ERR_IO_PENDING)
190         OnWritten(drainable, from, to, result);
191       return;
192     }
193
194     if (pending_destruction_) {
195       SelfDestruct();
196       return;
197     }
198     Pump(from, to);
199   }
200
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
204     // read callbacks.
205     if (about_to_destroy_)
206       return;
207     if (pending_writes_ > 0) {
208       pending_destruction_ = true;
209       return;
210     }
211     delete this;
212   }
213
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_;
218   int pending_writes_;
219   bool pending_destruction_;
220   CounterCallback callback_;
221   bool about_to_destroy_;
222 };
223
224 typedef DevToolsAndroidBridge::RemoteBrowser::ParsedVersion ParsedVersion;
225
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());
230 }
231
232 static bool IsPortForwardingSupported(const ParsedVersion& version) {
233   return !version.empty() && version[0] >= kMinVersionPortForwarding;
234 }
235
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;
249     }
250   }
251   return best_browser;
252 }
253
254 }  // namespace
255
256 class PortForwardingController::Connection
257     : public DevToolsAndroidBridge::AndroidWebSocket::Delegate,
258       public base::RefCountedThreadSafe<
259           Connection,
260           content::BrowserThread::DeleteOnUIThread> {
261  public:
262   Connection(Registry* registry,
263              scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
264              scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
265              const ForwardingMap& forwarding_map);
266
267   const PortStatusMap& GetPortStatusMap();
268
269   void UpdateForwardingMap(const ForwardingMap& new_forwarding_map);
270
271   void Shutdown();
272
273  private:
274   friend struct content::BrowserThread::DeleteOnThread<
275       content::BrowserThread::UI>;
276   friend class base::DeleteHelper<Connection>;
277
278   virtual ~Connection();
279
280   typedef std::map<int, std::string> ForwardingMap;
281
282   typedef base::Callback<void(PortStatus)> CommandCallback;
283   typedef std::map<int, CommandCallback> CommandCallbackMap;
284
285   void SerializeChanges(const std::string& method,
286                         const ForwardingMap& old_map,
287                         const ForwardingMap& new_map);
288
289   void SendCommand(const std::string& method, int port);
290   bool ProcessResponse(const std::string& json);
291
292   void ProcessBindResponse(int port, PortStatus status);
293   void ProcessUnbindResponse(int port, PortStatus status);
294
295   void UpdateSocketCountOnHandlerThread(int port, int increment);
296   void UpdateSocketCount(int port, int increment);
297
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;
302
303   PortForwardingController::Registry* registry_;
304   scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device_;
305   scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
306   scoped_refptr<DevToolsAndroidBridge::AndroidWebSocket> web_socket_;
307   int command_id_;
308   bool connected_;
309   ForwardingMap forwarding_map_;
310   CommandCallbackMap pending_responses_;
311   PortStatusMap port_status_;
312
313   DISALLOW_COPY_AND_ASSIGN(Connection);
314 };
315
316 PortForwardingController::Connection::Connection(
317     Registry* registry,
318     scoped_refptr<DevToolsAndroidBridge::RemoteDevice> device,
319     scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
320     const ForwardingMap& forwarding_map)
321     : registry_(registry),
322       device_(device),
323       browser_(browser),
324       command_id_(0),
325       connected_(false),
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();
332 }
333
334 void PortForwardingController::Connection::Shutdown() {
335   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
336   registry_ = NULL;
337   // This will have no effect if the socket is not connected yet.
338   web_socket_->Disconnect();
339 }
340
341 PortForwardingController::Connection::~Connection() {
342   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343   if (registry_) {
344     DCHECK(registry_->find(device_->serial()) != registry_->end());
345     registry_->erase(device_->serial());
346   }
347 }
348
349 void PortForwardingController::Connection::UpdateForwardingMap(
350     const ForwardingMap& new_forwarding_map) {
351   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352   if (connected_) {
353     SerializeChanges(kTetheringUnbind, new_forwarding_map, forwarding_map_);
354     SerializeChanges(kTetheringBind, forwarding_map_, new_forwarding_map);
355   }
356   forwarding_map_ = new_forwarding_map;
357 }
358
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.
371
372     SendCommand(method, port);
373   }
374 }
375
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, &params);
382
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)
390   } else {
391     DCHECK_EQ(kTetheringUnbind, method);
392
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);
397       return;
398     }
399
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)
406   }
407
408   web_socket_->SendFrame(command.Serialize());
409 }
410
411 bool PortForwardingController::Connection::ProcessResponse(
412     const std::string& message) {
413   scoped_ptr<DevToolsProtocol::Response> response(
414       DevToolsProtocol::ParseResponse(message));
415   if (!response)
416     return false;
417
418   CommandCallbackMap::iterator it = pending_responses_.find(response->id());
419   if (it == pending_responses_.end())
420     return false;
421
422   it->second.Run(response->error_code() ? kStatusError : kStatusOK);
423   pending_responses_.erase(it);
424   return true;
425 }
426
427 void PortForwardingController::Connection::ProcessBindResponse(
428     int port, PortStatus status) {
429   port_status_[port] = status;
430 }
431
432 void PortForwardingController::Connection::ProcessUnbindResponse(
433     int port, PortStatus status) {
434   PortStatusMap::iterator it = port_status_.find(port);
435   if (it == port_status_.end())
436     return;
437   if (status == kStatusError)
438     it->second = status;
439   else
440     port_status_.erase(it);
441 }
442
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));
447 }
448
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())
455     return;
456   if (it->second < 0 || (it->second == 0 && increment < 0))
457     return;
458   it->second += increment;
459 #endif  // defined(DEBUG_DEVTOOLS)
460 }
461
462 const PortForwardingController::PortStatusMap&
463 PortForwardingController::Connection::GetPortStatusMap() {
464   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
465   return port_status_;
466 }
467
468 void PortForwardingController::Connection::OnSocketOpened() {
469   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
470   if (!registry_) {
471     // Socket was created after Shutdown was called. Disconnect immediately.
472     web_socket_->Disconnect();
473     return;
474   }
475   connected_ = true;
476   SerializeChanges(kTetheringBind, ForwardingMap(), forwarding_map_);
477 }
478
479 void PortForwardingController::Connection::OnSocketClosed(
480     bool closed_by_device) {
481   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
482   Release();  // Balanced in the constructor.
483 }
484
485 void PortForwardingController::Connection::OnFrameRead(
486     const std::string& message) {
487   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
488   if (ProcessResponse(message))
489     return;
490
491   scoped_ptr<DevToolsProtocol::Notification> notification(
492       DevToolsProtocol::ParseNotification(message));
493   if (!notification)
494     return;
495
496   if (notification->method() != kTetheringAccepted)
497     return;
498
499   base::DictionaryValue* params = notification->params();
500   if (!params)
501     return;
502
503   int port;
504   std::string connection_id;
505   if (!params->GetInteger(kPortAttribute, &port) ||
506       !params->GetString(kConnectionIdAttribute, &connection_id))
507     return;
508
509   std::map<int, std::string>::iterator it = forwarding_map_.find(port);
510   if (it == forwarding_map_.end())
511     return;
512
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))
518     return;
519   std::string destination_host = tokens[0];
520
521   SocketTunnel::CounterCallback callback =
522       base::Bind(&Connection::UpdateSocketCountOnHandlerThread, this, port);
523
524   device_->OpenSocket(
525       connection_id.c_str(),
526       base::Bind(&SocketTunnel::StartTunnel,
527                  destination_host,
528                  destination_port,
529                  callback));
530 }
531
532 PortForwardingController::PortForwardingController(Profile* profile)
533     : profile_(profile),
534       pref_service_(profile->GetPrefs()),
535       listening_(false) {
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);
541   OnPrefsChange();
542 }
543
544
545 PortForwardingController::~PortForwardingController() {}
546
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.
550   StopListening();
551 }
552
553 void PortForwardingController::AddListener(Listener* listener) {
554   listeners_.push_back(listener);
555 }
556
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);
562 }
563
564 void PortForwardingController::DeviceListChanged(
565     const DevToolsAndroidBridge::RemoteDevices& devices) {
566   DevicesStatus status;
567
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())
572       continue;
573     Registry::iterator rit = registry_.find(device->serial());
574     if (rit == registry_.end()) {
575       scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser =
576           FindBestBrowserForTethering(device->browsers());
577       if (browser) {
578         new Connection(&registry_, device, browser, forwarding_map_);
579       }
580     } else {
581       status[device->serial()] = (*rit).second->GetPortStatusMap();
582     }
583   }
584
585   NotifyListeners(status);
586 }
587
588 void PortForwardingController::OnPrefsChange() {
589   forwarding_map_.clear();
590
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()) {
596       int port_num;
597       std::string location;
598       if (base::StringToInt(it.key(), &port_num) &&
599           dict->GetString(it.key(), &location))
600         forwarding_map_[port_num] = location;
601     }
602   }
603
604   if (!forwarding_map_.empty()) {
605     StartListening();
606     UpdateConnections();
607   } else {
608     StopListening();
609     ShutdownConnections();
610     NotifyListeners(DevicesStatus());
611   }
612 }
613
614 void PortForwardingController::StartListening() {
615   if (listening_)
616     return;
617   listening_ = true;
618   DevToolsAndroidBridge* android_bridge =
619       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
620   if (android_bridge)
621     android_bridge->AddDeviceListListener(this);
622
623 }
624
625 void PortForwardingController::StopListening() {
626   if (!listening_)
627     return;
628   listening_ = false;
629   DevToolsAndroidBridge* android_bridge =
630       DevToolsAndroidBridge::Factory::GetForProfile(profile_);
631   if (android_bridge)
632     android_bridge->RemoveDeviceListListener(this);
633 }
634
635 void PortForwardingController::UpdateConnections() {
636   for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
637     it->second->UpdateForwardingMap(forwarding_map_);
638 }
639
640 void PortForwardingController::ShutdownConnections() {
641   for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
642     it->second->Shutdown();
643   registry_.clear();
644 }
645
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);
651 }
652
653 // static
654 PortForwardingController::Factory*
655 PortForwardingController::Factory::GetInstance() {
656   return Singleton<PortForwardingController::Factory>::get();
657 }
658
659 // static
660 PortForwardingController* PortForwardingController::Factory::GetForProfile(
661     Profile* profile) {
662   return static_cast<PortForwardingController*>(GetInstance()->
663           GetServiceForBrowserContext(profile, true));
664 }
665
666 PortForwardingController::Factory::Factory()
667     : BrowserContextKeyedServiceFactory(
668           "PortForwardingController",
669           BrowserContextDependencyManager::GetInstance()) {}
670
671 PortForwardingController::Factory::~Factory() {}
672
673 KeyedService* PortForwardingController::Factory::BuildServiceInstanceFor(
674     content::BrowserContext* context) const {
675   Profile* profile = Profile::FromBrowserContext(context);
676   return new PortForwardingController(profile);
677 }