blob: 0aff03977336046272874fe02f2ca825fee5c837 [file] [log] [blame]
// Copyright 2014 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 <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/devtools/device/android_device_manager.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/server/web_socket_encoder.h"
#include "net/socket/stream_socket.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
using content::BrowserThread;
using net::WebSocket;
namespace {
const int kBufferSize = 16 * 1024;
const char kCloseResponse[] = "\x88\x80\x2D\x0E\x1E\xFA";
net::NetworkTrafficAnnotationTag kAndroidWebSocketTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("android_web_socket", R"(
semantics {
sender: "Android Web Socket"
description:
"Remote debugging is supported over existing ADB (Android Debug "
"Bridge) connection, in addition to raw USB connection. This "
"socket talks to the local ADB daemon which routes debugging "
"traffic to a remote device."
trigger:
"A user connects to an Android device using remote debugging."
data: "Any data required for remote debugging."
destination: LOCAL
}
policy {
cookies_allowed: NO
setting:
"To use adb with a device connected over USB, you must enable USB "
"debugging in the device system settings, under Developer options."
policy_exception_justification:
"This is not a network request and is only used for remote "
"debugging."
})");
} // namespace
class AndroidDeviceManager::AndroidWebSocket::WebSocketImpl {
public:
WebSocketImpl(
scoped_refptr<base::SingleThreadTaskRunner> response_task_runner,
base::WeakPtr<AndroidWebSocket> weak_socket,
const std::string& extensions,
const std::string& body_head,
std::unique_ptr<net::StreamSocket> socket)
: response_task_runner_(response_task_runner),
weak_socket_(weak_socket),
socket_(std::move(socket)),
encoder_(net::WebSocketEncoder::CreateClient(extensions)),
response_buffer_(body_head),
weak_factory_(this) {
thread_checker_.DetachFromThread();
}
void StartListening() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(socket_);
scoped_refptr<net::IOBuffer> buffer =
base::MakeRefCounted<net::IOBuffer>(kBufferSize);
if (!response_buffer_.empty())
ProcessResponseBuffer(buffer);
else
Read(buffer);
}
void SendFrame(const std::string& message) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!socket_)
return;
int mask = base::RandInt(0, 0x7FFFFFFF);
std::string encoded_frame;
encoder_->EncodeFrame(message, mask, &encoded_frame);
SendData(encoded_frame);
}
base::WeakPtr<WebSocketImpl> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
void Read(scoped_refptr<net::IOBuffer> io_buffer) {
int result =
socket_->Read(io_buffer.get(), kBufferSize,
base::Bind(&WebSocketImpl::OnBytesRead,
weak_factory_.GetWeakPtr(), io_buffer));
if (result != net::ERR_IO_PENDING)
OnBytesRead(io_buffer, result);
}
void OnBytesRead(scoped_refptr<net::IOBuffer> io_buffer, int result) {
DCHECK(thread_checker_.CalledOnValidThread());
if (result <= 0) {
Disconnect();
return;
}
response_buffer_.append(io_buffer->data(), result);
ProcessResponseBuffer(io_buffer);
}
void ProcessResponseBuffer(scoped_refptr<net::IOBuffer> io_buffer) {
int bytes_consumed;
std::string output;
WebSocket::ParseResult parse_result = encoder_->DecodeFrame(
response_buffer_, &bytes_consumed, &output);
while (parse_result == WebSocket::FRAME_OK) {
response_buffer_ = response_buffer_.substr(bytes_consumed);
response_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AndroidWebSocket::OnFrameRead, weak_socket_, output));
parse_result = encoder_->DecodeFrame(
response_buffer_, &bytes_consumed, &output);
}
if (parse_result == WebSocket::FRAME_CLOSE)
SendData(kCloseResponse);
if (parse_result == WebSocket::FRAME_ERROR) {
Disconnect();
return;
}
Read(io_buffer);
}
void SendData(const std::string& data) {
request_buffer_ += data;
if (request_buffer_.length() == data.length())
SendPendingRequests(0);
}
void SendPendingRequests(int result) {
DCHECK(thread_checker_.CalledOnValidThread());
if (result < 0) {
Disconnect();
return;
}
request_buffer_ = request_buffer_.substr(result);
if (request_buffer_.empty())
return;
scoped_refptr<net::StringIOBuffer> buffer =
base::MakeRefCounted<net::StringIOBuffer>(request_buffer_);
result = socket_->Write(buffer.get(), buffer->size(),
base::Bind(&WebSocketImpl::SendPendingRequests,
weak_factory_.GetWeakPtr()),
kAndroidWebSocketTrafficAnnotation);
if (result != net::ERR_IO_PENDING)
SendPendingRequests(result);
}
void Disconnect() {
DCHECK(thread_checker_.CalledOnValidThread());
socket_.reset();
response_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AndroidWebSocket::OnSocketClosed, weak_socket_));
}
scoped_refptr<base::SingleThreadTaskRunner> response_task_runner_;
base::WeakPtr<AndroidWebSocket> weak_socket_;
std::unique_ptr<net::StreamSocket> socket_;
std::unique_ptr<net::WebSocketEncoder> encoder_;
std::string response_buffer_;
std::string request_buffer_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(WebSocketImpl);
base::WeakPtrFactory<WebSocketImpl> weak_factory_;
};
AndroidDeviceManager::AndroidWebSocket::AndroidWebSocket(
scoped_refptr<Device> device,
const std::string& socket_name,
const std::string& path,
Delegate* delegate)
: device_(device),
socket_impl_(nullptr, base::OnTaskRunnerDeleter(device->task_runner_)),
delegate_(delegate),
weak_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(delegate_);
DCHECK(device_);
device_->HttpUpgrade(
socket_name, path, net::WebSocketEncoder::kClientExtensions,
base::Bind(&AndroidWebSocket::Connected, weak_factory_.GetWeakPtr()));
}
AndroidDeviceManager::AndroidWebSocket::~AndroidWebSocket() = default;
void AndroidDeviceManager::AndroidWebSocket::SendFrame(
const std::string& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(socket_impl_);
DCHECK(device_);
device_->task_runner_->PostTask(
FROM_HERE, base::BindOnce(&WebSocketImpl::SendFrame,
socket_impl_->GetWeakPtr(), message));
}
void AndroidDeviceManager::AndroidWebSocket::Connected(
int result,
const std::string& extensions,
const std::string& body_head,
std::unique_ptr<net::StreamSocket> socket) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (result != net::OK || !socket.get()) {
OnSocketClosed();
return;
}
socket_impl_.reset(new WebSocketImpl(base::ThreadTaskRunnerHandle::Get(),
weak_factory_.GetWeakPtr(), extensions,
body_head, std::move(socket)));
device_->task_runner_->PostTask(FROM_HERE,
base::BindOnce(&WebSocketImpl::StartListening,
socket_impl_->GetWeakPtr()));
delegate_->OnSocketOpened();
}
void AndroidDeviceManager::AndroidWebSocket::OnFrameRead(
const std::string& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
delegate_->OnFrameRead(message);
}
void AndroidDeviceManager::AndroidWebSocket::OnSocketClosed() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
delegate_->OnSocketClosed();
}
AndroidDeviceManager::AndroidWebSocket*
AndroidDeviceManager::Device::CreateWebSocket(
const std::string& socket_name,
const std::string& path,
AndroidWebSocket::Delegate* delegate) {
return new AndroidWebSocket(this, socket_name, path, delegate);
}