blob: b740497923e19dc3b715ae2d288d5b3bbe6ed9bf [file] [log] [blame]
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01001// Copyright (c) 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.
4
5#include "chrome/browser/devtools/adb_web_socket.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/rand_util.h"
9#include "base/strings/stringprintf.h"
10#include "content/public/browser/browser_thread.h"
11#include "net/base/net_errors.h"
12#include "net/server/web_socket.h"
13
14using content::BrowserThread;
15using net::WebSocket;
16
17const int kBufferSize = 16 * 1024;
18
19static const char kWebSocketUpgradeRequest[] = "GET %s HTTP/1.1\r\n"
20 "Upgrade: WebSocket\r\n"
21 "Connection: Upgrade\r\n"
22 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
23 "Sec-WebSocket-Version: 13\r\n"
24 "\r\n";
25
26AdbWebSocket::AdbWebSocket(
27 scoped_refptr<DevToolsAdbBridge::AndroidDevice> device,
28 const std::string& socket_name,
29 const std::string& url,
30 base::MessageLoop* adb_message_loop,
31 Delegate* delegate)
32 : device_(device),
33 socket_name_(socket_name),
34 url_(url),
35 adb_message_loop_(adb_message_loop),
36 delegate_(delegate) {
37 adb_message_loop_->PostTask(
38 FROM_HERE, base::Bind(&AdbWebSocket::ConnectOnHandlerThread, this));
39}
40
41void AdbWebSocket::Disconnect() {
42 adb_message_loop_->PostTask(
43 FROM_HERE,
44 base::Bind(&AdbWebSocket::DisconnectOnHandlerThread, this, false));
45 adb_message_loop_ = NULL;
46}
47
48void AdbWebSocket::SendFrame(const std::string& message) {
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
50 adb_message_loop_->PostTask(
51 FROM_HERE,
52 base::Bind(&AdbWebSocket::SendFrameOnHandlerThread, this, message));
53}
54
Ben Murdoch2385ea32013-08-06 11:01:04 +010055void AdbWebSocket::SendFrameOnHandlerThread(const std::string& message) {
56 int mask = base::RandInt(0, 0x7FFFFFFF);
57 std::string encoded_frame = WebSocket::EncodeFrameHybi17(message, mask);
58 request_buffer_ += encoded_frame;
59 if (request_buffer_.length() == encoded_frame.length())
60 SendPendingRequests(0);
61}
62
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010063AdbWebSocket::~AdbWebSocket() {}
64
65void AdbWebSocket::ConnectOnHandlerThread() {
Ben Murdochbb1529c2013-08-08 10:24:53 +010066 device_->HttpUpgrade(
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010067 socket_name_,
68 base::StringPrintf(kWebSocketUpgradeRequest, url_.c_str()),
69 base::Bind(&AdbWebSocket::ConnectedOnHandlerThread, this));
70}
71
72void AdbWebSocket::ConnectedOnHandlerThread(
73 int result, net::StreamSocket* socket) {
74 if (result != net::OK || socket == NULL) {
75 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
76 base::Bind(&AdbWebSocket::OnSocketClosed, this, true));
77 return;
78 }
79 socket_.reset(socket);
80 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
81 base::Bind(&AdbWebSocket::OnSocketOpened, this));
82 StartListeningOnHandlerThread();
83}
84
85void AdbWebSocket::StartListeningOnHandlerThread() {
86 scoped_refptr<net::IOBuffer> response_buffer =
87 new net::IOBuffer(kBufferSize);
88 int result = socket_->Read(
89 response_buffer.get(),
90 kBufferSize,
91 base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer));
92 if (result != net::ERR_IO_PENDING)
93 OnBytesRead(response_buffer, result);
94}
95
96void AdbWebSocket::OnBytesRead(
97 scoped_refptr<net::IOBuffer> response_buffer, int result) {
98 if (!socket_)
99 return;
100
101 if (result <= 0) {
102 DisconnectOnHandlerThread(true);
103 return;
104 }
105
106 std::string data = std::string(response_buffer->data(), result);
107 response_buffer_ += data;
108
109 int bytes_consumed;
110 std::string output;
111 WebSocket::ParseResult parse_result = WebSocket::DecodeFrameHybi17(
112 response_buffer_, false, &bytes_consumed, &output);
113
114 while (parse_result == WebSocket::FRAME_OK) {
115 response_buffer_ = response_buffer_.substr(bytes_consumed);
116 if (!delegate_ || !delegate_->ProcessIncomingMessage(output)) {
117 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
118 base::Bind(&AdbWebSocket::OnFrameRead, this, output));
119 }
120 parse_result = WebSocket::DecodeFrameHybi17(
121 response_buffer_, false, &bytes_consumed, &output);
122 }
123
124 if (parse_result == WebSocket::FRAME_ERROR ||
125 parse_result == WebSocket::FRAME_CLOSE) {
126 DisconnectOnHandlerThread(true);
127 return;
128 }
129
130 result = socket_->Read(
131 response_buffer.get(),
132 kBufferSize,
133 base::Bind(&AdbWebSocket::OnBytesRead, this, response_buffer));
134 if (result != net::ERR_IO_PENDING)
135 OnBytesRead(response_buffer, result);
136}
137
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100138void AdbWebSocket::SendPendingRequests(int result) {
139 if (!socket_)
140 return;
141 if (result < 0) {
142 DisconnectOnHandlerThread(true);
143 return;
144 }
145 request_buffer_ = request_buffer_.substr(result);
146 if (request_buffer_.empty())
147 return;
148
149 scoped_refptr<net::StringIOBuffer> buffer =
150 new net::StringIOBuffer(request_buffer_);
151 result = socket_->Write(buffer.get(), buffer->size(),
152 base::Bind(&AdbWebSocket::SendPendingRequests,
153 this));
154 if (result != net::ERR_IO_PENDING)
155 SendPendingRequests(result);
156}
157
158void AdbWebSocket::DisconnectOnHandlerThread(bool closed_by_device) {
159 if (!socket_)
160 return;
161 // Wipe out socket_ first since Disconnect can re-enter this method.
162 scoped_ptr<net::StreamSocket> socket(socket_.release());
163 socket->Disconnect();
164 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
165 base::Bind(&AdbWebSocket::OnSocketClosed, this, closed_by_device));
166}
167
168void AdbWebSocket::OnSocketOpened() {
169 delegate_->OnSocketOpened();
170}
171
172void AdbWebSocket::OnFrameRead(const std::string& message) {
173 delegate_->OnFrameRead(message);
174}
175
176void AdbWebSocket::OnSocketClosed(bool closed_by_device) {
177 delegate_->OnSocketClosed(closed_by_device);
178}