OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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/test/chromedriver/net/websocket.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/memory/scoped_vector.h" | |
9 #include "base/rand_util.h" | |
10 #include "base/sha1.h" | |
11 #include "base/string_split.h" | |
12 #include "base/stringprintf.h" | |
13 #include "net/base/io_buffer.h" | |
14 #include "net/http/http_response_headers.h" | |
15 #include "net/http/http_util.h" | |
16 #include "net/url_request/url_request_context_getter.h" | |
17 #include "net/websockets/websocket_frame.h" | |
18 #include "net/websockets/websocket_frame_parser.h" | |
19 #include "net/websockets/websocket_job.h" | |
20 | |
21 WebSocket::WebSocket( | |
22 net::URLRequestContextGetter* context_getter, | |
23 const GURL& url, | |
24 WebSocketListener* listener) | |
25 : context_getter_(context_getter), | |
26 url_(url), | |
27 listener_(listener), | |
28 connected_(false) { | |
29 net::WebSocketJob::EnsureInit(); | |
30 web_socket_ = new net::WebSocketJob(this); | |
31 } | |
32 | |
33 WebSocket::~WebSocket() { | |
34 CHECK(thread_checker_.CalledOnValidThread()); | |
35 web_socket_->Close(); | |
36 web_socket_->DetachDelegate(); | |
37 } | |
38 | |
39 void WebSocket::Connect(const net::CompletionCallback& callback) { | |
40 CHECK(thread_checker_.CalledOnValidThread()); | |
41 CHECK_EQ(net::WebSocketJob::INITIALIZED, web_socket_->state()); | |
42 | |
43 connect_callback_ = callback; | |
44 | |
45 scoped_refptr<net::SocketStream> socket = new net::SocketStream( | |
46 url_, web_socket_); | |
47 socket->set_context(context_getter_->GetURLRequestContext()); | |
48 | |
49 web_socket_->InitSocketStream(socket); | |
50 web_socket_->Connect(); | |
51 } | |
52 | |
53 bool WebSocket::Send(const std::string& message) { | |
54 CHECK(thread_checker_.CalledOnValidThread()); | |
55 | |
56 net::WebSocketFrameHeader header; | |
57 header.final = true; | |
58 header.reserved1 = false; | |
59 header.reserved2 = false; | |
60 header.reserved3 = false; | |
61 header.opcode = net::WebSocketFrameHeader::kOpCodeText; | |
62 header.masked = true; | |
63 header.payload_length = message.length(); | |
64 int header_size = net::GetWebSocketFrameHeaderSize(header); | |
65 net::WebSocketMaskingKey masking_key = net::GenerateWebSocketMaskingKey(); | |
66 std::string header_str; | |
67 header_str.resize(header_size); | |
68 CHECK_EQ(header_size, net::WriteWebSocketFrameHeader( | |
69 header, &masking_key, &header_str[0], header_str.length())); | |
70 | |
71 std::string masked_message = message; | |
72 net::MaskWebSocketFramePayload( | |
73 masking_key, 0, &masked_message[0], masked_message.length()); | |
74 std::string data = header_str + masked_message; | |
75 return web_socket_->SendData(data.c_str(), data.length()); | |
76 } | |
77 | |
78 void WebSocket::OnConnected(net::SocketStream* socket, | |
79 int max_pending_send_allowed) { | |
80 CHECK(base::Base64Encode(base::RandBytesAsString(16), &sec_key_)); | |
Takashi Toyoshima
2012/11/28 09:11:30
Ah, this is smarter than my suggestion :)
kkania
2012/11/28 16:02:12
Done.
| |
81 std::string handshake = base::StringPrintf( | |
82 "GET %s HTTP/1.1\r\n" | |
83 "Host: %s\r\n" | |
84 "Upgrade: websocket\r\n" | |
85 "Connection: Upgrade\r\n" | |
86 "Sec-WebSocket-Key: %s\r\n" | |
87 "Sec-WebSocket-Version: 13\r\n" | |
88 "Pragma: no-cache\r\n" | |
89 "Cache-Control: no-cache\r\n" | |
90 "\r\n", | |
91 url_.path().c_str(), | |
92 url_.host().c_str(), | |
93 sec_key_.c_str()); | |
94 if (!web_socket_->SendData(handshake.c_str(), handshake.length())) | |
95 OnConnectFinished(net::ERR_FAILED); | |
96 } | |
97 | |
98 void WebSocket::OnSentData(net::SocketStream* socket, | |
99 int amount_sent) {} | |
100 | |
101 void WebSocket::OnReceivedData(net::SocketStream* socket, | |
102 const char* data, int len) { | |
103 net::WebSocketJob::State state = web_socket_->state(); | |
104 if (!connect_callback_.is_null()) { | |
Takashi Toyoshima
2012/11/28 09:11:30
Can you add some comment here, as WebSocketJob gua
kkania
2012/11/28 16:02:12
Good idea. Done.
| |
105 const char kMagicKey[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
106 std::string websocket_accept; | |
107 CHECK(base::Base64Encode(base::SHA1HashString(sec_key_ + kMagicKey), | |
108 &websocket_accept)); | |
109 scoped_refptr<net::HttpResponseHeaders> headers( | |
110 new net::HttpResponseHeaders( | |
111 net::HttpUtil::AssembleRawHeaders(data, len))); | |
112 if (headers->response_code() != 101 || | |
113 !headers->HasHeaderValue("Upgrade", "WebSocket") || | |
114 !headers->HasHeaderValue("Connection", "Upgrade") || | |
115 !headers->HasHeaderValue("Sec-WebSocket-Accept", websocket_accept)) { | |
116 OnConnectFinished(net::ERR_FAILED); | |
117 return; | |
118 } | |
119 OnConnectFinished( | |
120 state == net::WebSocketJob::OPEN ? net::OK : net::ERR_FAILED); | |
121 } else if (connected_) { | |
122 incoming_msg_buf_ += std::string(data, len); | |
123 ScopedVector<net::WebSocketFrameChunk> frame_chunks; | |
124 net::WebSocketFrameParser parser; | |
Takashi Toyoshima
2012/11/28 09:11:30
I suggest you make net::WebSocketFrameParser a mem
kkania
2012/11/28 16:02:12
Ah, I wasn't aware of that. Thanks! Done.
| |
125 CHECK(parser.Decode(incoming_msg_buf_.c_str(), | |
126 incoming_msg_buf_.length(), | |
127 &frame_chunks)); | |
128 int processed_data = 0; | |
129 std::string message; | |
130 int header = 0; | |
131 for (size_t i = 0; i < frame_chunks.size(); ++i) { | |
132 scoped_refptr<net::IOBufferWithSize> buffer = frame_chunks[i]->data; | |
133 if (buffer) | |
134 message += std::string(buffer->data(), buffer->size()); | |
135 if (frame_chunks[i]->final_chunk) { | |
136 processed_data += frame_chunks[header]->header->payload_length + | |
137 net::GetWebSocketFrameHeaderSize(*frame_chunks[header]->header); | |
138 listener_->OnMessageReceived(message); | |
139 message.clear(); | |
140 header = i + 1; | |
141 } | |
142 } | |
143 if (processed_data) | |
144 incoming_msg_buf_.erase(0, processed_data); | |
145 } | |
146 } | |
147 | |
148 void WebSocket::OnClose(net::SocketStream* socket) { | |
149 if (!connect_callback_.is_null()) | |
150 OnConnectFinished(net::ERR_CONNECTION_CLOSED); | |
151 else | |
152 listener_->OnClose(); | |
153 } | |
154 | |
155 void WebSocket::OnConnectFinished(net::Error error) { | |
156 if (error == net::OK) | |
157 connected_ = true; | |
158 net::CompletionCallback temp = connect_callback_; | |
159 connect_callback_.Reset(); | |
160 temp.Run(error); | |
161 } | |
OLD | NEW |