Changeset 91243 in webkit


Ignore:
Timestamp:
Jul 19, 2011 1:57:29 AM (13 years ago)
Author:
yutak@chromium.org
Message:

WebSocket: Implement hybi framing
https://bugs.webkit.org/show_bug.cgi?id=64522

Reviewed by Kent Tamura.

Source/WebCore:

Implement WebSocket framing protocol which is mainly described in
<http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4> and
<http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-6>.

Hybi protocol introduces a new frame format which is drastically different from
the old one. Notable differences are:

  • Binary data support.
  • Fragmentation support: a single message can be fragmented to multiple frames.
  • Ping-pong support.
  • Masking: frame content of a client must be masked to prevent cross-protocol attacks.

This patch covers the following features:

  • Send a pong frame when a ping frame is received.
  • Receive fragmented frames.
  • Receive masked frames. (Servers do not have to mask frames, but they may if they wish.)

The following features are NOT implemented yet:

  • Send or receive binary messages.
  • Send a ping message.
  • Send fragmented frames. (It is unclear whether this is necessary.)
  • Rewrite the frame content by WebSocket protocol extensions (like frame compression).

New tests: http/tests/websocket/tests/hybi/broken-utf8.html

http/tests/websocket/tests/hybi/fragmented-control-frame.html
http/tests/websocket/tests/hybi/fragmented-frames.html
http/tests/websocket/tests/hybi/interleaved-fragments.html
http/tests/websocket/tests/hybi/long-control-frame.html
http/tests/websocket/tests/hybi/masked-frames.html
http/tests/websocket/tests/hybi/pong.html
http/tests/websocket/tests/hybi/reserved-bits.html
http/tests/websocket/tests/hybi/reserved-opcodes.html
http/tests/websocket/tests/hybi/too-long-payload.html

  • websockets/WebSocketChannel.cpp:

(WebCore::WebSocketChannel::WebSocketChannel):
(WebCore::WebSocketChannel::send):
The original content of send() was moved to a private method sendFrameHixie76().
(WebCore::WebSocketChannel::fail):
Stop handling incoming data after the WebSocket connection is failed.
It was unclear to me whether we should do the same thing for hixie-76 connection;
for now, I kept the original behavior.
(WebCore::WebSocketChannel::processBuffer):
(WebCore::WebSocketChannel::resumeTimerFired):
(WebCore::WebSocketChannel::startClosingHandshake):
(WebCore::WebSocketChannel::closingTimerFired):
(WebCore::WebSocketChannel::parseFrame):
(WebCore::WebSocketChannel::processFrame):
(WebCore::WebSocketChannel::processFrameHixie76):
(WebCore::WebSocketChannel::sendFrame):
(WebCore::WebSocketChannel::sendFrameHixie76):

  • websockets/WebSocketChannel.h:

(WebCore::WebSocketChannel::isNonControlOpCode):
(WebCore::WebSocketChannel::isControlOpCode):
(WebCore::WebSocketChannel::isReservedOpCode):

LayoutTests:

Fix existing tests so they match the new frame format, and add tests for the new frame types
and error conditions related to the new frame format.

Unskip hybi tests on mac, win and chromium. Other ports (wk2, qt and gtk) still skip these tests
because they do not support changing the value of "WebKitHixie76WebSocketProtocolEnabled"
preference key via layoutTestController.overridePreferences() yet.

  • http/tests/websocket/tests/hybi/broken-utf8-expected.txt: Added.
  • http/tests/websocket/tests/hybi/broken-utf8.html: Added.
  • http/tests/websocket/tests/hybi/broken-utf8_wsh.py: Added.
  • http/tests/websocket/tests/hybi/client-close-expected.txt:
  • http/tests/websocket/tests/hybi/client-close.html:

The format of a close frame has been changed. Currently, we do not include any payload
in a close frame, thus it must start with "\x88\x80" (see section 4.1 of hybi-10
specification for more details).

  • http/tests/websocket/tests/hybi/client-close_wsh.py:
  • http/tests/websocket/tests/hybi/fragmented-control-frame-expected.txt: Added.
  • http/tests/websocket/tests/hybi/fragmented-control-frame.html: Added.
  • http/tests/websocket/tests/hybi/fragmented-control-frame_wsh.py: Added.
  • http/tests/websocket/tests/hybi/fragmented-frames-expected.txt: Added.
  • http/tests/websocket/tests/hybi/fragmented-frames.html: Added.
  • http/tests/websocket/tests/hybi/fragmented-frames_wsh.py: Added.
  • http/tests/websocket/tests/hybi/interleaved-fragments-expected.txt: Added.
  • http/tests/websocket/tests/hybi/interleaved-fragments.html: Added.
  • http/tests/websocket/tests/hybi/interleaved-fragments_wsh.py: Added.
  • http/tests/websocket/tests/hybi/long-control-frame-expected.txt: Added.
  • http/tests/websocket/tests/hybi/long-control-frame.html: Added.
  • http/tests/websocket/tests/hybi/long-control-frame_wsh.py: Added.
  • http/tests/websocket/tests/hybi/masked-frames-expected.txt: Added.
  • http/tests/websocket/tests/hybi/masked-frames.html: Added.
  • http/tests/websocket/tests/hybi/masked-frames_wsh.py: Added.
  • http/tests/websocket/tests/hybi/pong-expected.txt: Added.
  • http/tests/websocket/tests/hybi/pong.html: Added.
  • http/tests/websocket/tests/hybi/pong_wsh.py: Added.
  • http/tests/websocket/tests/hybi/reserved-bits-expected.txt: Added.
  • http/tests/websocket/tests/hybi/reserved-bits.html: Added.
  • http/tests/websocket/tests/hybi/reserved-bits_wsh.py: Added.
  • http/tests/websocket/tests/hybi/reserved-opcodes-expected.txt: Added.
  • http/tests/websocket/tests/hybi/reserved-opcodes.html: Added.
  • http/tests/websocket/tests/hybi/reserved-opcodes_wsh.py: Added.
  • http/tests/websocket/tests/hybi/send2_wsh.py:

