Add more checks to investigate SupervisedUserPrefStore crash at startup.
[chromium-blink-merge.git] / chrome / browser / devtools / device / port_forwarding_controller.cc
blob25bdc39263671f7cac4e075430b2406670bb7a7e
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/profiler/scoped_tracker.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/threading/non_thread_safe.h"
20 #include "chrome/browser/devtools/devtools_protocol.h"
21 #include "chrome/browser/devtools/devtools_protocol_constants.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/pref_names.h"
24 #include "components/keyed_service/content/browser_context_dependency_manager.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "net/base/address_list.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/net_errors.h"
29 #include "net/base/net_util.h"
30 #include "net/dns/host_resolver.h"
31 #include "net/socket/tcp_client_socket.h"
33 using content::BrowserThread;
35 namespace {
37 const int kBufferSize = 16 * 1024;
39 enum {
40 kStatusError = -3,
41 kStatusDisconnecting = -2,
42 kStatusConnecting = -1,
43 kStatusOK = 0,
44 // Positive values are used to count open connections.
47 namespace tethering = ::chrome::devtools::Tethering;
49 static const char kDevToolsRemoteBrowserTarget[] = "/devtools/browser";
50 const int kMinVersionPortForwarding = 28;
52 class SocketTunnel : public base::NonThreadSafe {
53 public:
54 typedef base::Callback<void(int)> CounterCallback;
56 static void StartTunnel(const std::string& host,
57 int port,
58 const CounterCallback& callback,
59 int result,
60 scoped_ptr<net::StreamSocket> socket) {
61 if (result < 0)
62 return;
63 SocketTunnel* tunnel = new SocketTunnel(callback);
64 tunnel->Start(socket.Pass(), host, port);
67 private:
68 explicit SocketTunnel(const CounterCallback& callback)
69 : pending_writes_(0),
70 pending_destruction_(false),
71 callback_(callback),
72 about_to_destroy_(false) {
73 callback_.Run(1);
76 void Start(scoped_ptr<net::StreamSocket> socket,
77 const std::string& host, int port) {
78 remote_socket_.swap(socket);
80 host_resolver_ = net::HostResolver::CreateDefaultResolver(NULL);
81 net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
82 int result = host_resolver_->Resolve(
83 request_info,
84 net::DEFAULT_PRIORITY,
85 &address_list_,
86 base::Bind(&SocketTunnel::OnResolved, base::Unretained(this)),
87 NULL,
88 net::BoundNetLog());
89 if (result != net::ERR_IO_PENDING)
90 OnResolved(result);
93 void OnResolved(int result) {
94 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed.
95 tracked_objects::ScopedTracker tracking_profile(
96 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 SocketTunnel::OnResolved"));
98 if (result < 0) {
99 SelfDestruct();
100 return;
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)
108 OnConnected(result);
111 ~SocketTunnel() {
112 about_to_destroy_ = true;
113 if (host_socket_)
114 host_socket_->Disconnect();
115 if (remote_socket_)
116 remote_socket_->Disconnect();
117 callback_.Run(-1);
120 void OnConnected(int result) {
121 if (result < 0) {
122 SelfDestruct();
123 return;
126 ++pending_writes_; // avoid SelfDestruct in first Pump
127 Pump(host_socket_.get(), remote_socket_.get());
128 --pending_writes_;
129 if (pending_destruction_) {
130 SelfDestruct();
131 } else {
132 Pump(remote_socket_.get(), host_socket_.get());
136 void Pump(net::StreamSocket* from, net::StreamSocket* to) {
137 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kBufferSize);
138 int result = from->Read(
139 buffer.get(),
140 kBufferSize,
141 base::Bind(
142 &SocketTunnel::OnRead, base::Unretained(this), from, to, buffer));
143 if (result != net::ERR_IO_PENDING)
144 OnRead(from, to, buffer, result);
147 void OnRead(net::StreamSocket* from,
148 net::StreamSocket* to,
149 scoped_refptr<net::IOBuffer> buffer,
150 int result) {
151 if (result <= 0) {
152 SelfDestruct();
153 return;
156 int total = result;
157 scoped_refptr<net::DrainableIOBuffer> drainable =
158 new net::DrainableIOBuffer(buffer.get(), total);
160 ++pending_writes_;
161 result = to->Write(drainable.get(),
162 total,
163 base::Bind(&SocketTunnel::OnWritten,
164 base::Unretained(this),
165 drainable,
166 from,
167 to));
168 if (result != net::ERR_IO_PENDING)
169 OnWritten(drainable, from, to, result);
172 void OnWritten(scoped_refptr<net::DrainableIOBuffer> drainable,
173 net::StreamSocket* from,
174 net::StreamSocket* to,
175 int result) {
176 --pending_writes_;
177 if (result < 0) {
178 SelfDestruct();
179 return;
182 drainable->DidConsume(result);
183 if (drainable->BytesRemaining() > 0) {
184 ++pending_writes_;
185 result = to->Write(drainable.get(),
186 drainable->BytesRemaining(),
187 base::Bind(&SocketTunnel::OnWritten,
188 base::Unretained(this),
189 drainable,
190 from,
191 to));
192 if (result != net::ERR_IO_PENDING)
193 OnWritten(drainable, from, to, result);
194 return;
197 if (pending_destruction_) {
198 SelfDestruct();
199 return;
201 Pump(from, to);
204 void SelfDestruct() {
205 // In case one of the connections closes, we could get here
206 // from another one due to Disconnect firing back on all
207 // read callbacks.
208 if (about_to_destroy_)
209 return;
210 if (pending_writes_ > 0) {
211 pending_destruction_ = true;
212 return;
214 delete this;
217 scoped_ptr<net::StreamSocket> remote_socket_;
218 scoped_ptr<net::StreamSocket> host_socket_;
219 scoped_ptr<net::HostResolver> host_resolver_;
220 net::AddressList address_list_;
221 int pending_writes_;
222 bool pending_destruction_;
223 CounterCallback callback_;
224 bool about_to_destroy_;
227 typedef DevToolsAndroidBridge::RemoteBrowser::ParsedVersion ParsedVersion;
229 static bool IsVersionLower(const ParsedVersion& left,
230 const ParsedVersion& right) {
231 return std::lexicographical_compare(
232 left.begin(), left.end(), right.begin(), right.end());
235 static bool IsPortForwardingSupported(const ParsedVersion& version) {
236 return !version.empty() && version[0] >= kMinVersionPortForwarding;
239 static scoped_refptr<DevToolsAndroidBridge::RemoteBrowser>
240 FindBestBrowserForTethering(
241 const DevToolsAndroidBridge::RemoteBrowsers browsers) {
242 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> best_browser;
243 ParsedVersion newest_version;
244 for (DevToolsAndroidBridge::RemoteBrowsers::const_iterator it =
245 browsers.begin(); it != browsers.end(); ++it) {
246 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser = *it;
247 ParsedVersion current_version = browser->GetParsedVersion();
248 if (IsPortForwardingSupported(current_version) &&
249 IsVersionLower(newest_version, current_version)) {
250 best_browser = browser;
251 newest_version = current_version;
254 return best_browser;
257 } // namespace
259 class PortForwardingController::Connection
260 : public AndroidDeviceManager::AndroidWebSocket::Delegate {
261 public:
262 Connection(PortForwardingController* controller,
263 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
264 const ForwardingMap& forwarding_map);
265 ~Connection() override;
267 const PortStatusMap& GetPortStatusMap();
269 void UpdateForwardingMap(const ForwardingMap& new_forwarding_map);
271 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser() {
272 return browser_;
275 private:
276 friend struct content::BrowserThread::DeleteOnThread<
277 content::BrowserThread::UI>;
278 friend class base::DeleteHelper<Connection>;
280 typedef std::map<int, std::string> ForwardingMap;
281 typedef base::Callback<void(PortStatus)> CommandCallback;
282 typedef std::map<int, CommandCallback> CommandCallbackMap;
284 void SerializeChanges(const std::string& method,
285 const ForwardingMap& old_map,
286 const ForwardingMap& new_map);
288 void SendCommand(const std::string& method, int port);
289 bool ProcessResponse(const std::string& json);
291 void ProcessBindResponse(int port, PortStatus status);
292 void ProcessUnbindResponse(int port, PortStatus status);
294 static void UpdateSocketCountOnHandlerThread(
295 base::WeakPtr<Connection> weak_connection, int port, int increment);
296 void UpdateSocketCount(int port, int increment);
298 // DevToolsAndroidBridge::AndroidWebSocket::Delegate implementation:
299 void OnSocketOpened() override;
300 void OnFrameRead(const std::string& message) override;
301 void OnSocketClosed() override;
303 PortForwardingController* controller_;
304 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser_;
305 scoped_ptr<AndroidDeviceManager::AndroidWebSocket> web_socket_;
306 int command_id_;
307 bool connected_;
308 ForwardingMap forwarding_map_;
309 CommandCallbackMap pending_responses_;
310 PortStatusMap port_status_;
311 base::WeakPtrFactory<Connection> weak_factory_;
313 DISALLOW_COPY_AND_ASSIGN(Connection);
316 PortForwardingController::Connection::Connection(
317 PortForwardingController* controller,
318 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser,
319 const ForwardingMap& forwarding_map)
320 : controller_(controller),
321 browser_(browser),
322 command_id_(0),
323 connected_(false),
324 forwarding_map_(forwarding_map),
325 weak_factory_(this) {
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327 controller_->registry_[browser->serial()] = this;
328 scoped_refptr<AndroidDeviceManager::Device> device(
329 controller_->bridge_->FindDevice(browser->serial()));
330 DCHECK(device.get());
331 web_socket_.reset(
332 device->CreateWebSocket(browser->socket(),
333 kDevToolsRemoteBrowserTarget, this));
336 PortForwardingController::Connection::~Connection() {
337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338 DCHECK(controller_->registry_.find(browser_->serial()) !=
339 controller_->registry_.end());
340 controller_->registry_.erase(browser_->serial());
343 void PortForwardingController::Connection::UpdateForwardingMap(
344 const ForwardingMap& new_forwarding_map) {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
346 if (connected_) {
347 SerializeChanges(tethering::unbind::kName,
348 new_forwarding_map, forwarding_map_);
349 SerializeChanges(tethering::bind::kName,
350 forwarding_map_, new_forwarding_map);
352 forwarding_map_ = new_forwarding_map;
355 void PortForwardingController::Connection::SerializeChanges(
356 const std::string& method,
357 const ForwardingMap& old_map,
358 const ForwardingMap& new_map) {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360 for (ForwardingMap::const_iterator new_it(new_map.begin());
361 new_it != new_map.end(); ++new_it) {
362 int port = new_it->first;
363 const std::string& location = new_it->second;
364 ForwardingMap::const_iterator old_it = old_map.find(port);
365 if (old_it != old_map.end() && old_it->second == location)
366 continue; // The port points to the same location in both configs, skip.
368 SendCommand(method, port);
372 void PortForwardingController::Connection::SendCommand(
373 const std::string& method, int port) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375 scoped_ptr<base::DictionaryValue> params(new base::DictionaryValue);
376 if (method == tethering::bind::kName) {
377 params->SetInteger(tethering::bind::kParamPort, port);
378 } else {
379 DCHECK_EQ(tethering::unbind::kName, method);
380 params->SetInteger(tethering::unbind::kParamPort, port);
382 int id = ++command_id_;
384 if (method == tethering::bind::kName) {
385 pending_responses_[id] =
386 base::Bind(&Connection::ProcessBindResponse,
387 base::Unretained(this), port);
388 #if defined(DEBUG_DEVTOOLS)
389 port_status_[port] = kStatusConnecting;
390 #endif // defined(DEBUG_DEVTOOLS)
391 } else {
392 PortStatusMap::iterator it = port_status_.find(port);
393 if (it != port_status_.end() && it->second == kStatusError) {
394 // The bind command failed on this port, do not attempt unbind.
395 port_status_.erase(it);
396 return;
399 pending_responses_[id] =
400 base::Bind(&Connection::ProcessUnbindResponse,
401 base::Unretained(this), port);
402 #if defined(DEBUG_DEVTOOLS)
403 port_status_[port] = kStatusDisconnecting;
404 #endif // defined(DEBUG_DEVTOOLS)
407 web_socket_->SendFrame(
408 DevToolsProtocol::SerializeCommand(id, method, params.Pass()));
411 bool PortForwardingController::Connection::ProcessResponse(
412 const std::string& message) {
413 int id = 0;
414 int error_code = 0;
415 if (!DevToolsProtocol::ParseResponse(message, &id, &error_code))
416 return false;
418 CommandCallbackMap::iterator it = pending_responses_.find(id);
419 if (it == pending_responses_.end())
420 return false;
422 it->second.Run(error_code ? kStatusError : kStatusOK);
423 pending_responses_.erase(it);
424 return true;
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())
436 return;
437 if (status == kStatusError)
438 it->second = status;
439 else
440 port_status_.erase(it);
443 // static
444 void PortForwardingController::Connection::UpdateSocketCountOnHandlerThread(
445 base::WeakPtr<Connection> weak_connection, int port, int increment) {
446 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
447 base::Bind(&Connection::UpdateSocketCount,
448 weak_connection, port, increment));
451 void PortForwardingController::Connection::UpdateSocketCount(
452 int port, int increment) {
453 #if defined(DEBUG_DEVTOOLS)
454 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
455 PortStatusMap::iterator it = port_status_.find(port);
456 if (it == port_status_.end())
457 return;
458 if (it->second < 0 || (it->second == 0 && increment < 0))
459 return;
460 it->second += increment;
461 #endif // defined(DEBUG_DEVTOOLS)
464 const PortForwardingController::PortStatusMap&
465 PortForwardingController::Connection::GetPortStatusMap() {
466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
467 return port_status_;
470 void PortForwardingController::Connection::OnSocketOpened() {
471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
472 connected_ = true;
473 SerializeChanges(tethering::bind::kName, ForwardingMap(), forwarding_map_);
476 void PortForwardingController::Connection::OnSocketClosed() {
477 delete this;
480 void PortForwardingController::Connection::OnFrameRead(
481 const std::string& message) {
482 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
483 if (ProcessResponse(message))
484 return;
486 std::string method;
487 scoped_ptr<base::DictionaryValue> params;
488 if (!DevToolsProtocol::ParseNotification(message, &method, &params))
489 return;
491 if (method != tethering::accepted::kName || !params)
492 return;
494 int port;
495 std::string connection_id;
496 if (!params->GetInteger(tethering::accepted::kParamPort, &port) ||
497 !params->GetString(tethering::accepted::kParamConnectionId,
498 &connection_id))
499 return;
501 std::map<int, std::string>::iterator it = forwarding_map_.find(port);
502 if (it == forwarding_map_.end())
503 return;
505 std::string location = it->second;
506 std::vector<std::string> tokens;
507 Tokenize(location, ":", &tokens);
508 int destination_port = 0;
509 if (tokens.size() != 2 || !base::StringToInt(tokens[1], &destination_port))
510 return;
511 std::string destination_host = tokens[0];
513 SocketTunnel::CounterCallback callback =
514 base::Bind(&Connection::UpdateSocketCountOnHandlerThread,
515 weak_factory_.GetWeakPtr(), port);
517 scoped_refptr<AndroidDeviceManager::Device> device(
518 controller_->bridge_->FindDevice(browser_->serial()));
519 DCHECK(device.get());
520 device->OpenSocket(
521 connection_id.c_str(),
522 base::Bind(&SocketTunnel::StartTunnel,
523 destination_host,
524 destination_port,
525 callback));
528 PortForwardingController::PortForwardingController(
529 Profile* profile,
530 DevToolsAndroidBridge* bridge)
531 : profile_(profile),
532 bridge_(bridge),
533 pref_service_(profile->GetPrefs()) {
534 pref_change_registrar_.Init(pref_service_);
535 base::Closure callback = base::Bind(
536 &PortForwardingController::OnPrefsChange, base::Unretained(this));
537 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingEnabled, callback);
538 pref_change_registrar_.Add(prefs::kDevToolsPortForwardingConfig, callback);
539 OnPrefsChange();
542 PortForwardingController::~PortForwardingController() {}
544 PortForwardingController::ForwardingStatus
545 PortForwardingController::DeviceListChanged(
546 const DevToolsAndroidBridge::RemoteDevices& devices) {
547 ForwardingStatus status;
548 if (forwarding_map_.empty())
549 return status;
551 for (const auto& device : devices) {
552 if (!device->is_connected())
553 continue;
554 Registry::iterator rit = registry_.find(device->serial());
555 if (rit == registry_.end()) {
556 scoped_refptr<DevToolsAndroidBridge::RemoteBrowser> browser(
557 FindBestBrowserForTethering(device->browsers()));
558 if (browser.get())
559 new Connection(this, browser, forwarding_map_);
560 } else {
561 status.push_back(std::make_pair(rit->second->browser(),
562 rit->second->GetPortStatusMap()));
565 return status;
568 void PortForwardingController::OnPrefsChange() {
569 forwarding_map_.clear();
571 if (pref_service_->GetBoolean(prefs::kDevToolsPortForwardingEnabled)) {
572 const base::DictionaryValue* dict =
573 pref_service_->GetDictionary(prefs::kDevToolsPortForwardingConfig);
574 for (base::DictionaryValue::Iterator it(*dict);
575 !it.IsAtEnd(); it.Advance()) {
576 int port_num;
577 std::string location;
578 if (base::StringToInt(it.key(), &port_num) &&
579 dict->GetString(it.key(), &location))
580 forwarding_map_[port_num] = location;
584 if (!forwarding_map_.empty()) {
585 UpdateConnections();
586 } else {
587 std::vector<Connection*> registry_copy;
588 for (Registry::iterator it = registry_.begin();
589 it != registry_.end(); ++it) {
590 registry_copy.push_back(it->second);
592 STLDeleteElements(&registry_copy);
596 void PortForwardingController::UpdateConnections() {
597 for (Registry::iterator it = registry_.begin(); it != registry_.end(); ++it)
598 it->second->UpdateForwardingMap(forwarding_map_);