Chromium Code Reviews| OLD | NEW |
|---|---|
| (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_server_request_info.h" | |
| 15 | |
| 16 #if defined(OS_WIN) | |
| 17 #include <winsock2.h> | |
| 18 #else | |
| 19 #include <arpa/inet.h> | |
| 20 #endif | |
| 21 | |
| 22 #include <limits> | |
| 23 | |
| 24 namespace net { | |
| 25 | |
| 26 WebSocket::WebSocket(Connection* connection) | |
| 27 : connection_(connection) { | |
| 28 } | |
| 29 | |
| 30 static std::string GetHeaderValue( | |
| 31 const HttpServerRequestInfo& request, | |
| 32 const std::string& header_name) { | |
| 33 HttpServerRequestInfo::HeadersMap::iterator it = | |
| 34 request.headers.find(header_name); | |
| 35 if (it != request.headers.end()) | |
| 36 return it->second; | |
| 37 return ""; | |
| 38 } | |
| 39 | |
| 40 class WebSocketHixie76 : public WebSocket { | |
| 41 public: | |
| 42 static WebSocket* create(Connection* connection, | |
| 43 const HttpServerRequestInfo& request, | |
| 44 size_t* pos); | |
| 45 | |
| 46 virtual void Accept(const HttpServerRequestInfo& request); | |
| 47 virtual ParseResult Read(std::string* message); | |
| 48 virtual void Send(const std::string& message); | |
| 49 | |
|
eroman
2011/08/02 18:46:41
Note that virtual dtor is missing here, so this wi
| |
| 50 private: | |
| 51 WebSocketHixie76(Connection* connection, | |
| 52 const HttpServerRequestInfo& request, | |
| 53 size_t* pos); | |
| 54 static const int kWebSocketHandshakeBodyLen; | |
| 55 std::string key3_; | |
| 56 }; | |
| 57 | |
| 58 const int WebSocketHixie76::kWebSocketHandshakeBodyLen = 8; | |
| 59 | |
| 60 WebSocket* WebSocketHixie76::create(Connection* connection, | |
| 61 const HttpServerRequestInfo& request, | |
| 62 size_t* pos) { | |
| 63 if (connection->RecvData().length() < *pos + kWebSocketHandshakeBodyLen) | |
| 64 return NULL; | |
| 65 return new WebSocketHixie76(connection, request, pos); | |
| 66 } | |
| 67 | |
| 68 WebSocketHixie76::WebSocketHixie76(Connection* connection, | |
| 69 const HttpServerRequestInfo& request, | |
| 70 size_t* pos) | |
| 71 : WebSocket(connection) { | |
| 72 std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1"); | |
| 73 std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2"); | |
| 74 | |
| 75 if (key1.empty()) { | |
| 76 connection->Send500("Invalid request format. " | |
| 77 "Sec-WebSocket-Key1 is empty or isn't specified."); | |
| 78 return; | |
| 79 } | |
| 80 | |
| 81 if (key2.empty()) { | |
| 82 connection->Send500("Invalid request format. " | |
| 83 "Sec-WebSocket-Key2 is empty or isn't specified."); | |
| 84 return; | |
| 85 } | |
| 86 | |
| 87 key3_ = connection->RecvData().substr( | |
| 88 *pos, | |
| 89 *pos + kWebSocketHandshakeBodyLen); | |
| 90 *pos += kWebSocketHandshakeBodyLen; | |
| 91 } | |
| 92 | |
| 93 static uint32 WebSocketKeyFingerprint(const std::string& str) { | |
| 94 std::string result; | |
| 95 const char* pChar = str.c_str(); | |
| 96 int length = str.length(); | |
| 97 int spaces = 0; | |
| 98 for (int i = 0; i < length; ++i) { | |
| 99 if (pChar[i] >= '0' && pChar[i] <= '9') | |
| 100 result.append(&pChar[i], 1); | |
| 101 else if (pChar[i] == ' ') | |
| 102 spaces++; | |
| 103 } | |
| 104 if (spaces == 0) | |
| 105 return 0; | |
| 106 int64 number = 0; | |
| 107 if (!base::StringToInt64(result, &number)) | |
| 108 return 0; | |
| 109 return htonl(static_cast<uint32>(number / spaces)); | |
| 110 } | |
| 111 | |
| 112 void WebSocketHixie76::Accept(const HttpServerRequestInfo& request) { | |
| 113 std::string key1 = GetHeaderValue(request, "Sec-WebSocket-Key1"); | |
| 114 std::string key2 = GetHeaderValue(request, "Sec-WebSocket-Key2"); | |
| 115 | |
| 116 uint32 fp1 = WebSocketKeyFingerprint(key1); | |
| 117 uint32 fp2 = WebSocketKeyFingerprint(key2); | |
| 118 | |
| 119 char data[16]; | |
| 120 memcpy(data, &fp1, 4); | |
| 121 memcpy(data + 4, &fp2, 4); | |
| 122 memcpy(data + 8, &key3_[0], 8); | |
| 123 | |
| 124 base::MD5Digest digest; | |
| 125 base::MD5Sum(data, 16, &digest); | |
| 126 | |
| 127 std::string origin = GetHeaderValue(request, "Origin"); | |
| 128 std::string host = GetHeaderValue(request, "Host"); | |
| 129 std::string location = "ws://" + host + request.path; | |
| 130 connection_->Send(base::StringPrintf( | |
| 131 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" | |
| 132 "Upgrade: WebSocket\r\n" | |
| 133 "Connection: Upgrade\r\n" | |
| 134 "Sec-WebSocket-Origin: %s\r\n" | |
| 135 "Sec-WebSocket-Location: %s\r\n" | |
| 136 "\r\n", | |
| 137 origin.c_str(), | |
| 138 location.c_str())); | |
| 139 connection_->Send(reinterpret_cast<char*>(digest.a), 16); | |
| 140 } | |
| 141 | |
| 142 WebSocket::ParseResult WebSocketHixie76::Read(std::string* message) { | |
| 143 DCHECK(message); | |
| 144 const std::string& data = connection_->RecvData(); | |
| 145 if (data[0]) | |
| 146 return FrameError; | |
| 147 | |
| 148 size_t pos = data.find('\377', 1); | |
| 149 if (pos == std::string::npos) | |
| 150 return FrameIncomplete; | |
| 151 | |
| 152 std::string buffer(data.begin() + 1, data.begin() + pos); | |
| 153 message->swap(buffer); | |
| 154 connection_->Shift(pos + 1); | |
| 155 | |
| 156 return FrameOK; | |
| 157 } | |
| 158 | |
| 159 void WebSocketHixie76::Send(const std::string& message) { | |
| 160 char message_start = 0; | |
| 161 char message_end = -1; | |
| 162 connection_->Send(&message_start, 1); | |
| 163 connection_->Send(message); | |
| 164 connection_->Send(&message_end, 1); | |
| 165 } | |
| 166 | |
| 167 class WebSocketHybi10 : public WebSocket { | |
| 168 public: | |
| 169 | |
|
eroman
2011/08/02 18:46:41
Remove space.
| |
| 170 typedef int OpCode; | |
| 171 | |
| 172 static WebSocket* create(Connection* connection, | |
|
eroman
2011/08/02 18:46:41
Create
| |
| 173 const HttpServerRequestInfo& request, | |
| 174 size_t* pos); | |
| 175 virtual void Accept(const HttpServerRequestInfo& request); | |
| 176 virtual ParseResult Read(std::string* message); | |
| 177 virtual void Send(const std::string& message); | |
| 178 | |
| 179 private: | |
| 180 WebSocketHybi10(Connection* connection, | |
| 181 const HttpServerRequestInfo& request, | |
| 182 size_t* pos); | |
| 183 | |
| 184 OpCode op_code_; | |
| 185 bool final_; | |
| 186 bool reserved1_; | |
| 187 bool reserved2_; | |
| 188 bool reserved3_; | |
| 189 bool masked_; | |
| 190 const char* payload_; | |
| 191 size_t payload_length_; | |
| 192 const char* frame_end_; | |
| 193 }; | |
| 194 | |
| 195 namespace { | |
| 196 | |
| 197 // Constants for hybi-10 frame format. | |
| 198 const WebSocketHybi10::OpCode kOpCodeContinuation = 0x0; | |
| 199 const WebSocketHybi10::OpCode kOpCodeText = 0x1; | |
| 200 const WebSocketHybi10::OpCode kOpCodeBinary = 0x2; | |
| 201 const WebSocketHybi10::OpCode kOpCodeClose = 0x8; | |
| 202 const WebSocketHybi10::OpCode kOpCodePing = 0x9; | |
| 203 const WebSocketHybi10::OpCode kOpCodePong = 0xA; | |
| 204 | |
| 205 const unsigned char kFinalBit = 0x80; | |
| 206 const unsigned char kReserved1Bit = 0x40; | |
| 207 const unsigned char kReserved2Bit = 0x20; | |
| 208 const unsigned char kReserved3Bit = 0x10; | |
| 209 const unsigned char kOpCodeMask = 0xF; | |
| 210 const unsigned char kMaskBit = 0x80; | |
| 211 const unsigned char kPayloadLengthMask = 0x7F; | |
| 212 | |
| 213 const size_t kMaxSingleBytePayloadLength = 125; | |
| 214 const size_t kTwoBytePayloadLengthField = 126; | |
| 215 const size_t kEightBytePayloadLengthField = 127; | |
| 216 const size_t kMaskingKeyWidthInBytes = 4; | |
| 217 | |
| 218 } | |
|
eroman
2011/08/02 18:46:41
nit: Please mark this with:
// anonymous
(conve
| |
| 219 | |
| 220 WebSocket* WebSocketHybi10::create(Connection* connection, | |
| 221 const HttpServerRequestInfo& request, | |
| 222 size_t* pos) { | |
| 223 std::string key = GetHeaderValue(request, "Sec-WebSocket-Key"); | |
| 224 if (key.empty()) { | |
| 225 connection->Send500("Invalid request format. " | |
| 226 "Sec-WebSocket-Key is empty or isn't specified."); | |
| 227 return NULL; | |
| 228 } | |
| 229 return new WebSocketHybi10(connection, request, pos); | |
| 230 } | |
| 231 | |
| 232 WebSocketHybi10::WebSocketHybi10(Connection* connection, | |
| 233 const HttpServerRequestInfo& request, | |
| 234 size_t* pos) | |
| 235 : WebSocket(connection) { | |
| 236 } | |
| 237 | |
| 238 void WebSocketHybi10::Accept(const HttpServerRequestInfo& request) | |
| 239 { | |
| 240 static const char* const web_socket_GUID = | |
| 241 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
| 242 std::string key = GetHeaderValue(request, "Sec-WebSocket-Key"); | |
| 243 std::string data = base::StringPrintf("%s%s", key.c_str(), web_socket_GUID); | |
| 244 std::string encoded_hash; | |
| 245 base::Base64Encode(base::SHA1HashString(data), &encoded_hash); | |
| 246 | |
| 247 std::string response = base::StringPrintf( | |
| 248 "HTTP/1.1 101 WebSocket Protocol Handshake\r\n" | |
| 249 "Upgrade: WebSocket\r\n" | |
| 250 "Connection: Upgrade\r\n" | |
| 251 "Sec-WebSocket-Accept: %s\r\n" | |
| 252 "\r\n", | |
| 253 encoded_hash.c_str()); | |
| 254 connection_->Send(response); | |
| 255 } | |
| 256 | |
| 257 WebSocket::ParseResult WebSocketHybi10::Read(std::string* message) { | |
| 258 size_t data_length = connection_->RecvData().length(); | |
| 259 if (data_length < 2) | |
| 260 return FrameIncomplete; | |
| 261 | |
| 262 const char* p = connection_->RecvData().c_str(); | |
| 263 const char* buffer_end = p + data_length; | |
| 264 | |
| 265 unsigned char first_byte = *p++; | |
| 266 unsigned char second_byte = *p++; | |
| 267 | |
| 268 final_ = first_byte & kFinalBit; | |
| 269 reserved1_ = first_byte & kReserved1Bit; | |
| 270 reserved2_ = first_byte & kReserved2Bit; | |
| 271 reserved3_ = first_byte & kReserved3Bit; | |
| 272 op_code_ = first_byte & kOpCodeMask; | |
| 273 masked_ = second_byte & kMaskBit; | |
| 274 | |
| 275 CHECK(op_code_ == kOpCodeText); | |
|
eroman
2011/08/02 18:46:41
CHECK_EQ ?
| |
| 276 | |
| 277 uint64 payload_length64 = second_byte & kPayloadLengthMask; | |
| 278 if (payload_length64 > kMaxSingleBytePayloadLength) { | |
| 279 int extended_payload_length_size; | |
| 280 if (payload_length64 == kTwoBytePayloadLengthField) | |
| 281 extended_payload_length_size = 2; | |
| 282 else { | |
| 283 DCHECK(payload_length64 == kEightBytePayloadLengthField); | |
| 284 extended_payload_length_size = 8; | |
| 285 } | |
| 286 if (buffer_end - p < extended_payload_length_size) | |
| 287 return FrameIncomplete; | |
| 288 payload_length64 = 0; | |
| 289 for (int i = 0; i < extended_payload_length_size; ++i) { | |
| 290 payload_length64 <<= 8; | |
| 291 payload_length64 |= static_cast<unsigned char>(*p++); | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 static const uint64 max_payload_ength = 0x7FFFFFFFFFFFFFFFull; | |
|
eroman
2011/08/02 18:46:41
ength --> length
| |
| 296 size_t masking_key_length = masked_ ? kMaskingKeyWidthInBytes : 0; | |
| 297 static size_t max_length = std::numeric_limits<size_t>::max(); | |
| 298 if (payload_length64 > max_payload_ength || | |
| 299 payload_length64 + masking_key_length > max_length) { | |
| 300 // WebSocket frame length too large. | |
| 301 return FrameError; | |
| 302 } | |
| 303 payload_length_ = static_cast<size_t>(payload_length64); | |
| 304 | |
| 305 size_t total_length = masking_key_length + payload_length_; | |
| 306 if (static_cast<size_t>(buffer_end - p) < total_length) | |
| 307 return FrameIncomplete; | |
| 308 | |
| 309 if (masked_) { | |
| 310 message->resize(payload_length_); | |
| 311 const char* masking_key = p; | |
| 312 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes); | |
| 313 for (size_t i = 0; i < payload_length_; ++i) // Unmask the payload. | |
| 314 (*message)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes]; | |
| 315 } else { | |
| 316 std::string buffer(p, p + payload_length_); | |
| 317 message->swap(buffer); | |
| 318 } | |
| 319 | |
| 320 size_t pos = p + masking_key_length + payload_length_ - | |
| 321 connection_->RecvData().c_str(); | |
|
eroman
2011/08/02 18:46:41
Huh? What is subtracting the c_str() about? That l
| |
| 322 connection_->Shift(pos); | |
| 323 return FrameOK; | |
| 324 } | |
| 325 | |
| 326 void WebSocketHybi10::Send(const std::string& message) { | |
| 327 std::vector<char> frame; | |
| 328 OpCode op_code = kOpCodeText; | |
| 329 size_t data_length = message.length(); | |
| 330 | |
| 331 frame.push_back(kFinalBit | op_code); | |
| 332 if (data_length <= kMaxSingleBytePayloadLength) | |
| 333 frame.push_back(kMaskBit | data_length); | |
| 334 else if (data_length <= 0xFFFF) { | |
| 335 frame.push_back(kMaskBit | kTwoBytePayloadLengthField); | |
| 336 frame.push_back((data_length & 0xFF00) >> 8); | |
| 337 frame.push_back(data_length & 0xFF); | |
| 338 } else { | |
| 339 frame.push_back(kMaskBit | kEightBytePayloadLengthField); | |
| 340 char extended_payload_length[8]; | |
| 341 size_t remaining = data_length; | |
| 342 // Fill the length into extended_payload_length in the network byte order. | |
| 343 for (int i = 0; i < 8; ++i) { | |
| 344 extended_payload_length[7 - i] = remaining & 0xFF; | |
| 345 remaining >>= 8; | |
| 346 } | |
| 347 frame.insert(frame.end(), | |
| 348 extended_payload_length, | |
| 349 extended_payload_length + 8); | |
| 350 DCHECK(!remaining); | |
| 351 } | |
| 352 | |
| 353 // Mask the frame. | |
| 354 size_t masking_key_start = frame.size(); | |
| 355 // Add placeholder for masking key. Will be overwritten. | |
| 356 frame.resize(frame.size() + kMaskingKeyWidthInBytes); | |
| 357 size_t payload_start = frame.size(); | |
| 358 const char* data = message.c_str(); | |
| 359 frame.insert(frame.end(), data, data + data_length); | |
| 360 | |
| 361 base::RandBytes(&frame[0] + masking_key_start, | |
| 362 kMaskingKeyWidthInBytes); | |
| 363 for (size_t i = 0; i < data_length; ++i) { | |
| 364 frame[payload_start + i] ^= | |
| 365 frame[masking_key_start + i % kMaskingKeyWidthInBytes]; | |
| 366 } | |
| 367 connection_->Send(&frame[0], frame.size()); | |
| 368 } | |
| 369 | |
| 370 WebSocket* WebSocket::createWebSocket(Connection* connection, | |
| 371 const HttpServerRequestInfo& request, | |
| 372 size_t* pos) { | |
| 373 std::string version = GetHeaderValue(request, "Sec-WebSocket-Version"); | |
| 374 if (version.length() && version == "8") | |
| 375 return WebSocketHybi10::create(connection, request, pos); | |
| 376 else | |
| 377 return WebSocketHixie76::create(connection, request, pos); | |
| 378 } | |
| 379 | |
| 380 } // namespace net | |
| OLD | NEW |