Send two text frames at once.

  • http/tests/websocket/tests/hybi/too-long-payload-expected.txt: Added.
  • http/tests/websocket/tests/hybi/too-long-payload.html: Added.
  • http/tests/websocket/tests/hybi/too-long-payload_wsh.py: Added.
  • platform/chromium/test_expectations.txt:

Derive test expectations of hixie76 tests, because these tests are likely to behave
the same way as hixie76 tests. Will be checked later whether they really do.

  • platform/mac/Skipped:
  • platform/win/Skipped:
Location:
trunk
Files:
23 added
11 edited
7 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r91242 r91243  
     12011-07-19  Yuta Kitamura  <yutak@chromium.org>
     2
     3        WebSocket: Implement hybi framing
     4        https://bugs.webkit.org/show_bug.cgi?id=64522
     5
     6        Reviewed by Kent Tamura.
     7
     8        Fix existing tests so they match the new frame format, and add tests for the new frame types
     9        and error conditions related to the new frame format.
     10
     11        Unskip hybi tests on mac, win and chromium. Other ports (wk2, qt and gtk) still skip these tests
     12        because they do not support changing the value of "WebKitHixie76WebSocketProtocolEnabled"
     13        preference key via layoutTestController.overridePreferences() yet.
     14
     15        * http/tests/websocket/tests/hybi/broken-utf8-expected.txt: Added.
     16        * http/tests/websocket/tests/hybi/broken-utf8.html: Added.
     17        * http/tests/websocket/tests/hybi/broken-utf8_wsh.py: Added.
     18        * http/tests/websocket/tests/hybi/client-close-expected.txt:
     19        * http/tests/websocket/tests/hybi/client-close.html:
     20        The format of a close frame has been changed. Currently, we do not include any payload
     21        in a close frame, thus it must start with "\x88\x80" (see section 4.1 of hybi-10
     22        specification for more details).
     23        * http/tests/websocket/tests/hybi/client-close_wsh.py:
     24        * http/tests/websocket/tests/hybi/fragmented-control-frame-expected.txt: Added.
     25        * http/tests/websocket/tests/hybi/fragmented-control-frame.html: Added.
     26        * http/tests/websocket/tests/hybi/fragmented-control-frame_wsh.py: Added.
     27        * http/tests/websocket/tests/hybi/fragmented-frames-expected.txt: Added.
     28        * http/tests/websocket/tests/hybi/fragmented-frames.html: Added.
     29        * http/tests/websocket/tests/hybi/fragmented-frames_wsh.py: Added.
     30        * http/tests/websocket/tests/hybi/interleaved-fragments-expected.txt: Added.
     31        * http/tests/websocket/tests/hybi/interleaved-fragments.html: Added.
     32        * http/tests/websocket/tests/hybi/interleaved-fragments_wsh.py: Added.
     33        * http/tests/websocket/tests/hybi/long-control-frame-expected.txt: Added.
     34        * http/tests/websocket/tests/hybi/long-control-frame.html: Added.
     35        * http/tests/websocket/tests/hybi/long-control-frame_wsh.py: Added.
     36        * http/tests/websocket/tests/hybi/masked-frames-expected.txt: Added.
     37        * http/tests/websocket/tests/hybi/masked-frames.html: Added.
     38        * http/tests/websocket/tests/hybi/masked-frames_wsh.py: Added.
     39        * http/tests/websocket/tests/hybi/pong-expected.txt: Added.
     40        * http/tests/websocket/tests/hybi/pong.html: Added.
     41        * http/tests/websocket/tests/hybi/pong_wsh.py: Added.
     42        * http/tests/websocket/tests/hybi/reserved-bits-expected.txt: Added.
     43        * http/tests/websocket/tests/hybi/reserved-bits.html: Added.
     44        * http/tests/websocket/tests/hybi/reserved-bits_wsh.py: Added.
     45        * http/tests/websocket/tests/hybi/reserved-opcodes-expected.txt: Added.
     46        * http/tests/websocket/tests/hybi/reserved-opcodes.html: Added.
     47        * http/tests/websocket/tests/hybi/reserved-opcodes_wsh.py: Added.
     48        * http/tests/websocket/tests/hybi/send2_wsh.py:
     49        Send two text frames at once.
     50        * http/tests/websocket/tests/hybi/too-long-payload-expected.txt: Added.
     51        * http/tests/websocket/tests/hybi/too-long-payload.html: Added.
     52        * http/tests/websocket/tests/hybi/too-long-payload_wsh.py: Added.
     53        * platform/chromium/test_expectations.txt:
     54        Derive test expectations of hixie76 tests, because these tests are likely to behave
     55        the same way as hixie76 tests. Will be checked later whether they really do.
     56        * platform/mac/Skipped:
     57        * platform/win/Skipped:
     58
    1592011-07-19  Nikolas Zimmermann  <nzimmermann@rim.com>
    260
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/broken-utf8.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    88<div id="description"></div>
    99<div id="console"></div>
    10 <script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     10<script>
     11description("Test whether WebSocket aborts the connection when it receives a text frame containing broken UTF-8 data.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/broken-utf8");
    1818var closeEvent;
    19 var receivedMessage;
    2019
    2120ws.onopen = function()
    2221{
    23     debug("Connected");
    24     ws.close();
     22    debug("onopen() was called.");
    2523};
    2624
    27 ws.onmessage = function(messageEvent)
     25ws.onmessage = function(event)
    2826{
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     27    var message = event.data;
     28    testFailed("onmessage() was called. (message = \"" + message + "\")");
    3129};
    3230
    3331ws.onclose = function(event)
    3432{
    35     debug("Closed");
     33    debug("onclose() was called.");
    3634    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    38     shouldBeTrue("closeEvent.wasClean");
     35    shouldBeFalse("closeEvent.wasClean");
    3936    finishJSTest();
    4037};
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/client-close-expected.txt

    r90726 r91243  
    44
    55Connected
    6 Received: close_frame='\xff\x00'
     6Received: close_frame[:2]='\x88\x80'
    77Closed
    8 PASS receivedMessage is "close_frame='\\xff\\x00'"
     8PASS receivedMessage is "close_frame[:2]='\\x88\\x80'"
    99PASS closeEvent.wasClean is true
    1010PASS successfullyParsed is true
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/client-close.html

    r90726 r91243  
    3535    debug("Closed");
    3636    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
     37    shouldBeEqualToString("receivedMessage", "close_frame[:2]='\\x88\\x80'");
    3838    shouldBeTrue("closeEvent.wasClean");
    3939    finishJSTest();
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/client-close_wsh.py

    r90726 r91243  
    88def web_socket_transfer_data(request):
    99    # Wait for a close frame sent from the client.
    10     close_frame = request.ws_stream.receive_bytes(2)
     10    close_frame = request.ws_stream.receive_bytes(6)
    1111
    12     # Tell the client what we have received.
    13     msgutil.send_message(request, 'close_frame=%r' % close_frame)
     12    # Send only first two bytes of the received frame. The remaining four bytes are
     13    # "masking key", which changes every time the test runs.
     14    msgutil.send_message(request, 'close_frame[:2]=%r' % close_frame[:2])
    1415
    1516    # If the following assertion fails, AssertionError will be raised,
     
    1718    # In this case, the client will fail to finish closing handshake, thus
    1819    # closeEvent.wasClean will become false.
    19     assert close_frame == '\xff\x00'
     20    assert close_frame[:2] == '\x88\x80'
    2021
    2122    # Pretend we have received a close frame from the client.
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/fragmented-control-frame.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    88<div id="description"></div>
    99<div id="console"></div>
    10 <script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     10<script>
     11description("Test whether WebSocket rejects a fragmented control frame and aborts the connection.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var url = "ws://localhost:8880/websocket/tests/hybi/fragmented-control-frame";
     18var ws = new WebSocket(url);
    1819var closeEvent;
    19 var receivedMessage;
    2020
    2121ws.onopen = function()
    2222{
    23     debug("Connected");
    24     ws.close();
     23    debug("onopen() was called.");
    2524};
    2625
    27 ws.onmessage = function(messageEvent)
     26ws.onmessage = function(event)
    2827{
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     28    var message = event.data;
     29    testFailed("onmessage() was called. (message = \"" + message + "\")");
    3130};
    3231
    3332ws.onclose = function(event)
    3433{
    35     debug("Closed");
    3634    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    38     shouldBeTrue("closeEvent.wasClean");
     35    shouldBeFalse("closeEvent.wasClean");
    3936    finishJSTest();
    4037};
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/fragmented-frames.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    88<div id="description"></div>
    99<div id="console"></div>
    10 <script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     10<script>
     11description("Receive fragmented WebSocket frames.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var url = "ws://localhost:8880/websocket/tests/hybi/fragmented-frames";
     18var ws = new WebSocket(url);
    1819var closeEvent;
    19 var receivedMessage;
     20var expectedMessages = ["First message", "Second message", "Third message"];
     21var actualMessages = [];
    2022
    2123ws.onopen = function()
    2224{
    23     debug("Connected");
     25    debug("onopen() was called.");
    2426    ws.close();
    2527};
    2628
    27 ws.onmessage = function(messageEvent)
     29ws.onmessage = function(event)
    2830{
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     31    var message = event.data;
     32    debug("onmessage() was called. (message = \"" + message + "\")");
     33    actualMessages.push(message);
    3134};
    3235
    3336ws.onclose = function(event)
    3437{
    35     debug("Closed");
    3638    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    3839    shouldBeTrue("closeEvent.wasClean");
     40    shouldBeTrue("actualMessages.length === expectedMessages.length");
     41    for (var i = 0; i < expectedMessages.length; ++i)
     42        shouldBeEqualToString("actualMessages[" + i + "]", expectedMessages[i]);
    3943    finishJSTest();
    4044};
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/interleaved-fragments.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    88<div id="description"></div>
    99<div id="console"></div>
    10 <script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     10<script>
     11description("Test whether WebSocket rejects interleaved fragmented frames.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var url = "ws://localhost:8880/websocket/tests/hybi/interleaved-fragments";
     18var ws = new WebSocket(url);
    1819var closeEvent;
    19 var receivedMessage;
    2020
    2121ws.onopen = function()
    2222{
    23     debug("Connected");
    24     ws.close();
     23    debug("onopen() was called.");
    2524};
    2625
    27 ws.onmessage = function(messageEvent)
     26ws.onmessage = function(event)
    2827{
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     28    var message = event.data;
     29    testFailed("onmessage() was called. (message = \"" + message + "\")");
    3130};
    3231
    3332ws.onclose = function(event)
    3433{
    35     debug("Closed");
    3634    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    38     shouldBeTrue("closeEvent.wasClean");
     35    shouldBeFalse("closeEvent.wasClean");
    3936    finishJSTest();
    4037};
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/long-control-frame.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    88<div id="description"></div>
    99<div id="console"></div>
    10 <script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     10<script>
     11description("Test whether WebSocket rejects control frames longer than 125 bytes.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var url = "ws://localhost:8880/websocket/tests/hybi/long-control-frame";
     18var ws = new WebSocket(url);
    1819var closeEvent;
    19 var receivedMessage;
     20var message;
    2021
    2122ws.onopen = function()
    2223{
    23     debug("Connected");
    24     ws.close();
    25 };
    26 
    27 ws.onmessage = function(messageEvent)
    28 {
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     24    debug("onopen() was called.");
    3125};
    3226
    3327ws.onclose = function(event)
    3428{
    35     debug("Closed");
    3629    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    38     shouldBeTrue("closeEvent.wasClean");
     30    shouldBeFalse("closeEvent.wasClean");
    3931    finishJSTest();
    4032};
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/pong.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    88<div id="description"></div>
    99<div id="console"></div>
    10 <script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     10<script>
     11description("Test whether WebSocket correctly responds to a ping message sent from the server.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var url = "ws://localhost:8880/websocket/tests/hybi/pong";
     18var ws = new WebSocket(url);
    1819var closeEvent;
    19 var receivedMessage;
     20var message;
    2021
    2122ws.onopen = function()
    2223{
    23     debug("Connected");
    24     ws.close();
     24    debug("onopen() was called.");
    2525};
    2626
    27 ws.onmessage = function(messageEvent)
     27ws.onmessage = function(event)
    2828{
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     29    message = event.data;
     30    debug("onmessage() was called. (message = \"" + message + "\")");
     31    shouldBeEqualToString("message", "PASS");
    3132};
    3233
    3334ws.onclose = function(event)
    3435{
    35     debug("Closed");
    3636    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    3837    shouldBeTrue("closeEvent.wasClean");
    3938    finishJSTest();
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/send2_wsh.py

    r90726 r91243  
     1from mod_pywebsocket import stream
     2
     3
    14def web_socket_do_extra_handshake(request):
    25    pass # Always accept.
     
    58def web_socket_transfer_data(request):
    69    # send 2 messages in one packet.
    7     request.connection.write("\x00" + "first message" + "\xff" +
    8                              "\x00" + "second message" + "\xff")
     10    request.connection.write(stream.create_text_frame("first message") + stream.create_text_frame("second message"))
  • trunk/LayoutTests/http/tests/websocket/tests/hybi/too-long-payload.html

    r91242 r91243  
    1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
     1<!DOCTYPE HTML>
    22<html>
    33<head>
     
    99<div id="console"></div>
    1010<script type="text/javascript">
    11 description("WebSocket: Test client-initiated close.");
     11description("Tests whether WebSocket correctly aborts the connection when it receives a frame with too long payload.");
    1212
    1313window.jsTestIsAsync = true;
     
    1515    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
    1616
    17 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/client-close");
     17var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/hybi/too-long-payload");
    1818var closeEvent;
    19 var receivedMessage;
    2019
    2120ws.onopen = function()
    2221{
    23     debug("Connected");
    24     ws.close();
     22    debug("onopen() was called.");
    2523};
    2624
    27 ws.onmessage = function(messageEvent)
     25ws.onmessage = function(event)
    2826{
    29     debug("Received: " + messageEvent.data);
    30     receivedMessage = messageEvent.data;
     27    var message = event.data;
     28    testFailed("onmessage() was called. (message = \"" + message + "\")");
    3129};
    3230
    3331ws.onclose = function(event)
    3432{
    35     debug("Closed");
     33    debug("onclose() was called.");
    3634    closeEvent = event;
    37     shouldBeEqualToString("receivedMessage", "close_frame='\\xff\\x00'");
    38     shouldBeTrue("closeEvent.wasClean");
     35    shouldBeFalse("closeEvent.wasClean");
    3936    finishJSTest();
    4037};
  • trunk/LayoutTests/platform/chromium/test_expectations.txt

    r91238 r91243  
    4747
    4848BUGCR32018 SLOW : http/tests/websocket/tests/hixie76/frame-lengths.html = PASS
     49BUGCR32018 SLOW : http/tests/websocket/tests/hybi/frame-lengths.html = PASS
    4950BUGCR69513 DEBUG SLOW : html5lib/webkit-resumer.html = PASS
    5051
     
    106107WONTFIX SKIP : http/tests/eventsource/workers = PASS TIMEOUT FAIL
    107108WONTFIX SKIP : http/tests/websocket/tests/hixie76/workers/ = PASS TIMEOUT FAIL
     109WONTFIX SKIP : http/tests/websocket/tests/hybi/workers/ = PASS TIMEOUT FAIL
    108110WONTFIX SKIP : inspector/debugger/script-formatter.html = PASS TIMEOUT FAIL
    109111
     
    156158BUGWK60877 SKIP : loader/navigation-while-deferring-loads.html = FAIL
    157159BUGWK60877 SKIP : loader/load-defer-resume-crash.html = FAIL
    158 
    159 // Skipped until new WebSocket protocol is implemented.
    160 BUGWK50099 SKIP : http/tests/websocket/tests/hybi/ = PASS FAIL TIMEOUT
    161160
    162161// CSS3 Selectors3 test suite
     
    17221721// WebKit roll 52852 -> 52867
    17231722BUGCR32018 DEBUG : http/tests/websocket/tests/hixie76/simple-stress.html = PASS TEXT TIMEOUT
     1723BUGCR32018 DEBUG : http/tests/websocket/tests/hybi/simple-stress.html = PASS TEXT TIMEOUT
    17241724
    17251725// V8's implementation of getOwnPropertyNames has different results for built-in
     
    28012801BUGCR73092 WIN : fast/dom/gc-11.html = PASS TEXT
    28022802BUGCR73094 LINUX : http/tests/websocket/tests/hixie76/send-after-close-on-unload.html = PASS TIMEOUT
     2803BUGCR73094 LINUX : http/tests/websocket/tests/hybi/send-after-close-on-unload.html = PASS TIMEOUT
    28032804
    28042805BUGV8_1168 : fast/js/mozilla/eval/exhaustive-fun-normalcaller-indirect-strictcode.html = TEXT
     
    36463647
    36473648BUGWK61767 WIN DEBUG : http/tests/websocket/tests/hixie76/client-close.html = PASS TEXT
     3649BUGWK61767 WIN DEBUG : http/tests/websocket/tests/hybi/client-close.html = PASS TEXT
    36483650
    36493651BUGWK59782 WIN LINUX DEBUG : svg/dynamic-updates/SVGFEDropShadowElement-dom-dx-attr.html = TIMEOUT
  • trunk/LayoutTests/platform/mac/Skipped

    r90946 r91243  
    387387compositing/rtl/rtl-iframe-fixed.html
    388388compositing/rtl/rtl-iframe-relative.html
    389 
    390 # Skipped until new WebSocket protocol is implemented. http://webkit.org/b/50099
    391 http/tests/websocket/tests/hybi/
  • trunk/LayoutTests/platform/win/Skipped

    r91104 r91243  
    13471347plugins/form-value.html
    13481348
    1349 # Skipped until new WebSocket protocol is implemented. http://webkit.org/b/50099
    1350 http/tests/websocket/tests/hybi/
    1351 
    13521349# Needs generated results
    13531350css3/selectors3
  • trunk/Source/WebCore/ChangeLog

    r91242 r91243  
     12011-07-19  Yuta Kitamura  <yutak@chromium.org>
     2
     3        WebSocket: Implement hybi framing
     4        https://bugs.webkit.org/show_bug.cgi?id=64522
     5
     6        Reviewed by Kent Tamura.
     7
     8        Implement WebSocket framing protocol which is mainly described in
     9        <http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4> and
     10        <http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-6>.
     11
     12        Hybi protocol introduces a new frame format which is drastically different from
     13        the old one. Notable differences are:
     14        - Binary data support.
     15        - Fragmentation support: a single message can be fragmented to multiple frames.
     16        - Ping-pong support.
     17        - Masking: frame content of a client must be masked to prevent cross-protocol attacks.
     18
     19        This patch covers the following features:
     20        - Send a pong frame when a ping frame is received.
     21        - Receive fragmented frames.
     22        - Receive masked frames. (Servers do not have to mask frames, but they may if they wish.)
     23
     24        The following features are NOT implemented yet:
     25        - Send or receive binary messages.
     26        - Send a ping message.
     27        - Send fragmented frames. (It is unclear whether this is necessary.)
     28        - Rewrite the frame content by WebSocket protocol extensions (like frame compression).
     29
     30        New tests: http/tests/websocket/tests/hybi/broken-utf8.html
     31                   http/tests/websocket/tests/hybi/fragmented-control-frame.html
     32                   http/tests/websocket/tests/hybi/fragmented-frames.html
     33                   http/tests/websocket/tests/hybi/interleaved-fragments.html
     34                   http/tests/websocket/tests/hybi/long-control-frame.html
     35                   http/tests/websocket/tests/hybi/masked-frames.html
     36                   http/tests/websocket/tests/hybi/pong.html
     37                   http/tests/websocket/tests/hybi/reserved-bits.html
     38                   http/tests/websocket/tests/hybi/reserved-opcodes.html
     39                   http/tests/websocket/tests/hybi/too-long-payload.html
     40
     41        * websockets/WebSocketChannel.cpp:
     42        (WebCore::WebSocketChannel::WebSocketChannel):
     43        (WebCore::WebSocketChannel::send):
     44        The original content of send() was moved to a private method sendFrameHixie76().
     45        (WebCore::WebSocketChannel::fail):
     46        Stop handling incoming data after the WebSocket connection is failed.
     47        It was unclear to me whether we should do the same thing for hixie-76 connection;
     48        for now, I kept the original behavior.
     49        (WebCore::WebSocketChannel::processBuffer):
     50        (WebCore::WebSocketChannel::resumeTimerFired):
     51        (WebCore::WebSocketChannel::startClosingHandshake):
     52        (WebCore::WebSocketChannel::closingTimerFired):
     53        (WebCore::WebSocketChannel::parseFrame):
     54        (WebCore::WebSocketChannel::processFrame):
     55        (WebCore::WebSocketChannel::processFrameHixie76):
     56        (WebCore::WebSocketChannel::sendFrame):
     57        (WebCore::WebSocketChannel::sendFrameHixie76):
     58        * websockets/WebSocketChannel.h:
     59        (WebCore::WebSocketChannel::isNonControlOpCode):
     60        (WebCore::WebSocketChannel::isControlOpCode):
     61        (WebCore::WebSocketChannel::isReservedOpCode):
     62
    1632011-07-19  Nikolas Zimmermann  <nzimmermann@rim.com>
    264
  • trunk/Source/WebCore/websockets/WebSocketChannel.cpp

    r90704 r91243  
    4949#include "WebSocketHandshake.h"
    5050
    51 #include <wtf/text/CString.h>
    52 #include <wtf/text/WTFString.h>
    53 #include <wtf/text/StringHash.h>
     51#include <wtf/CryptographicallyRandomNumber.h>
    5452#include <wtf/Deque.h>
    5553#include <wtf/FastMalloc.h>
    5654#include <wtf/HashMap.h>
     55#include <wtf/text/CString.h>
     56#include <wtf/text/StringHash.h>
     57#include <wtf/text/WTFString.h>
     58
     59using namespace std;
    5760
    5861namespace WebCore {
    5962
    6063const double TCPMaximumSegmentLifetime = 2 * 60.0;
     64
     65// Constants for hybi-10 frame format.
     66const unsigned char finalBit = 0x80;
     67const unsigned char reserved1Bit = 0x40;
     68const unsigned char reserved2Bit = 0x20;
     69const unsigned char reserved3Bit = 0x10;
     70const unsigned char opCodeMask = 0xF;
     71const unsigned char maskBit = 0x80;
     72const unsigned char payloadLengthMask = 0x7F;
     73const size_t maxPayloadLengthWithoutExtendedLengthField = 125;
     74const size_t payloadLengthWithTwoByteExtendedLengthField = 126;
     75const size_t payloadLengthWithEightByteExtendedLengthField = 127;
     76const size_t maskingKeyWidthInBytes = 4;
     77
     78const WebSocketChannel::OpCode WebSocketChannel::OpCodeContinuation = 0x0;
     79const WebSocketChannel::OpCode WebSocketChannel::OpCodeText = 0x1;
     80const WebSocketChannel::OpCode WebSocketChannel::OpCodeBinary = 0x2;
     81const WebSocketChannel::OpCode WebSocketChannel::OpCodeClose = 0x8;
     82const WebSocketChannel::OpCode WebSocketChannel::OpCodePing = 0x9;
     83const WebSocketChannel::OpCode WebSocketChannel::OpCodePong = 0xA;
    6184
    6285WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol)
     
    7598    , m_identifier(0)
    7699    , m_useHixie76Protocol(true)
     100    , m_hasContinuousFrame(false)
    77101{
    78102    ASSERT(m_context->isDocument());
     
    103127}
    104128
    105 bool WebSocketChannel::send(const String& msg)
    106 {
    107     LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data());
    108     ASSERT(m_handle);
    109     ASSERT(!m_suspended);
    110     Vector<char> buf;
    111     buf.append('\0');  // frame type
    112     CString utf8 = msg.utf8();
    113     buf.append(utf8.data(), utf8.length());
    114     buf.append('\xff');  // frame end
    115     return m_handle->send(buf.data(), buf.size());
     129bool WebSocketChannel::send(const String& message)
     130{
     131    LOG(Network, "WebSocketChannel %p send %s", this, message.utf8().data());
     132    CString utf8 = message.utf8();
     133    if (m_useHixie76Protocol)
     134        return sendFrameHixie76(utf8.data(), utf8.length());
     135    return sendFrame(OpCodeText, utf8.data(), utf8.length());
    116136}
    117137
     
    141161    if (m_context)
    142162        m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, reason, 0, m_handshake->clientOrigin(), 0);
     163    if (!m_useHixie76Protocol) {
     164        // Hybi-10 specification explicitly states we must not continue to handle incoming data
     165        // once the WebSocket connection is failed (section 7.1.7).
     166        // FIXME: Should we do this in hixie-76 too?
     167        m_shouldDiscardReceivedData = true;
     168        if (m_buffer)
     169            skipBuffer(m_bufferSize); // Save memory.
     170        m_hasContinuousFrame = false;
     171        m_continuousFrameData.clear();
     172    }
    143173    if (m_handle && !m_closed)
    144174        m_handle->disconnect(); // Will call didClose().
     
    346376        return false;
    347377
     378    if (m_useHixie76Protocol)
     379        return processFrameHixie76();
     380
     381    return processFrame();
     382}
     383
     384void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer)
     385{
     386    ASSERT_UNUSED(timer, timer == &m_resumeTimer);
     387
     388    RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
     389    while (!m_suspended && m_client && m_buffer)
     390        if (!processBuffer())
     391            break;
     392    if (!m_suspended && m_client && m_closed && m_handle)
     393        didClose(m_handle.get());
     394}
     395
     396void WebSocketChannel::startClosingHandshake()
     397{
     398    LOG(Network, "WebSocketChannel %p closing %d %d", this, m_closing, m_receivedClosingHandshake);
     399    if (m_closing)
     400        return;
     401    ASSERT(m_handle);
     402    bool sentSuccessfully;
     403    if (m_useHixie76Protocol) {
     404        Vector<char> buf;
     405        buf.append('\xff');
     406        buf.append('\0');
     407        sentSuccessfully = m_handle->send(buf.data(), buf.size());
     408    } else
     409        sentSuccessfully = sendFrame(OpCodeClose, "", 0); // FIXME: Send status code and reason message.
     410
     411    if (!sentSuccessfully) {
     412        m_handle->disconnect();
     413        return;
     414    }
     415    m_closing = true;
     416    if (m_client)
     417        m_client->didStartClosingHandshake();
     418}
     419
     420void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer)
     421{
     422    LOG(Network, "WebSocketChannel %p closing timer", this);
     423    ASSERT_UNUSED(timer, &m_closingTimer == timer);
     424    if (m_handle)
     425        m_handle->disconnect();
     426}
     427
     428WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(FrameData& frame)
     429{
     430    const char* p = m_buffer;
     431    const char* bufferEnd = m_buffer + m_bufferSize;
     432
     433    if (m_bufferSize < 2)
     434        return FrameIncomplete;
     435
     436    unsigned char firstByte = *p++;
     437    unsigned char secondByte = *p++;
     438
     439    bool final = firstByte & finalBit;
     440    bool reserved1 = firstByte & reserved1Bit;
     441    bool reserved2 = firstByte & reserved2Bit;
     442    bool reserved3 = firstByte & reserved3Bit;
     443    OpCode opCode = firstByte & opCodeMask;
     444
     445    bool masked = secondByte & maskBit;
     446    uint64_t payloadLength64 = secondByte & payloadLengthMask;
     447    if (payloadLength64 > maxPayloadLengthWithoutExtendedLengthField) {
     448        int extendedPayloadLengthSize;
     449        if (payloadLength64 == payloadLengthWithTwoByteExtendedLengthField)
     450            extendedPayloadLengthSize = 2;
     451        else {
     452            ASSERT(payloadLength64 == payloadLengthWithEightByteExtendedLengthField);
     453            extendedPayloadLengthSize = 8;
     454        }
     455        if (bufferEnd - p < extendedPayloadLengthSize)
     456            return FrameIncomplete;
     457        payloadLength64 = 0;
     458        for (int i = 0; i < extendedPayloadLengthSize; ++i) {
     459            payloadLength64 <<= 8;
     460            payloadLength64 |= static_cast<unsigned char>(*p++);
     461        }
     462    }
     463
     464    // FIXME: UINT64_C(0x7FFFFFFFFFFFFFFF) should be used but it did not compile on Qt bots.
     465#if COMPILER(MSVC)
     466    static const uint64_t maxPayloadLength = 0x7FFFFFFFFFFFFFFFui64;
     467#else
     468    static const uint64_t maxPayloadLength = 0x7FFFFFFFFFFFFFFFull;
     469#endif
     470    size_t maskingKeyLength = masked ? maskingKeyWidthInBytes : 0;
     471    if (payloadLength64 > maxPayloadLength || payloadLength64 + maskingKeyLength > numeric_limits<size_t>::max()) {
     472        fail("WebSocket frame length too large: " + String::number(payloadLength64) + " bytes");
     473        return FrameError;
     474    }
     475    size_t payloadLength = static_cast<size_t>(payloadLength64);
     476
     477    if (static_cast<size_t>(bufferEnd - p) < maskingKeyLength + payloadLength)
     478        return FrameIncomplete;
     479
     480    if (masked) {
     481        const char* maskingKey = p;
     482        char* payload = const_cast<char*>(p + maskingKeyWidthInBytes);
     483        for (size_t i = 0; i < payloadLength; ++i)
     484            payload[i] ^= maskingKey[i % maskingKeyWidthInBytes]; // Unmask the payload.
     485    }
     486
     487    frame.opCode = opCode;
     488    frame.final = final;
     489    frame.reserved1 = reserved1;
     490    frame.reserved2 = reserved2;
     491    frame.reserved3 = reserved3;
     492    frame.masked = masked;
     493    frame.payload = p + maskingKeyLength;
     494    frame.payloadLength = payloadLength;
     495    frame.frameEnd = p + maskingKeyLength + payloadLength;
     496    return FrameOK;
     497}
     498
     499bool WebSocketChannel::processFrame()
     500{
     501    ASSERT(m_buffer);
     502
     503    FrameData frame;
     504    if (parseFrame(frame) != FrameOK)
     505        return false;
     506
     507    // Validate the frame data.
     508    if (isReservedOpCode(frame.opCode)) {
     509        fail("Unrecognized frame opcode: " + String::number(frame.opCode));
     510        return false;
     511    }
     512
     513    if (frame.reserved1 || frame.reserved2 || frame.reserved3) {
     514        fail("One or more reserved bits are on: reserved1 = " + String::number(frame.reserved1) + ", reserved2 = " + String::number(frame.reserved2) + ", reserved3 = " + String::number(frame.reserved3));
     515        return false;
     516    }
     517
     518    // All control frames must not be fragmented.
     519    if (isControlOpCode(frame.opCode) && !frame.final) {
     520        fail("Received fragmented control frame: opcode = " + String::number(frame.opCode));
     521        return false;
     522    }
     523
     524    // All control frames must have a payload of 125 bytes or less, which means the frame must not contain
     525    // the "extended payload length" field.
     526    if (isControlOpCode(frame.opCode) && frame.payloadLength > maxPayloadLengthWithoutExtendedLengthField) {
     527        fail("Received control frame having too long payload: " + String::number(frame.payloadLength) + " bytes");
     528        return false;
     529    }
     530
     531    // A new data frame is received before the previous continuous frame finishes.
     532    // Note that control frames are allowed to come in the middle of continuous frames.
     533    if (m_hasContinuousFrame && frame.opCode != OpCodeContinuation && !isControlOpCode(frame.opCode)) {
     534        fail("Received new data frame but previous continuous frame is unfinished.");
     535        return false;
     536    }
     537
     538    switch (frame.opCode) {
     539    case OpCodeContinuation:
     540        // Throw away content of a binary message because binary messages are not supported yet.
     541        if (m_continuousFrameOpCode == OpCodeText)
     542            m_continuousFrameData.append(frame.payload, frame.payloadLength);
     543        skipBuffer(frame.frameEnd - m_buffer);
     544        if (frame.final) {
     545            // onmessage handler may eventually call the other methods of this channel,
     546            // so we should pretend that we have finished to read this frame and
     547            // make sure that the member variables are in a consistent state before
     548            // the handler is invoked.
     549            // Vector<char>::swap() is used here to clear m_continuousFrameData.
     550            Vector<char> continuousFrameData;
     551            m_continuousFrameData.swap(continuousFrameData);
     552            m_hasContinuousFrame = false;
     553            if (m_continuousFrameOpCode == OpCodeText) {
     554                String message = String::fromUTF8(continuousFrameData.data(), continuousFrameData.size());
     555                if (message.isNull())
     556                    fail("Could not decode a text frame as UTF-8.");
     557                else
     558                    m_client->didReceiveMessage(message);
     559            } else if (m_continuousFrameOpCode == OpCodeBinary) {
     560                ASSERT(m_continuousFrameData.isEmpty());
     561                fail("Received a binary frame which is not supported yet.");
     562            }
     563        }
     564        break;
     565
     566    case OpCodeText:
     567        if (frame.final) {
     568            String message = String::fromUTF8(frame.payload, frame.payloadLength);
     569            skipBuffer(frame.frameEnd - m_buffer);
     570            if (message.isNull())
     571                fail("Could not decode a text frame as UTF-8.");
     572            else
     573                m_client->didReceiveMessage(message);
     574        } else {
     575            m_hasContinuousFrame = true;
     576            m_continuousFrameOpCode = OpCodeText;
     577            ASSERT(m_continuousFrameData.isEmpty());
     578            m_continuousFrameData.append(frame.payload, frame.payloadLength);
     579            skipBuffer(frame.frameEnd - m_buffer);
     580        }
     581        break;
     582
     583    case OpCodeBinary:
     584        if (frame.final)
     585            fail("Received a binary frame which is not supported yet.");
     586        else {
     587            m_hasContinuousFrame = true;
     588            m_continuousFrameOpCode = OpCodeBinary;
     589            ASSERT(m_continuousFrameData.isEmpty());
     590            // Do not store data of a binary message to m_continuousFrameData to save memory.
     591            skipBuffer(frame.frameEnd - m_buffer);
     592        }
     593        break;
     594
     595    case OpCodeClose:
     596        // FIXME: Handle payload.
     597        skipBuffer(frame.frameEnd - m_buffer);
     598        m_receivedClosingHandshake = true;
     599        startClosingHandshake();
     600        if (m_closing)
     601            m_handle->close(); // Close after sending a close frame.
     602        break;
     603
     604    case OpCodePing: {
     605        bool result = sendFrame(OpCodePong, frame.payload, frame.payloadLength);
     606        skipBuffer(frame.frameEnd - m_buffer);
     607        if (!result)
     608            fail("Failed to send a pong frame.");
     609        break;
     610    }
     611
     612    case OpCodePong:
     613        // A server may send a pong in response to our ping, or an unsolicited pong which is not associated with
     614        // any specific ping. Either way, there's nothing to do on receipt of pong.
     615        skipBuffer(frame.frameEnd - m_buffer);
     616        break;
     617
     618    default:
     619        ASSERT_NOT_REACHED();
     620        skipBuffer(frame.frameEnd - m_buffer);
     621        break;
     622    }
     623
     624    return m_buffer;
     625}
     626
     627bool WebSocketChannel::processFrameHixie76()
     628{
    348629    const char* nextFrame = m_buffer;
    349630    const char* p = m_buffer;
     
    355636        bool errorFrame = false;
    356637        while (p < end) {
    357             if (length > std::numeric_limits<size_t>::max() / 128) {
     638            if (length > numeric_limits<size_t>::max() / 128) {
    358639                LOG(Network, "frame length overflow %lu", static_cast<unsigned long>(length));
    359640                errorFrame = true;
     
    363644            unsigned char msgByte = static_cast<unsigned char>(*p);
    364645            unsigned int lengthMsgByte = msgByte & 0x7f;
    365             if (newLength > std::numeric_limits<size_t>::max() - lengthMsgByte) {
     646            if (newLength > numeric_limits<size_t>::max() - lengthMsgByte) {
    366647                LOG(Network, "frame length overflow %lu+%u", static_cast<unsigned long>(newLength), lengthMsgByte);
    367648                errorFrame = true;
     
    428709}
    429710
    430 void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer)
    431 {
    432     ASSERT_UNUSED(timer, timer == &m_resumeTimer);
    433 
    434     RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
    435     while (!m_suspended && m_client && m_buffer)
    436         if (!processBuffer())
    437             break;
    438     if (!m_suspended && m_client && m_closed && m_handle)
    439         didClose(m_handle.get());
    440 }
    441 
    442 void WebSocketChannel::startClosingHandshake()
    443 {
    444     LOG(Network, "WebSocketChannel %p closing %d %d", this, m_closing, m_receivedClosingHandshake);
    445     if (m_closing)
    446         return;
     711bool WebSocketChannel::sendFrame(OpCode opCode, const char* data, size_t dataLength)
     712{
    447713    ASSERT(m_handle);
    448     Vector<char> buf;
    449     buf.append('\xff');
    450     buf.append('\0');
    451     if (!m_handle->send(buf.data(), buf.size())) {
    452         m_handle->disconnect();
    453         return;
    454     }
    455     m_closing = true;
    456     if (m_client)
    457         m_client->didStartClosingHandshake();
    458 }
    459 
    460 void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer)
    461 {
    462     LOG(Network, "WebSocketChannel %p closing timer", this);
    463     ASSERT_UNUSED(timer, &m_closingTimer == timer);
    464     if (m_handle)
    465         m_handle->disconnect();
     714    ASSERT(!m_suspended);
     715
     716    Vector<char> frame;
     717    ASSERT(!(opCode & ~opCodeMask)); // Checks whether "opCode" fits in the range of opCodes.
     718    frame.append(finalBit | opCode);
     719    if (dataLength <= maxPayloadLengthWithoutExtendedLengthField)
     720        frame.append(maskBit | dataLength);
     721    else if (dataLength <= 0xFFFF) {
     722        frame.append(maskBit | payloadLengthWithTwoByteExtendedLengthField);
     723        frame.append((dataLength & 0xFF00) >> 8);
     724        frame.append(dataLength & 0xFF);
     725    } else {
     726        frame.append(maskBit | payloadLengthWithEightByteExtendedLengthField);
     727        char extendedPayloadLength[8];
     728        size_t remaining = dataLength;
     729        // Fill the length into extendedPayloadLength in the network byte order.
     730        for (int i = 0; i < 8; ++i) {
     731            extendedPayloadLength[7 - i] = remaining & 0xFF;
     732            remaining >>= 8;
     733        }
     734        ASSERT(!remaining);
     735        frame.append(extendedPayloadLength, 8);
     736    }
     737
     738    // Mask the frame.
     739    size_t maskingKeyStart = frame.size();
     740    frame.grow(frame.size() + maskingKeyWidthInBytes); // Add placeholder for masking key. Will be overwritten.
     741    size_t payloadStart = frame.size();
     742    frame.append(data, dataLength);
     743
     744    cryptographicallyRandomValues(frame.data() + maskingKeyStart, maskingKeyWidthInBytes);
     745    for (size_t i = 0; i < dataLength; ++i)
     746        frame[payloadStart + i] ^= frame[maskingKeyStart + i % maskingKeyWidthInBytes];
     747
     748    return m_handle->send(frame.data(), frame.size());
     749}
     750
     751bool WebSocketChannel::sendFrameHixie76(const char* data, size_t dataLength)
     752{
     753    ASSERT(m_handle);
     754    ASSERT(!m_suspended);
     755
     756    Vector<char> frame;
     757    frame.append('\0'); // Frame type.
     758    frame.append(data, dataLength);
     759    frame.append('\xff'); // Frame end.
     760    return m_handle->send(frame.data(), frame.size());
    466761}
    467762
  • trunk/Source/WebCore/websockets/WebSocketChannel.h

    r90704 r91243  
    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
     
    8989        void closingTimerFired(Timer<WebSocketChannel>*);
    9090
     91        // Hybi-10 opcodes.
     92        typedef unsigned int OpCode;
     93        static const OpCode OpCodeContinuation;
     94        static const OpCode OpCodeText;
     95        static const OpCode OpCodeBinary;
     96        static const OpCode OpCodeClose;
     97        static const OpCode OpCodePing;
     98        static const OpCode OpCodePong;
     99
     100        static bool isNonControlOpCode(OpCode opCode) { return opCode == OpCodeContinuation || opCode == OpCodeText || opCode == OpCodeBinary; }
     101        static bool isControlOpCode(OpCode opCode) { return opCode == OpCodeClose || opCode == OpCodePing || opCode == OpCodePong; }
     102        static bool isReservedOpCode(OpCode opCode) { return !isNonControlOpCode(opCode) && !isControlOpCode(opCode); }
     103
     104        enum ParseFrameResult {
     105            FrameOK,
     106            FrameIncomplete,
     107            FrameError
     108        };
     109
     110        struct FrameData {
     111            OpCode opCode;
     112            bool final;
     113            bool reserved1;
     114            bool reserved2;
     115            bool reserved3;
     116            bool masked;
     117            const char* payload;
     118            size_t payloadLength;
     119            const char* frameEnd;
     120        };
     121
     122        ParseFrameResult parseFrame(FrameData&); // May modify part of m_buffer to unmask the frame.
     123
     124        bool processFrame();
     125        bool processFrameHixie76();
     126
     127        bool sendFrame(OpCode, const char* data, size_t dataLength);
     128        bool sendFrameHixie76(const char* data, size_t dataLength);
     129
    91130        ScriptExecutionContext* m_context;
    92131        WebSocketChannelClient* m_client;
     
    108147
    109148        bool m_useHixie76Protocol;
     149
     150        // Private members only for hybi-10 protocol.
     151        bool m_hasContinuousFrame;
     152        OpCode m_continuousFrameOpCode;
     153        Vector<char> m_continuousFrameData;
    110154    };
    111155
Note: See TracChangeset for help on using the changeset viewer.