Changeset 90979 in webkit


Ignore:
Timestamp:
Jul 13, 2011 10:32:44 PM (13 years ago)
Author:
yutak@chromium.org
Message:

WebSocket: Implement hybi handshake
https://bugs.webkit.org/show_bug.cgi?id=64344

Reviewed by Kent Tamura.

Source/WebCore:

Implement WebSocket handshake which is described at
<http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-5.1>.

Notable differences from hixie-76 protocol are:

  • Challenge-response scheme has been changed dramatically.
  • Servers do not send Sec-WebSocket-Location and Sec-WebSocket-Origin anymore.
  • The value of "Upgrade" header has been changed to "websocket" (lower-case only).
  • "Origin" header in client's request is renamed to "Sec-WebSocket-Origin".

New tests: http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header.html

http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header.html
http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header.html

Note: Tests under hybi/ directory (including the new ones) are skipped and cannot be enabled
yet, because pywebsocket does not accept requests from half-baked client implementation
(i.e. hybi handshake + hixie-76 framing). They will be unskipped when hybi framing is landed
in WebCore.

  • websockets/WebSocketHandshake.cpp:

Functions and members only used for hixie-76 handshake are renamed so that they are not confused
with hybi-10's ones.
(WebCore::generateHixie76SecWebSocketKey):
(WebCore::generateHixie76Key3):
(WebCore::generateHixie76ExpectedChallengeResponse):
(WebCore::generateSecWebSocketKey):
Generates a value for Sec-WebSocket-Key as stated in hybi-10.
(WebCore::getExpectedWebSocketAccept):
Calculates the expected value of Sec-WebSocket-Accept.
(WebCore::WebSocketHandshake::WebSocketHandshake):
(WebCore::WebSocketHandshake::clientHandshakeMessage):
(WebCore::WebSocketHandshake::clientHandshakeRequest):
(WebCore::WebSocketHandshake::readServerHandshake):
(WebCore::WebSocketHandshake::serverWebSocketAccept):
(WebCore::WebSocketHandshake::serverWebSocketExtensions):
(WebCore::WebSocketHandshake::checkResponseHeaders):

  • websockets/WebSocketHandshake.h:

LayoutTests:

Fix existing tests so they match the new handshake format, and add tests for new header fields.

There are several changes in the format of handshake response, such as:

  • Reason string in HTTP status line has been changed to "Switching Protocols".
  • The value of "Upgrade" header has been changed to "websocket" (lower-case only).
  • Sec-WebSocket-Location and Sec-WebSocket-Origin are gone.
  • "Challenge response" value (16-byte data after the response header fields) is removed, and Sec-WebSocket-Accept header is added instead.

The value of Sec-WebSocket-Accept header is computed using compute_accept() function
located in mod_pywebsocket.handshake.hybi06 module. Although the module name contains
"hybi06", this function can be used to calculate the value of Sec-WebSocket-Accept header
for hybi-10 (calculation method has not been changed since hybi-06).

Note: Tests under hybi/ directory (including the new ones) are skipped and cannot be enabled
yet, because pywebsocket does not accept requests from half-baked client implementation
(i.e. hybi handshake + hixie-76 framing). They will be unskipped when hybi framing is landed
in WebCore.

  • http/tests/websocket/tests/handler_map.txt:

There must be only one handler that can handle requests to "/". hybi/echo-location_wsh.py
is modified to accept requests of both protocol versions.

  • http/tests/websocket/tests/hybi/bad-handshake-crash_wsh.py:
  • http/tests/websocket/tests/hybi/echo-challenge_wsh.py:
  • http/tests/websocket/tests/hybi/echo-location_wsh.py:
  • http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header-expected.txt: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header.html: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header_wsh.py: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-maxlength_wsh.py:
  • http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header-expected.txt: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header.html: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header_wsh.py: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-no-connection-header_wsh.py:
  • http/tests/websocket/tests/hybi/handshake-fail-by-no-cr_wsh.py:
  • http/tests/websocket/tests/hybi/handshake-fail-by-no-upgrade-header_wsh.py:
  • http/tests/websocket/tests/hybi/handshake-fail-by-prepended-null_wsh.py:
  • http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header-expected.txt: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header.html: Added.
  • http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header_wsh.py: Added.
  • http/tests/websocket/tests/hybi/long-invalid-header_wsh.py:
  • http/tests/websocket/tests/hybi/workers/resources/echo-challenge_wsh.py:
