Changeset 59903 in webkit
- Timestamp:
- May 20, 2010 8:06:42 PM (14 years ago)
- Location:
- trunk
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r59900 r59903 1 2010-05-20 Fumitoshi Ukai <ukai@chromium.org> 2 3 Reviewed by Alexey Proskuryakov. 4 5 WebSocket handshake incompatible change in draft-hixie-thewebsocketprotocol-76 6 https://bugs.webkit.org/show_bug.cgi?id=35572 7 8 Fix LayoutTests for new WebSocket protocol. 9 It requires https://bugs.webkit.org/show_bug.cgi?id=38034 to pass websocket tests. 10 11 * websocket/tests/handshake-error-expected.txt: Update expected data. 12 * websocket/tests/handshake-error_wsh.py: Without CRLF, the wsh will sends "ThisWillCauseHandshakeErrorHTTP/1.1 101 WebSocket Protocol Handshake\r\n", which is legal for the first line of draft 76 WebSocket opening handshake. (4.1 Opening Handhshake, step 28 to 30). To fail handshake, it needs \r\n. 13 * websocket/tests/long-invalid-header-expected.txt: Originally, it expects Upgrade header after status line. In draft 76, order is not important. But "pppp..\r\n" would fail because it misses ":" (end of name) before \r. 14 1 15 2010-05-20 Victor Wang <victorw@chromium.org> 2 16 -
trunk/LayoutTests/websocket/tests/handshake-error-expected.txt
r51979 r59903 1 CONSOLE MESSAGE: line 0: Unexpected response code:1011 CONSOLE MESSAGE: line 0: No response code found: ThisWillCauseHandshakeError 2 2 Handshake error test 3 3 -
trunk/LayoutTests/websocket/tests/handshake-error_wsh.py
r51508 r59903 32 32 33 33 def web_socket_do_extra_handshake(request): 34 request.connection.write('ThisWillCauseHandshakeError ')34 request.connection.write('ThisWillCauseHandshakeError\r\n') 35 35 36 36 -
trunk/LayoutTests/websocket/tests/long-invalid-header-expected.txt
r56511 r59903 1 CONSOLE MESSAGE: line 0: Bad Upgrade header:pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp...1 CONSOLE MESSAGE: line 0: Unexpected CR in name at pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp... 2 2 Make sure WebSocket gives errors on long invalid upgrade header. 3 3 -
trunk/WebCore/ChangeLog
r59897 r59903 1 2010-05-20 Fumitoshi Ukai <ukai@chromium.org> 2 3 Reviewed by Alexey Proskuryakov. 4 5 WebSocket handshake incompatible change in draft-hixie-thewebsocketprotocol-76 6 https://bugs.webkit.org/show_bug.cgi?id=35572 7 8 WebSocket opening handshake is changed. New protocol draft could be found at http://www.whatwg.org/specs/web-socket-protocol/ 9 It requires https://bugs.webkit.org/show_bug.cgi?id=38034 to pass websocket tests. 10 11 * websockets/WebSocketHandshake.cpp: 12 (WebCore::extractResponseCode): 13 add lineLength parameter to return length of status line. 14 (WebCore::hostName): Added. 15 (WebCore::generateSecWebSocketKey): Added. 16 (WebCore::generateKey3): Added. 17 (WebCore::setChallengeNumber): Added. 18 (WebCore::generateChallengeResponseExpected): Added. 19 (WebCore::WebSocketHandshake::WebSocketHandshake): 20 generate challenge response key and expected data. 21 (WebCore::WebSocketHandshake::clientLocation): 22 use hostName. 23 (WebCore::WebSocketHandshake::clientHandshakeMessage): 24 changed for draft 76 spec. 25 (WebCore::WebSocketHandshake::clientHandshakeRequest): 26 (WebCore::WebSocketHandshake::readServerHandshake): 27 changed for draft 76 spec. 28 m_mode is managed in this method. 29 (WebCore::WebSocketHandshake::readHTTPHeaders): 30 change error log messages. 31 (WebCore::WebSocketHandshake::processHeaders): 32 chagned for draft 76 spec. 33 (WebCore::WebSocketHandshake::checkResponseHeaders): 34 return boolean whether response header is ok or not and not change m_mode in it. 35 * websockets/WebSocketHandshake.h: 36 1 37 2010-05-20 Adam Roben <aroben@apple.com> 2 38 -
trunk/WebCore/websockets/WebSocketHandshake.cpp
r57760 r59903 45 45 #include "SecurityOrigin.h" 46 46 #include "StringBuilder.h" 47 #include <wtf/text/CString.h> 47 48 #include <wtf/MD5.h> 49 #include <wtf/RandomNumber.h> 50 #include <wtf/StdLibExtras.h> 48 51 #include <wtf/StringExtras.h> 49 52 #include <wtf/Vector.h> 53 #include <wtf/text/CString.h> 50 54 51 55 namespace WebCore { 52 56 53 const char webSocketServerHandshakeHeader[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"; 54 const char webSocketUpgradeHeader[] = "Upgrade: WebSocket\r\n"; 55 const char webSocketConnectionHeader[] = "Connection: Upgrade\r\n"; 56 57 static String extractResponseCode(const char* header, int len) 57 static const char randomCharacterInSecWebSocketKey[] = "!\"#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; 58 59 static String extractResponseCode(const char* header, int len, size_t& lineLength) 58 60 { 59 61 const char* space1 = 0; 60 62 const char* space2 = 0; 61 63 const char* p; 62 for (p = header; p - header < len; p++) { 64 lineLength = 0; 65 for (p = header; p - header < len; p++, lineLength++) { 63 66 if (*p == ' ') { 64 67 if (!space1) … … 88 91 } 89 92 93 static String hostName(const KURL& url, bool secure) 94 { 95 ASSERT(url.protocolIs("wss") == secure); 96 StringBuilder builder; 97 builder.append(url.host().lower()); 98 if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) { 99 builder.append(":"); 100 builder.append(String::number(url.port())); 101 } 102 return builder.toString(); 103 } 104 90 105 static String trimConsoleMessage(const char* p, size_t len) 91 106 { … … 94 109 s += "..."; 95 110 return s; 111 } 112 113 static void generateSecWebSocketKey(uint32_t& number, String& key) 114 { 115 uint32_t space = static_cast<uint32_t>(randomNumber() * 12) + 1; 116 uint32_t max = 4294967295U / space; 117 number = static_cast<uint32_t>(randomNumber() * max); 118 uint32_t product = number * space; 119 120 String s = String::number(product); 121 int n = static_cast<int>(randomNumber() * 12) + 1; 122 DEFINE_STATIC_LOCAL(String, randomChars, (randomCharacterInSecWebSocketKey)); 123 for (int i = 0; i < n; i++) { 124 int pos = static_cast<int>(randomNumber() * (s.length() + 1)); 125 int chpos = static_cast<int>(randomNumber() * randomChars.length()); 126 s.insert(randomChars.substring(chpos, 1), pos); 127 } 128 DEFINE_STATIC_LOCAL(String, spaceChar, (" ")); 129 for (uint32_t i = 0; i < space; i++) { 130 int pos = static_cast<int>(randomNumber() * s.length() - 1) + 1; 131 s.insert(spaceChar, pos); 132 } 133 key = s; 134 } 135 136 static void generateKey3(unsigned char key3[8]) 137 { 138 for (int i = 0; i < 8; i++) 139 key3[i] = randomNumber() * 256; 140 } 141 142 static void setChallengeNumber(unsigned char* buf, uint32_t number) 143 { 144 unsigned char* p = buf + 3; 145 for (int i = 0; i < 4; i++) { 146 *p = number & 0xFF; 147 --p; 148 number >>= 8; 149 } 150 } 151 152 static void generateExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16]) 153 { 154 unsigned char challenge[16]; 155 setChallengeNumber(&challenge[0], number1); 156 setChallengeNumber(&challenge[4], number2); 157 memcpy(&challenge[8], key3, 8); 158 MD5 md5; 159 md5.addBytes(challenge, sizeof(challenge)); 160 Vector<uint8_t, 16> digest; 161 md5.checksum(digest); 162 memcpy(expectedChallenge, digest.data(), 16); 96 163 } 97 164 … … 103 170 , m_mode(Incomplete) 104 171 { 172 uint32_t number1; 173 uint32_t number2; 174 generateSecWebSocketKey(number1, m_secWebSocketKey1); 175 generateSecWebSocketKey(number2, m_secWebSocketKey2); 176 generateKey3(m_key3); 177 generateExpectedChallengeResponse(number1, number2, m_key3, m_expectedChallengeResponse); 105 178 } 106 179 … … 149 222 builder.append(m_secure ? "wss" : "ws"); 150 223 builder.append("://"); 151 builder.append(m_url.host().lower()); 152 if (m_url.port()) { 153 if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) { 154 builder.append(":"); 155 builder.append(String::number(m_url.port())); 156 } 157 } 224 builder.append(hostName(m_url, m_secure)); 158 225 builder.append(resourceName(m_url)); 159 226 return builder.toString(); … … 168 235 builder.append(resourceName(m_url)); 169 236 builder.append(" HTTP/1.1\r\n"); 170 builder.append("Upgrade: WebSocket\r\n"); 171 builder.append("Connection: Upgrade\r\n"); 172 builder.append("Host: "); 173 builder.append(m_url.host().lower()); 174 if (m_url.port() && ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443))) { 175 builder.append(":"); 176 builder.append(String::number(m_url.port())); 177 } 178 builder.append("\r\n"); 179 builder.append("Origin: "); 180 builder.append(clientOrigin()); 181 builder.append("\r\n"); 182 if (!m_clientProtocol.isEmpty()) { 183 builder.append("WebSocket-Protocol: "); 184 builder.append(m_clientProtocol); 185 builder.append("\r\n"); 186 } 237 238 Vector<String> fields; 239 fields.append("Upgrade: WebSocket"); 240 fields.append("Connection: Upgrade"); 241 fields.append("Host: " + hostName(m_url, m_secure)); 242 fields.append("Origin: " + clientOrigin()); 243 if (!m_clientProtocol.isEmpty()) 244 fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol); 187 245 188 246 KURL url = httpURLForAuthenticationAndCookies(); … … 190 248 Document* document = static_cast<Document*>(m_context); 191 249 String cookie = cookieRequestHeaderFieldValue(document, url); 192 if (!cookie.isEmpty()) { 193 builder.append("Cookie: "); 194 builder.append(cookie); 195 builder.append("\r\n"); 196 } 250 if (!cookie.isEmpty()) 251 fields.append("Cookie: " + cookie); 197 252 // Set "Cookie2: <cookie>" if cookies 2 exists for url? 198 253 } 199 254 255 fields.append("Sec-WebSocket-Key1: " + m_secWebSocketKey1); 256 fields.append("Sec-WebSocket-Key2: " + m_secWebSocketKey2); 257 258 // Fields in the handshake are sent by the client in a random order; the 259 // order is not meaningful. Thus, it's ok to send the order we constructed 260 // the fields. 261 262 for (size_t i = 0; i < fields.size(); i++) { 263 builder.append(fields[i]); 264 builder.append("\r\n"); 265 } 266 200 267 builder.append("\r\n"); 201 return builder.toString().utf8(); 268 269 CString handshakeHeader = builder.toString().utf8(); 270 char* characterBuffer = 0; 271 CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_key3), characterBuffer); 272 memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length()); 273 memcpy(characterBuffer + handshakeHeader.length(), m_key3, sizeof(m_key3)); 274 return msg; 202 275 } 203 276 … … 205 278 { 206 279 // Keep the following consistent with clientHandshakeMessage(). 280 // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and 281 // m_key3 in WebSocketHandshakeRequest? 207 282 WebSocketHandshakeRequest request(m_url, clientOrigin(), m_clientProtocol); 208 283 … … 238 313 { 239 314 m_mode = Incomplete; 240 if (len < sizeof(webSocketServerHandshakeHeader) - 1) { 241 // Just hasn't been received fully yet. 315 size_t lineLength; 316 const String& code = extractResponseCode(header, len, lineLength); 317 if (code.isNull()) { 318 // Just hasn't been received yet. 242 319 return -1; 243 320 } 244 if (!memcmp(header, webSocketServerHandshakeHeader, sizeof(webSocketServerHandshakeHeader) - 1)) 245 m_mode = Normal; 246 else { 247 const String& code = extractResponseCode(header, len); 248 if (code.isNull()) { 249 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Short server handshake: " + trimConsoleMessage(header, len), 0, clientOrigin()); 250 return -1; 251 } 252 if (code.isEmpty()) { 253 m_mode = Failed; 254 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "No response code found: " + trimConsoleMessage(header, len), 0, clientOrigin()); 255 return len; 256 } 257 LOG(Network, "response code: %s", code.utf8().data()); 258 if (code == "401") { 259 m_mode = Failed; 260 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Authentication required, but not implemented yet.", 0, clientOrigin()); 261 return len; 262 } else { 263 m_mode = Failed; 264 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected response code:" + code, 0, clientOrigin()); 265 return len; 266 } 267 } 268 const char* p = header + sizeof(webSocketServerHandshakeHeader) - 1; 269 const char* end = header + len; 270 271 if (m_mode == Normal) { 272 size_t headerSize = end - p; 273 if (headerSize < sizeof(webSocketUpgradeHeader) - 1) { 274 m_mode = Incomplete; 275 return 0; 276 } 277 if (memcmp(p, webSocketUpgradeHeader, sizeof(webSocketUpgradeHeader) - 1)) { 278 m_mode = Failed; 279 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Bad Upgrade header: " + trimConsoleMessage(p, end - p), 0, clientOrigin()); 280 return p - header + sizeof(webSocketUpgradeHeader) - 1; 281 } 282 p += sizeof(webSocketUpgradeHeader) - 1; 283 284 headerSize = end - p; 285 if (headerSize < sizeof(webSocketConnectionHeader) - 1) { 286 m_mode = Incomplete; 287 return -1; 288 } 289 if (memcmp(p, webSocketConnectionHeader, sizeof(webSocketConnectionHeader) - 1)) { 290 m_mode = Failed; 291 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Bad Connection header: " + trimConsoleMessage(p, end - p), 0, clientOrigin()); 292 return p - header + sizeof(webSocketConnectionHeader) - 1; 293 } 294 p += sizeof(webSocketConnectionHeader) - 1; 295 } 296 297 if (!strnstr(p, "\r\n\r\n", end - p)) { 321 if (code.isEmpty()) { 322 m_mode = Failed; 323 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "No response code found: " + trimConsoleMessage(header, lineLength), 0, clientOrigin()); 324 return len; 325 } 326 LOG(Network, "response code: %s", code.utf8().data()); 327 if (code != "101") { 328 m_mode = Failed; 329 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected response code:" + code, 0, clientOrigin()); 330 return len; 331 } 332 m_mode = Normal; 333 if (!strnstr(header, "\r\n\r\n", len)) { 298 334 // Just hasn't been received fully yet. 299 335 m_mode = Incomplete; … … 301 337 } 302 338 HTTPHeaderMap headers; 303 p = readHTTPHeaders(p, end, &headers); 339 const char* headerFields = strnstr(header, "\r\n", len); // skip status line 340 ASSERT(headerFields); 341 headerFields += 2; // skip "\r\n". 342 const char* p = readHTTPHeaders(headerFields, header + len, &headers); 304 343 if (!p) { 305 344 LOG(Network, "readHTTPHeaders failed"); … … 307 346 return len; 308 347 } 309 if (!processHeaders(headers) ) {348 if (!processHeaders(headers) || !checkResponseHeaders()) { 310 349 LOG(Network, "header process failed"); 311 350 m_mode = Failed; 312 351 return p - header; 313 352 } 314 switch (m_mode) { 315 case Normal: 316 checkResponseHeaders(); 317 break; 318 default: 353 if (len < static_cast<size_t>(p - header + sizeof(m_expectedChallengeResponse))) { 354 // Just hasn't been received /expected/ yet. 355 m_mode = Incomplete; 356 return -1; 357 } 358 if (memcmp(p, m_expectedChallengeResponse, sizeof(m_expectedChallengeResponse))) { 319 359 m_mode = Failed; 320 break; 321 } 322 return p - header; 360 return (p - header) + sizeof(m_expectedChallengeResponse); 361 } 362 m_mode = Connected; 363 return (p - header) + sizeof(m_expectedChallengeResponse); 323 364 } 324 365 … … 403 444 return 0; 404 445 } 405 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected CR in name at " + trimConsoleMessage( p, end - p), 0, clientOrigin());446 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected CR in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin()); 406 447 return 0; 407 448 case '\n': 408 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in name at " + trimConsoleMessage( p, end - p), 0, clientOrigin());449 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin()); 409 450 return 0; 410 451 case ':': … … 430 471 break; 431 472 case '\n': 432 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in value at " + trimConsoleMessage( p, end - p), 0, clientOrigin());473 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in value at " + trimConsoleMessage(value.data(), value.size()), 0, clientOrigin()); 433 474 return 0; 434 475 default: … … 466 507 switch (m_mode) { 467 508 case Normal: 468 if (it->first == " websocket-origin")509 if (it->first == "sec-websocket-origin") 469 510 m_wsOrigin = it->second; 470 else if (it->first == " websocket-location")511 else if (it->first == "sec-websocket-location") 471 512 m_wsLocation = it->second; 472 else if (it->first == " websocket-protocol")513 else if (it->first == "sec-websocket-protocol") 473 514 m_wsProtocol = it->second; 474 515 else if (it->first == "set-cookie") … … 487 528 } 488 529 489 void WebSocketHandshake::checkResponseHeaders() 490 { 491 ASSERT(m_mode == Normal); 492 m_mode = Failed; 530 bool WebSocketHandshake::checkResponseHeaders() 531 { 493 532 if (m_wsOrigin.isNull()) { 494 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: ' websocket-origin' header is missing", 0, clientOrigin());495 return ;533 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'sec-websocket-origin' header is missing", 0, clientOrigin()); 534 return false; 496 535 } 497 536 if (m_wsLocation.isNull()) { 498 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: ' websocket-location' header is missing", 0, clientOrigin());499 return ;537 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'sec-websocket-location' header is missing", 0, clientOrigin()); 538 return false; 500 539 } 501 540 502 541 if (clientOrigin() != m_wsOrigin) { 503 542 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + m_wsOrigin, 0, clientOrigin()); 504 return ;543 return false; 505 544 } 506 545 if (clientLocation() != m_wsLocation) { 507 546 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + m_wsLocation, 0, clientOrigin()); 508 return ;547 return false; 509 548 } 510 549 if (!m_clientProtocol.isEmpty() && m_clientProtocol != m_wsProtocol) { 511 550 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + m_wsProtocol, 0, clientOrigin()); 512 return; 513 } 514 m_mode = Connected; 515 return; 516 } 517 518 } // namespace WebCore 519 520 #endif // ENABLE(WEB_SOCKETS) 551 return false; 552 } 553 return true; 554 } 555 556 } // namespace WebCore 557 558 #endif // ENABLE(WEB_SOCKETS) -
trunk/WebCore/websockets/WebSocketHandshake.h
r55570 r59903 41 41 namespace WebCore { 42 42 43 class HTTPHeaderMap; 43 44 class ScriptExecutionContext; 44 class HTTPHeaderMap;45 45 46 46 class WebSocketHandshake : public Noncopyable { … … 93 93 const char* readHTTPHeaders(const char* start, const char* end, HTTPHeaderMap* headers); 94 94 bool processHeaders(const HTTPHeaderMap& headers); 95 voidcheckResponseHeaders();95 bool checkResponseHeaders(); 96 96 97 97 KURL m_url; … … 107 107 String m_setCookie; 108 108 String m_setCookie2; 109 110 String m_secWebSocketKey1; 111 String m_secWebSocketKey2; 112 unsigned char m_key3[8]; 113 unsigned char m_expectedChallengeResponse[16]; 109 114 }; 110 115
Note: See TracChangeset
for help on using the changeset viewer.