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_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) { | |
|
yurys
2011/08/01 13:42:29
Consider splitting this class into two files with
| |
| 30 std::string result; | |
| 31 const char* pChar = str.c_str(); | |
| 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 = | |
| 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); | |
| 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_ength = 0x7FFFFFFFFFFFFFFFull; | |
|
yurys
2011/08/01 13:51:09
max_payload_ength -> max_payload_length
| |
| 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_ength || | |
| 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]; | |
| 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 | |
| OLD | NEW |