Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(377)

Side by Side Diff: net/server/web_socket.cc

Issue 7540023: DevTools: no way to remote debug using ToT build as a client. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review comments addressed. Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« net/server/web_socket.h ('K') | « net/server/web_socket.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "net/server/web_socket.h"
6
7 #include "base/base64.h"
8 #include "base/rand_util.h"
9 #include "base/logging.h"
10 #include "base/md5.h"
11 #include "base/sha1.h"
12 #include "base/string_number_conversions.h"
13 #include "base/stringprintf.h"
14 #include "net/server/http_connection.h"
15 #include "net/server/http_server_request_info.h"
16
17 #if defined(OS_WIN)
18 #include <winsock2.h>
19 #else
20 #include <arpa/inet.h>
21 #endif
22
23 #include <limits>
24
25 namespace net {
26
27 namespace {
28
29 static uint32 WebSocketKeyFingerprint(const std::string& str) {
30 std::string result;
31 const char* pChar = str.c_str();
eroman 2011/08/02 19:01:33 please change this name to match styleguide.
32 int length = str.length();
33 int spaces = 0;
34 for (int i = 0; i < length; ++i) {
35 if (pChar[i] >= '0' && pChar[i] <= '9')
36 result.append(&pChar[i], 1);
37 else if (pChar[i] == ' ')
38 spaces++;
39 }
40 if (spaces == 0)
41 return 0;
42 int64 number = 0;
43 if (!base::StringToInt64(result, &number))
44 return 0;
45 return htonl(static_cast<uint32>(number / spaces));
46 }
47
48 class WebSocketHixie76 : public net::WebSocket {
49 public:
50 static net::WebSocket* Create(HttpConnection* connection,
51 const HttpServerRequestInfo& request,
52 size_t* pos) {
53 if (connection->recv_data().length() < *pos + kWebSocketHandshakeBodyLen)
54 return NULL;
55 return new WebSocketHixie76(connection, request, pos);
56 }
57
58 virtual void Accept(const HttpServerRequestInfo& request) {
59 std::string key1 = request.GetHeaderValue("Sec-WebSocket-Key1");
60 std::string key2 = request.GetHeaderValue("Sec-WebSocket-Key2");
61
62 uint32 fp1 = WebSocketKeyFingerprint(key1);
63 uint32 fp2 = WebSocketKeyFingerprint(key2);
64
65 char data[16];
66 memcpy(data, &fp1, 4);
67 memcpy(data + 4, &fp2, 4);
68 memcpy(data + 8, &key3_[0], 8);
69
70 base::MD5Digest digest;
71 base::MD5Sum(data, 16, &digest);
72
73 std::string origin = request.GetHeaderValue("Origin");
74 std::string host = request.GetHeaderValue("Host");
75 std::string location = "ws://" + host + request.path;
76 connection_->Send(base::StringPrintf(
77 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
78 "Upgrade: WebSocket\r\n"
79 "Connection: Upgrade\r\n"
80 "Sec-WebSocket-Origin: %s\r\n"
81 "Sec-WebSocket-Location: %s\r\n"
82 "\r\n",
83 origin.c_str(),
84 location.c_str()));
85 connection_->Send(reinterpret_cast<char*>(digest.a), 16);
86 }
87
88 virtual ParseResult Read(std::string* message) {
89 DCHECK(message);
90 const std::string& data = connection_->recv_data();
91 if (data[0])
92 return FRAME_ERROR;
93
94 size_t pos = data.find('\377', 1);
95 if (pos == std::string::npos)
96 return FRAME_INCOMPLETE;
97
98 std::string buffer(data.begin() + 1, data.begin() + pos);
99 message->swap(buffer);
100 connection_->Shift(pos + 1);
101
102 return FRAME_OK;
103 }
104
105 virtual void Send(const std::string& message) {
106 char message_start = 0;
107 char message_end = -1;
108 connection_->Send(&message_start, 1);
109 connection_->Send(message);
110 connection_->Send(&message_end, 1);
111 }
112
113 private:
114 static const int kWebSocketHandshakeBodyLen;
115
116 WebSocketHixie76(HttpConnection* connection,
117 const HttpServerRequestInfo& request,
118 size_t* pos) : WebSocket(connection) {
119 std::string key1 = request.GetHeaderValue("Sec-WebSocket-Key1");
120 std::string key2 = request.GetHeaderValue("Sec-WebSocket-Key2");
121
122 if (key1.empty()) {
123 connection->Send500("Invalid request format. "
124 "Sec-WebSocket-Key1 is empty or isn't specified.");
125 return;
126 }
127
128 if (key2.empty()) {
129 connection->Send500("Invalid request format. "
130 "Sec-WebSocket-Key2 is empty or isn't specified.");
131 return;
132 }
133
134 key3_ = connection->recv_data().substr(
135 *pos,
136 *pos + kWebSocketHandshakeBodyLen);
137 *pos += kWebSocketHandshakeBodyLen;
138 }
139
140 std::string key3_;
141
142 DISALLOW_COPY_AND_ASSIGN(WebSocketHixie76);
143 };
144
145 const int WebSocketHixie76::kWebSocketHandshakeBodyLen = 8;
146
147
148 // Constants for hybi-10 frame format.
149
150 typedef int OpCode;
151
152 const OpCode kOpCodeContinuation = 0x0;
153 const OpCode kOpCodeText = 0x1;
154 const OpCode kOpCodeBinary = 0x2;
155 const OpCode kOpCodeClose = 0x8;
156 const OpCode kOpCodePing = 0x9;
157 const OpCode kOpCodePong = 0xA;
158
159 const unsigned char kFinalBit = 0x80;
160 const unsigned char kReserved1Bit = 0x40;
161 const unsigned char kReserved2Bit = 0x20;
162 const unsigned char kReserved3Bit = 0x10;
163 const unsigned char kOpCodeMask = 0xF;
164 const unsigned char kMaskBit = 0x80;
165 const unsigned char kPayloadLengthMask = 0x7F;
166
167 const size_t kMaxSingleBytePayloadLength = 125;
168 const size_t kTwoBytePayloadLengthField = 126;
169 const size_t kEightBytePayloadLengthField = 127;
170 const size_t kMaskingKeyWidthInBytes = 4;
171
172 class WebSocketHybi10 : public WebSocket {
173 public:
174 static WebSocket* Create(HttpConnection* connection,
175 const HttpServerRequestInfo& request,
176 size_t* pos) {
177 std::string version = request.GetHeaderValue("Sec-WebSocket-Version");
178 if (version != "8")
179 return NULL;
180
181 std::string key = request.GetHeaderValue("Sec-WebSocket-Key");
182 if (key.empty()) {
183 connection->Send500("Invalid request format. "
184 "Sec-WebSocket-Key is empty or isn't specified.");
185 return NULL;
186 }
187 return new WebSocketHybi10(connection, request, pos);
188 }
189
190 virtual void Accept(const HttpServerRequestInfo& request) {
191 static const char* const web_socket_GUID =
eroman 2011/08/02 19:01:33 kWebSocketGuid
192 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
193 std::string key = request.GetHeaderValue("Sec-WebSocket-Key");
194 std::string data = base::StringPrintf("%s%s", key.c_str(), web_socket_GUID);
195 std::string encoded_hash;
196 base::Base64Encode(base::SHA1HashString(data), &encoded_hash);
197
198 std::string response = base::StringPrintf(
199 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
200 "Upgrade: WebSocket\r\n"
201 "Connection: Upgrade\r\n"
202 "Sec-WebSocket-Accept: %s\r\n"
203 "\r\n",
204 encoded_hash.c_str());
205 connection_->Send(response);
206 }
207
208 virtual ParseResult Read(std::string* message) {
209 size_t data_length = connection_->recv_data().length();
210 if (data_length < 2)
211 return FRAME_INCOMPLETE;
212
213 const char* p = connection_->recv_data().c_str();
214 const char* buffer_end = p + data_length;
215
216 unsigned char first_byte = *p++;
217 unsigned char second_byte = *p++;
218
219 final_ = first_byte & kFinalBit;
220 reserved1_ = first_byte & kReserved1Bit;
221 reserved2_ = first_byte & kReserved2Bit;
222 reserved3_ = first_byte & kReserved3Bit;
223 op_code_ = first_byte & kOpCodeMask;
224 masked_ = second_byte & kMaskBit;
225
226 CHECK(op_code_ == kOpCodeText);
eroman 2011/08/02 19:01:33 check_eq
227
228 uint64 payload_length64 = second_byte & kPayloadLengthMask;
229 if (payload_length64 > kMaxSingleBytePayloadLength) {
230 int extended_payload_length_size;
231 if (payload_length64 == kTwoBytePayloadLengthField)
232 extended_payload_length_size = 2;
233 else {
234 DCHECK(payload_length64 == kEightBytePayloadLengthField);
235 extended_payload_length_size = 8;
236 }
237 if (buffer_end - p < extended_payload_length_size)
238 return FRAME_INCOMPLETE;
239 payload_length64 = 0;
240 for (int i = 0; i < extended_payload_length_size; ++i) {
241 payload_length64 <<= 8;
242 payload_length64 |= static_cast<unsigned char>(*p++);
243 }
244 }
245
246 static const uint64 max_payload_length = 0x7FFFFFFFFFFFFFFFull;
247 size_t masking_key_length = masked_ ? kMaskingKeyWidthInBytes : 0;
248 static size_t max_length = std::numeric_limits<size_t>::max();
249 if (payload_length64 > max_payload_length ||
250 payload_length64 + masking_key_length > max_length) {
251 // WebSocket frame length too large.
252 return FRAME_ERROR;
253 }
254 payload_length_ = static_cast<size_t>(payload_length64);
255
256 size_t total_length = masking_key_length + payload_length_;
257 if (static_cast<size_t>(buffer_end - p) < total_length)
258 return FRAME_INCOMPLETE;
259
260 if (masked_) {
261 message->resize(payload_length_);
262 const char* masking_key = p;
263 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
264 for (size_t i = 0; i < payload_length_; ++i) // Unmask the payload.
265 (*message)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
eroman 2011/08/02 19:01:33 I only did a cursory review of this logic BTW
266 } else {
267 std::string buffer(p, p + payload_length_);
268 message->swap(buffer);
269 }
270
271 size_t pos = p + masking_key_length + payload_length_ -
272 connection_->recv_data().c_str();
273 connection_->Shift(pos);
274 return FRAME_OK;
275 }
276
277 virtual void Send(const std::string& message) {
278 std::vector<char> frame;
279 OpCode op_code = kOpCodeText;
280 size_t data_length = message.length();
281
282 frame.push_back(kFinalBit | op_code);
283 if (data_length <= kMaxSingleBytePayloadLength)
284 frame.push_back(kMaskBit | data_length);
285 else if (data_length <= 0xFFFF) {
286 frame.push_back(kMaskBit | kTwoBytePayloadLengthField);
287 frame.push_back((data_length & 0xFF00) >> 8);
288 frame.push_back(data_length & 0xFF);
289 } else {
290 frame.push_back(kMaskBit | kEightBytePayloadLengthField);
291 char extended_payload_length[8];
292 size_t remaining = data_length;
293 // Fill the length into extended_payload_length in the network byte order.
294 for (int i = 0; i < 8; ++i) {
295 extended_payload_length[7 - i] = remaining & 0xFF;
296 remaining >>= 8;
297 }
298 frame.insert(frame.end(),
299 extended_payload_length,
300 extended_payload_length + 8);
301 DCHECK(!remaining);
302 }
303
304 // Mask the frame.
305 size_t masking_key_start = frame.size();
306 // Add placeholder for masking key. Will be overwritten.
307 frame.resize(frame.size() + kMaskingKeyWidthInBytes);
308 size_t payload_start = frame.size();
309 const char* data = message.c_str();
310 frame.insert(frame.end(), data, data + data_length);
311
312 base::RandBytes(&frame[0] + masking_key_start,
313 kMaskingKeyWidthInBytes);
314 for (size_t i = 0; i < data_length; ++i) {
315 frame[payload_start + i] ^=
316 frame[masking_key_start + i % kMaskingKeyWidthInBytes];
317 }
318 connection_->Send(&frame[0], frame.size());
319 }
320
321 private:
322 WebSocketHybi10(HttpConnection* connection,
323 const HttpServerRequestInfo& request,
324 size_t* pos)
325 : WebSocket(connection),
326 op_code_(0),
327 final_(false),
328 reserved1_(false),
329 reserved2_(false),
330 reserved3_(false),
331 masked_(false),
332 payload_(0),
333 payload_length_(0),
334 frame_end_(0) {
335 }
336
337 OpCode op_code_;
338 bool final_;
339 bool reserved1_;
340 bool reserved2_;
341 bool reserved3_;
342 bool masked_;
343 const char* payload_;
344 size_t payload_length_;
345 const char* frame_end_;
346
347 DISALLOW_COPY_AND_ASSIGN(WebSocketHybi10);
348 };
349
350 } // anonymous namespace
351
352 WebSocket* WebSocket::CreateWebSocket(HttpConnection* connection,
353 const HttpServerRequestInfo& request,
354 size_t* pos) {
355 WebSocket* socket = WebSocketHybi10::Create(connection, request, pos);
356 if (socket)
357 return socket;
358
359 return WebSocketHixie76::Create(connection, request, pos);
360 }
361
362 WebSocket::WebSocket(HttpConnection* connection) : connection_(connection) {
363 }
364
365 } // namespace net
OLDNEW
« net/server/web_socket.h ('K') | « net/server/web_socket.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698