// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/devtools/adb_web_socket.h" #include "base/message_loop/message_loop.h" #include "base/rand_util.h" #include "base/strings/stringprintf.h" #include "content/public/browser/browser_thread.h" #include "net/base/net_errors.h" #include "net/server/web_socket.h" using content::BrowserThread; using net::WebSocket; const int kBufferSize = 16 * 1024; static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n" "Upgrade: WebSocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n"; AdbWebSocket::AdbWebSocket( scoped_refptr device, const std::string& socket_name, const std::string& url, base::MessageLoop* adb_message_loop, Delegate* delegate) : device_(device), socket_name_(socket_name), url_(url), adb_message_loop_(adb_message_loop), delegate_(delegate) { adb_message_loop_->PostTask( FROM_HERE, base::Bind(&AdbWebSocket::ConnectOnHandlerThread, this)); } void AdbWebSocket::Disconnect() { adb_message_loop_->PostTask( FROM_HERE, base::Bind(&AdbWebSocket::DisconnectOnHandlerThread, this, false)); adb_message_loop_ = NULL; } void AdbWebSocket::SendFrame(const std::string& message) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); adb_message_loop_->PostTask( FROM_HERE, base::Bind(&AdbWebSocket::SendFrameOnHandlerThread, this, message)); } void AdbWebSocket::SendFrameOnHandlerThread(const std::string& message) { int mask = base::RandInt(0, 0x7FFFFFFF); std::string encoded_frame = WebSocket::EncodeFrameHybi17(message, mask); request_buffer_ += encoded_frame; if (request_buffer_.length() == encoded_frame.length()) SendPendingRequests(0); } AdbWebSocket::~AdbWebSocket() {} void AdbWebSocket::ConnectOnHandlerThread() { device_->HttpUpgrade( socket_name_, base::StringPrintf(kWebSocketUpgradeRequest, url_.c_str()), base::Bind(&AdbWebSocket::ConnectedOnHandlerThread, this)); } void AdbWebSocket::ConnectedOnHandlerThread( int result, net::StreamSocket* socket) { if (result != net::OK || socket == NULL) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&AdbWebSocket::OnSocketClosed, this, true)); return; } socket_.reset(socket); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&AdbWebSocket::OnSocketOpened, this)); StartListeningOnHandlerThread(); } void AdbWebSocket::StartListeningOnHandlerThread() { scoped_refptr response_buffer = new net::IOBuffer(kBufferSize); int result = socket_->Read( response_buffer.get(), kBufferSize, base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer)); if (result != net::ERR_IO_PENDING) OnBytesRead(response_buffer, result); } void AdbWebSocket::OnBytesRead( scoped_refptr response_buffer, int result) { if (!socket_) return; if (result <= 0) { DisconnectOnHandlerThread(true); return; } std::string data = std::string(response_buffer->data(), result); response_buffer_ += data; int bytes_consumed; std::string output; WebSocket::ParseResult parse_result = WebSocket::DecodeFrameHybi17( response_buffer_, false, &bytes_consumed, &output); while (parse_result == WebSocket::FRAME_OK) { response_buffer_ = response_buffer_.substr(bytes_consumed); if (!delegate_ || !delegate_->ProcessIncomingMessage(output)) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&AdbWebSocket::OnFrameRead, this, output)); } parse_result = WebSocket::DecodeFrameHybi17( response_buffer_, false, &bytes_consumed, &output); } if (parse_result == WebSocket::FRAME_ERROR || parse_result == WebSocket::FRAME_CLOSE) { DisconnectOnHandlerThread(true); return; } result = socket_->Read( response_buffer.get(), kBufferSize, base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer)); if (result != net::ERR_IO_PENDING) OnBytesRead(response_buffer, result); } void AdbWebSocket::SendPendingRequests(int result) { if (!socket_) return; if (result < 0) { DisconnectOnHandlerThread(true); return; } request_buffer_ = request_buffer_.substr(result); if (request_buffer_.empty()) return; scoped_refptr buffer = new net::StringIOBuffer(request_buffer_); result = socket_->Write(buffer.get(), buffer->size(), base::Bind(&AdbWebSocket::SendPendingRequests, this)); if (result != net::ERR_IO_PENDING) SendPendingRequests(result); } void AdbWebSocket::DisconnectOnHandlerThread(bool closed_by_device) { if (!socket_) return; // Wipe out socket_ first since Disconnect can re-enter this method. scoped_ptr socket(socket_.release()); socket->Disconnect(); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind(&AdbWebSocket::OnSocketClosed, this, closed_by_device)); } void AdbWebSocket::OnSocketOpened() { delegate_->OnSocketOpened(); } void AdbWebSocket::OnFrameRead(const std::string& message) { delegate_->OnFrameRead(message); } void AdbWebSocket::OnSocketClosed(bool closed_by_device) { delegate_->OnSocketClosed(closed_by_device); }