Location:
trunk
Files:
9 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r90976 r90979  
     12011-07-13  Yuta Kitamura  <yutak@chromium.org>
     2
     3        WebSocket: Implement hybi handshake
     4        https://bugs.webkit.org/show_bug.cgi?id=64344
     5
     6        Reviewed by Kent Tamura.
     7
     8        Fix existing tests so they match the new handshake format, and add tests for new header fields.
     9
     10        There are several changes in the format of handshake response, such as:
     11        - Reason string in HTTP status line has been changed to "Switching Protocols".
     12        - The value of "Upgrade" header has been changed to "websocket" (lower-case only).
     13        - Sec-WebSocket-Location and Sec-WebSocket-Origin are gone.
     14        - "Challenge response" value (16-byte data after the response header fields) is removed, and
     15          Sec-WebSocket-Accept header is added instead.
     16
     17        The value of Sec-WebSocket-Accept header is computed using compute_accept() function
     18        located in mod_pywebsocket.handshake.hybi06 module. Although the module name contains
     19        "hybi06", this function can be used to calculate the value of Sec-WebSocket-Accept header
     20        for hybi-10 (calculation method has not been changed since hybi-06).
     21
     22        Note: Tests under hybi/ directory (including the new ones) are skipped and cannot be enabled
     23        yet, because pywebsocket does not accept requests from half-baked client implementation
     24        (i.e. hybi handshake + hixie-76 framing). They will be unskipped when hybi framing is landed
     25        in WebCore.
     26
     27        * http/tests/websocket/tests/handler_map.txt:
     28        There must be only one handler that can handle requests to "/". hybi/echo-location_wsh.py
     29        is modified to accept requests of both protocol versions.
     30        * http/tests/websocket/tests/hybi/bad-handshake-crash_wsh.py:
     31        * http/tests/websocket/tests/hybi/echo-challenge_wsh.py:
     32        * http/tests/websocket/tests/hybi/echo-location_wsh.py:
     33        * http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header-expected.txt: Added.
     34        * http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header.html: Added.
     35        * http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header_wsh.py: Added.
     36        * http/tests/websocket/tests/hybi/handshake-fail-by-maxlength_wsh.py:
     37        * http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header-expected.txt: Added.
     38        * http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header.html: Added.
     39        * http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header_wsh.py: Added.
     40        * http/tests/websocket/tests/hybi/handshake-fail-by-no-connection-header_wsh.py:
     41        * http/tests/websocket/tests/hybi/handshake-fail-by-no-cr_wsh.py:
     42        * http/tests/websocket/tests/hybi/handshake-fail-by-no-upgrade-header_wsh.py:
     43        * http/tests/websocket/tests/hybi/handshake-fail-by-prepended-null_wsh.py:
     44        * http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header-expected.txt: Added.
     45        * http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header.html: Added.
     46        * http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header_wsh.py: Added.
     47        * http/tests/websocket/tests/hybi/long-invalid-header_wsh.py:
     48        * http/tests/websocket/tests/hybi/workers/resources/echo-challenge_wsh.py:
     49
    1502011-07-13  MORITA Hajime  <morrita@google.com>
    251
  • trunk/LayoutTests/http/tests/websocket/tests/handler_map.txt

    r90445 r90979  
    11# websocket handler map file.
    22# request to '/' will be handled by echo-location_wsh.py
    3 / /websocket/tests/hixie76/echo-location
     3# hybi/echo-location_wsh.py can handle both hixie-76 and hybi-10 protocols.
     4/ /websocket/tests/hybi/echo-location
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/bad-handshake-crash_wsh.py

    r90726 r90979  
     1from mod_pywebsocket.handshake.hybi06 import compute_accept
     2
     3
    14def web_socket_do_extra_handshake(request):
    2     msg = "HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
    3     msg += "Upgrade: WebSocket\r\n"
     5    msg = "HTTP/1.1 101 Switching Protocols\r\n"
     6    msg += "Upgrade: websocket\r\n"
    47    msg += "Connection: Upgrade\r\n"
    5     msg += "Sec-WebSocket-Location: " + request.ws_location + "\r\n"
    6     msg += "Sec-WebSocket-Origin: " + request.ws_origin + "\r\n"
     8    msg += "Sec-WebSocket-Accept: %s\r\n" % compute_accept(request.headers_in["Sec-WebSocket-Key"])[0]
    79    msg += "\xa5:\r\n"
    810    msg += "\r\n"
    9     msg += request.ws_challenge_md5
    1011    request.connection.write(msg)
    1112    print msg
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/echo-challenge_wsh.py

    r90726 r90979  
    77
    88def web_socket_transfer_data(request):
    9     msgutil.send_message(request, _hexify(request.ws_challenge))
    10 
    11 
    12 def _hexify(bytes):
    13     return ':'.join(['%02X' % ord(byte) for byte in bytes])
     9    msgutil.send_message(request, request.headers_in['Sec-WebSocket-Key'])
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/echo-location_wsh.py

    r90726 r90979  
    1 # Copyright (C) 2009 Google Inc. All rights reserved.
     1# Copyright (C) 2011 Google Inc. All rights reserved.
    22#
    33# Redistribution and use in source and binary forms, with or without
     
    2929
    3030from mod_pywebsocket import msgutil
     31from mod_pywebsocket.handshake._base import build_location
    3132
    3233
     
    3637
    3738def web_socket_transfer_data(request):
    38     print request.ws_location
    39     msgutil.send_message(request, request.ws_location)
     39    if hasattr(request, 'ws_location'):
     40        location = request.ws_location
     41    else:
     42        # When hybi protocol is used, pywebsocket does not provide
     43        # ws_location attribute because servers are not required to send
     44        # Sec-WebSocket-Location header according to the protocol
     45        # specification. If ws_location attribute is not available,
     46        # we use pywebsocket's internal function "build_function"
     47        # to obtain the identical value.
     48        location = build_location(request)
     49    msgutil.send_message(request, location)
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-fail-by-maxlength_wsh.py

    r90726 r90979  
    2121
    2222
     23from mod_pywebsocket.handshake.hybi06 import compute_accept
     24
     25
    2326def web_socket_do_extra_handshake(request):
    2427    # This will cause the handshake to fail because it pushes the length of the
    2528    # status line past 1024 characters
    2629    msg = '.' * 1024
    27     msg += 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    28     msg += 'Upgrade: WebSocket\r\n'
     30    msg += 'HTTP/1.1 101 Switching Protocols\r\n'
     31    msg += 'Upgrade: websocket\r\n'
    2932    msg += 'Connection: Upgrade\r\n'
    30     msg += 'Sec-WebSocket-Location: ' + request.ws_location + '\r\n'
    31     msg += 'Sec-WebSocket-Origin: ' + request.ws_origin + '\r\n'
     33    msg += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
    3234    msg += '\r\n'
    33     msg += request.ws_challenge_md5
    3435    request.connection.write(msg)
    3536    raise Exception('abort the connection') # Prevents pywebsocket from sending its own handshake message.
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-fail-by-no-connection-header_wsh.py

    r90726 r90979  
     1from mod_pywebsocket.handshake.hybi06 import compute_accept
     2
     3
    14def web_socket_do_extra_handshake(request):
    2     msg = 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    3     msg += 'Upgrade: WebSocket\r\n'
     5    msg = 'HTTP/1.1 101 Switching Protocols\r\n'
     6    msg += 'Upgrade: websocket\r\n'
    47#   Missing 'Connection: Upgrade\r\n'
    5     msg += 'Sec-WebSocket-Location: ' + request.ws_location + '\r\n'
    6     msg += 'Sec-WebSocket-Origin: ' + request.ws_origin + '\r\n'
     8    msg += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
    79    msg += '\r\n'
    8     msg += request.ws_challenge_md5
    910    request.connection.write(msg)
    1011    print msg
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-fail-by-no-cr_wsh.py

    r90726 r90979  
     1from mod_pywebsocket.handshake.hybi06 import compute_accept
     2
     3
    14def web_socket_do_extra_handshake(request):
    2     msg = 'HTTP/1.1 101 WebSocket Protocol Handshake\n' # Does not end with "\r\n".
    3     msg += 'Upgrade: WebSocket\r\n'
     5    msg = 'HTTP/1.1 101 Switching Protocols\n' # Does not end with "\r\n".
     6    msg += 'Upgrade: websocket\r\n'
    47    msg += 'Connection: Upgrade\r\n'
    5     msg += 'Sec-WebSocket-Location: ' + request.ws_location + '\r\n'
    6     msg += 'Sec-WebSocket-Origin: ' + request.ws_origin + '\r\n'
     8    msg += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
    79    msg += '\r\n'
    8     msg += request.ws_challenge_md5
    910    request.connection.write(msg)
    1011    print msg
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-fail-by-no-upgrade-header_wsh.py

    r90726 r90979  
     1from mod_pywebsocket.handshake.hybi06 import compute_accept
     2
     3
    14def web_socket_do_extra_handshake(request):
    2     msg = 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    3 #   Missing 'Upgrade: WebSocket\r\n'
     5    msg = 'HTTP/1.1 101 Switching Protocols\r\n'
     6#   Missing 'Upgrade: websocket\r\n'
    47    msg += 'Connection: Upgrade\r\n'
    5     msg += 'Sec-WebSocket-Location: ' + request.ws_location + '\r\n'
    6     msg += 'Sec-WebSocket-Origin: ' + request.ws_origin + '\r\n'
     8    msg += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
    79    msg += '\r\n'
    8     msg += request.ws_challenge_md5
    910    request.connection.write(msg)
    1011    print msg
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/handshake-fail-by-prepended-null_wsh.py

    r90726 r90979  
    2020# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2121
     22
    2223import time
     24from mod_pywebsocket import stream
     25from mod_pywebsocket.handshake.hybi06 import compute_accept
    2326
    2427
     
    2932    # to send more data - it should abort after reading a reasonable number of
    3033    # bytes (set arbitrarily to 1024).
    31     frame = '\0Frame-contains-thirty-two-bytes'
     34    frame = stream.create_text_frame('\0Frame-contains-thirty-two-bytes')
    3235
    3336    msg = frame
    34     msg += 'HTTP/1.1 101 WebSocket Protocol Handshake\r\n'
    35     msg += 'Upgrade: WebSocket\r\n'
     37    msg += 'HTTP/1.1 101 Switching Protocols\r\n'
     38    msg += 'Upgrade: websocket\r\n'
    3639    msg += 'Connection: Upgrade\r\n'
    37     msg += 'Sec-WebSocket-Location: ' + request.ws_location + '\r\n'
    38     msg += 'Sec-WebSocket-Origin: ' + request.ws_origin + '\r\n'
     40    msg += 'Sec-WebSocket-Accept: %s\r\n' % compute_accept(request.headers_in['Sec-WebSocket-Key'])[0]
    3941    msg += '\r\n'
    40     msg += request.ws_challenge_md5
    4142    request.connection.write(msg)
    4243    # continue writing data until the client disconnects
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/long-invalid-header_wsh.py

    r90726 r90979  
    33    msg += ("p" * 1024) + "\r\n"
    44    msg += "\r\n"
    5     msg += request.ws_challenge_md5
    65    request.connection.write(msg)
    76    raise Exception("Abort the connection") # Prevents pywebsocket from sending its own handshake message.
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/workers/resources/echo-challenge_wsh.py

    r90726 r90979  
    77
    88def web_socket_transfer_data(request):
    9     msgutil.send_message(request, _hexify(request.ws_challenge))
    10 
    11 
    12 def _hexify(bytes):
    13     return ':'.join(['%02X' % ord(byte) for byte in bytes])
     9    msgutil.send_message(request, request.headers_in['Sec-WebSocket-Key'])
  • trunk/Source/WebCore/ChangeLog

    r90976 r90979  
     12011-07-13  Yuta Kitamura  <yutak@chromium.org>
     2
     3        WebSocket: Implement hybi handshake
     4        https://bugs.webkit.org/show_bug.cgi?id=64344
     5
     6        Reviewed by Kent Tamura.
     7
     8        Implement WebSocket handshake which is described at
     9        <http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-5.1>.
     10
     11        Notable differences from hixie-76 protocol are:
     12        - Challenge-response scheme has been changed dramatically.
     13        - Servers do not send Sec-WebSocket-Location and Sec-WebSocket-Origin anymore.
     14        - The value of "Upgrade" header has been changed to "websocket" (lower-case only).
     15        - "Origin" header in client's request is renamed to "Sec-WebSocket-Origin".
     16
     17        New tests: http/tests/websocket/tests/hybi/handshake-fail-by-extensions-header.html
     18                   http/tests/websocket/tests/hybi/handshake-fail-by-no-accept-header.html
     19                   http/tests/websocket/tests/hybi/handshake-fail-by-wrong-accept-header.html
     20
     21        Note: Tests under hybi/ directory (including the new ones) are skipped and cannot be enabled
     22        yet, because pywebsocket does not accept requests from half-baked client implementation
     23        (i.e. hybi handshake + hixie-76 framing). They will be unskipped when hybi framing is landed
     24        in WebCore.
     25
     26        * websockets/WebSocketHandshake.cpp:
     27        Functions and members only used for hixie-76 handshake are renamed so that they are not confused
     28        with hybi-10's ones.
     29        (WebCore::generateHixie76SecWebSocketKey):
     30        (WebCore::generateHixie76Key3):
     31        (WebCore::generateHixie76ExpectedChallengeResponse):
     32        (WebCore::generateSecWebSocketKey):
     33        Generates a value for Sec-WebSocket-Key as stated in hybi-10.
     34        (WebCore::getExpectedWebSocketAccept):
     35        Calculates the expected value of Sec-WebSocket-Accept.
     36        (WebCore::WebSocketHandshake::WebSocketHandshake):
     37        (WebCore::WebSocketHandshake::clientHandshakeMessage):
     38        (WebCore::WebSocketHandshake::clientHandshakeRequest):
     39        (WebCore::WebSocketHandshake::readServerHandshake):
     40        (WebCore::WebSocketHandshake::serverWebSocketAccept):
     41        (WebCore::WebSocketHandshake::serverWebSocketExtensions):
     42        (WebCore::WebSocketHandshake::checkResponseHeaders):
     43        * websockets/WebSocketHandshake.h:
     44
    1452011-07-13  MORITA Hajime  <morrita@google.com>
    246
  • trunk/Source/WebCore/websockets/WebSocketHandshake.cpp

    r90704 r90979  
    3636#include "WebSocketHandshake.h"
    3737
     38#include "Base64.h"
    3839#include "Cookie.h"
    3940#include "CookieJar.h"
     
    4748#include <wtf/CryptographicallyRandomNumber.h>
    4849#include <wtf/MD5.h>
     50#include <wtf/SHA1.h>
    4951#include <wtf/StdLibExtras.h>
    5052#include <wtf/StringExtras.h>
     
    107109}
    108110
    109 static void generateSecWebSocketKey(uint32_t& number, String& key)
     111static void generateHixie76SecWebSocketKey(uint32_t& number, String& key)
    110112{
    111113    uint32_t space = randomNumberLessThan(12) + 1;
     
    132134}
    133135
    134 static void generateKey3(unsigned char key3[8])
     136static void generateHixie76Key3(unsigned char key3[8])
    135137{
    136138    cryptographicallyRandomValues(key3, 8);
     
    147149}
    148150
    149 static void generateExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16])
     151static void generateHixie76ExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16])
    150152{
    151153    unsigned char challenge[16];
     
    160162}
    161163
     164static String generateSecWebSocketKey()
     165{
     166    static const size_t nonceSize = 16;
     167    unsigned char key[nonceSize];
     168    cryptographicallyRandomValues(key, nonceSize);
     169    return base64Encode(reinterpret_cast<char*>(key), nonceSize);
     170}
     171
     172static String getExpectedWebSocketAccept(const String& secWebSocketKey)
     173{
     174    static const char* const webSocketKeyGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
     175    static const size_t sha1HashSize = 20; // FIXME: This should be defined in SHA1.h.
     176    SHA1 sha1;
     177    CString keyData = secWebSocketKey.ascii();
     178    sha1.addBytes(reinterpret_cast<const uint8_t*>(keyData.data()), keyData.length());
     179    sha1.addBytes(reinterpret_cast<const uint8_t*>(webSocketKeyGUID), strlen(webSocketKeyGUID));
     180    Vector<uint8_t, sha1HashSize> hash;
     181    sha1.computeHash(hash);
     182    return base64Encode(reinterpret_cast<const char*>(hash.data()), sha1HashSize);
     183}
     184
    162185WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context, bool useHixie76Protocol)
    163186    : m_url(url)
     
    168191    , m_mode(Incomplete)
    169192{
    170     uint32_t number1;
    171     uint32_t number2;
    172     generateSecWebSocketKey(number1, m_secWebSocketKey1);
    173     generateSecWebSocketKey(number2, m_secWebSocketKey2);
    174     generateKey3(m_key3);
    175     generateExpectedChallengeResponse(number1, number2, m_key3, m_expectedChallengeResponse);
     193    if (m_useHixie76Protocol) {
     194        uint32_t number1;
     195        uint32_t number2;
     196        generateHixie76SecWebSocketKey(number1, m_hixie76SecWebSocketKey1);
     197        generateHixie76SecWebSocketKey(number2, m_hixie76SecWebSocketKey2);
     198        generateHixie76Key3(m_hixie76Key3);
     199        generateHixie76ExpectedChallengeResponse(number1, number2, m_hixie76Key3, m_hixie76ExpectedChallengeResponse);
     200    } else {
     201        m_secWebSocketKey = generateSecWebSocketKey();
     202        m_expectedAccept = getExpectedWebSocketAccept(m_secWebSocketKey);
     203    }
    176204}
    177205
     
    235263
    236264    Vector<String> fields;
    237     fields.append("Upgrade: WebSocket");
     265    if (m_useHixie76Protocol)
     266        fields.append("Upgrade: WebSocket");
     267    else
     268        fields.append("Upgrade: websocket");
    238269    fields.append("Connection: Upgrade");
    239270    fields.append("Host: " + hostName(m_url, m_secure));
    240     fields.append("Origin: " + clientOrigin());
     271    if (m_useHixie76Protocol)
     272        fields.append("Origin: " + clientOrigin());
     273    else
     274        fields.append("Sec-WebSocket-Origin: " + clientOrigin());
    241275    if (!m_clientProtocol.isEmpty())
    242276        fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
     
    251285    }
    252286
    253     fields.append("Sec-WebSocket-Key1: " + m_secWebSocketKey1);
    254     fields.append("Sec-WebSocket-Key2: " + m_secWebSocketKey2);
     287    if (m_useHixie76Protocol) {
     288        fields.append("Sec-WebSocket-Key1: " + m_hixie76SecWebSocketKey1);
     289        fields.append("Sec-WebSocket-Key2: " + m_hixie76SecWebSocketKey2);
     290    } else {
     291        fields.append("Sec-WebSocket-Key: " + m_secWebSocketKey);
     292        // FIXME: Current version of pywebsocket only accepts version value of 7,
     293        // while hybi-10 requires this value to be 8. Should be fixed when
     294        // a new version of pywebsocket is released.
     295        fields.append("Sec-WebSocket-Version: 7");
     296    }
    255297
    256298    // Fields in the handshake are sent by the client in a random order; the
     
    266308
    267309    CString handshakeHeader = builder.toString().utf8();
     310    // Hybi-10 handshake is complete at this point.
     311    if (!m_useHixie76Protocol)
     312        return handshakeHeader;
     313    // Hixie-76 protocol requires sending eight-byte data (so-called "key3") after the request header fields.
    268314    char* characterBuffer = 0;
    269     CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_key3), characterBuffer);
     315    CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_hixie76Key3), characterBuffer);
    270316    memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length());
    271     memcpy(characterBuffer + handshakeHeader.length(), m_key3, sizeof(m_key3));
     317    memcpy(characterBuffer + handshakeHeader.length(), m_hixie76Key3, sizeof(m_hixie76Key3));
    272318    return msg;
    273319}
     
    279325    // m_key3 in WebSocketHandshakeRequest?
    280326    WebSocketHandshakeRequest request("GET", m_url);
    281     request.addHeaderField("Upgrade", "WebSocket");
     327    if (m_useHixie76Protocol)
     328        request.addHeaderField("Upgrade", "WebSocket");
     329    else
     330        request.addHeaderField("Upgrade", "websocket");
    282331    request.addHeaderField("Connection", "Upgrade");
    283332    request.addHeaderField("Host", hostName(m_url, m_secure));
    284     request.addHeaderField("Origin", clientOrigin());
     333    if (m_useHixie76Protocol)
     334        request.addHeaderField("Origin", clientOrigin());
     335    else
     336        request.addHeaderField("Sec-WebSocket-Origin", clientOrigin());
    285337    if (!m_clientProtocol.isEmpty())
    286338        request.addHeaderField("Sec-WebSocket-Protocol:", m_clientProtocol);
     
    295347    }
    296348
    297     request.addHeaderField("Sec-WebSocket-Key1", m_secWebSocketKey1);
    298     request.addHeaderField("Sec-WebSocket-Key2", m_secWebSocketKey2);
    299     request.setKey3(m_key3);
     349    if (m_useHixie76Protocol) {
     350        request.addHeaderField("Sec-WebSocket-Key1", m_hixie76SecWebSocketKey1);
     351        request.addHeaderField("Sec-WebSocket-Key2", m_hixie76SecWebSocketKey2);
     352        request.setKey3(m_hixie76Key3);
     353    } else {
     354        request.addHeaderField("Sec-WebSocket-Key", m_secWebSocketKey);
     355        request.addHeaderField("Sec-WebSocket-Version", "7"); // FIXME: See FIXME in clientHandshakeMessage().
     356    }
    300357
    301358    return request;
     
    349406        return p - header;
    350407    }
    351     if (len < static_cast<size_t>(p - header + sizeof(m_expectedChallengeResponse))) {
     408
     409    if (!m_useHixie76Protocol) { // Hybi-10 handshake is complete at this point.
     410        m_mode = Connected;
     411        return p - header;
     412    }
     413
     414    // In hixie-76 protocol, server's handshake contains sixteen-byte data (called "challenge response")
     415    // after the header fields.
     416    if (len < static_cast<size_t>(p - header + sizeof(m_hixie76ExpectedChallengeResponse))) {
    352417        // Just hasn't been received /expected/ yet.
    353418        m_mode = Incomplete;
    354419        return -1;
    355420    }
     421
    356422    m_response.setChallengeResponse(static_cast<const unsigned char*>(static_cast<const void*>(p)));
    357     if (memcmp(p, m_expectedChallengeResponse, sizeof(m_expectedChallengeResponse))) {
     423    if (memcmp(p, m_hixie76ExpectedChallengeResponse, sizeof(m_hixie76ExpectedChallengeResponse))) {
    358424        m_mode = Failed;
    359         return (p - header) + sizeof(m_expectedChallengeResponse);
     425        return (p - header) + sizeof(m_hixie76ExpectedChallengeResponse);
    360426    }
    361427    m_mode = Connected;
    362     return (p - header) + sizeof(m_expectedChallengeResponse);
     428    return (p - header) + sizeof(m_hixie76ExpectedChallengeResponse);
    363429}
    364430
     
    406472{
    407473    return m_response.headerFields().get("connection");
     474}
     475
     476String WebSocketHandshake::serverWebSocketAccept() const
     477{
     478    return m_response.headerFields().get("sec-websocket-accept");
     479}
     480
     481String WebSocketHandshake::serverWebSocketExtensions() const
     482{
     483    return m_response.headerFields().get("sec-websocket-extensions");
    408484}
    409485
     
    572648    const String& serverUpgrade = this->serverUpgrade();
    573649    const String& serverConnection = this->serverConnection();
     650    const String& serverWebSocketAccept = this->serverWebSocketAccept();
     651    const String& serverWebSocketExtensions = this->serverWebSocketExtensions();
    574652
    575653    if (serverUpgrade.isNull()) {
     
    581659        return false;
    582660    }
    583     if (serverWebSocketOrigin.isNull()) {
    584         m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Origin' header is missing";
    585         return false;
    586     }
    587     if (serverWebSocketLocation.isNull()) {
    588         m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Location' header is missing";
    589         return false;
     661    if (m_useHixie76Protocol) {
     662        if (serverWebSocketOrigin.isNull()) {
     663            m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Origin' header is missing";
     664            return false;
     665        }
     666        if (serverWebSocketLocation.isNull()) {
     667            m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Location' header is missing";
     668            return false;
     669        }
     670    } else {
     671        if (serverWebSocketAccept.isNull()) {
     672            m_failureReason = "Error during WebSocket handshake: 'Sec-WebSocket-Accept' header is missing";
     673            return false;
     674        }
    590675    }
    591676
     
    599684    }
    600685
    601     if (clientOrigin() != serverWebSocketOrigin) {
    602         m_failureReason = "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + serverWebSocketOrigin;
    603         return false;
    604     }
    605     if (clientLocation() != serverWebSocketLocation) {
    606         m_failureReason = "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + serverWebSocketLocation;
    607         return false;
    608     }
    609     if (!m_clientProtocol.isEmpty() && m_clientProtocol != serverWebSocketProtocol) {
    610         m_failureReason = "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + serverWebSocketProtocol;
    611         return false;
     686    if (m_useHixie76Protocol) {
     687        if (clientOrigin() != serverWebSocketOrigin) {
     688            m_failureReason = "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + serverWebSocketOrigin;
     689            return false;
     690        }
     691        if (clientLocation() != serverWebSocketLocation) {
     692            m_failureReason = "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + serverWebSocketLocation;
     693            return false;
     694        }
     695        if (!m_clientProtocol.isEmpty() && m_clientProtocol != serverWebSocketProtocol) {
     696            m_failureReason = "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + serverWebSocketProtocol;
     697            return false;
     698        }
     699    } else {
     700        if (serverWebSocketAccept != m_expectedAccept) {
     701            m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Accept mismatch";
     702            return false;
     703        }
     704        if (!serverWebSocketExtensions.isNull()) {
     705            // WebSocket protocol extensions are not supported yet.
     706            // We do not send Sec-WebSocket-Extensions header in our request, thus
     707            // servers should not return this header, either.
     708            m_failureReason = "Error during WebSocket handshake: Sec-WebSocket-Extensions header is invalid";
     709            return false;
     710        }
    612711    }
    613712    return true;
  • trunk/Source/WebCore/websockets/WebSocketHandshake.h

    r90704 r90979  
    11/*
    2  * Copyright (C) 2009 Google Inc.  All rights reserved.
     2 * Copyright (C) 2011 Google Inc.  All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    7474        String failureReason() const; // Returns a string indicating the reason of failure if mode() == Failed.
    7575
    76         String serverWebSocketOrigin() const;
    77         String serverWebSocketLocation() const;
     76        String serverWebSocketOrigin() const; // Only for hixie-76 handshake.
     77        String serverWebSocketLocation() const; // Only for hixie-76 handshake.
    7878        String serverWebSocketProtocol() const;
    7979        String serverSetCookie() const;
     
    8181        String serverUpgrade() const;
    8282        String serverConnection() const;
     83        String serverWebSocketAccept() const; // Only for hybi-10 handshake.
     84        String serverWebSocketExtensions() const; // Only for hybi-10 handshake.
    8385
    8486        const WebSocketHandshakeResponse& serverHandshakeResponse() const;
     
    102104        Mode m_mode;
    103105
    104         String m_secWebSocketKey1;
    105         String m_secWebSocketKey2;
    106         unsigned char m_key3[8];
    107         unsigned char m_expectedChallengeResponse[16];
    108 
    109106        WebSocketHandshakeResponse m_response;
    110107
    111108        String m_failureReason;
     109
     110        // For hixie-76 handshake.
     111        String m_hixie76SecWebSocketKey1;
     112        String m_hixie76SecWebSocketKey2;
     113        unsigned char m_hixie76Key3[8];
     114        unsigned char m_hixie76ExpectedChallengeResponse[16];
     115
     116        // For hybi-10 handshake.
     117        String m_secWebSocketKey;
     118        String m_expectedAccept;
    112119    };
    113120
Note: See TracChangeset for help on using the changeset viewer.