Changeset 87464 in webkit


Ignore:
Timestamp:
May 26, 2011 8:24:02 PM (13 years ago)
Author:
yutak@chromium.org
Message:

2011-05-26 Yuta Kitamura <yutak@chromium.org>

Reviewed by Kent Tamura.

WebSocket closing handshake
https://bugs.webkit.org/show_bug.cgi?id=35721

  • http/tests/websocket/tests/client-close-expected.txt: Added.
  • http/tests/websocket/tests/client-close.html: Added. Test client-initiated close.
  • http/tests/websocket/tests/client-close_wsh.py: Added.
  • http/tests/websocket/tests/close-before-open-expected.txt: Add a new console message.
  • http/tests/websocket/tests/close-event-expected.txt:
  • http/tests/websocket/tests/close-event.html: Test if closeEvent.wasClean is true.
  • http/tests/websocket/tests/close-unref-websocket-expected.txt: Add a new console message.
  • http/tests/websocket/tests/frame-length-longer-than-buffer_wsh.py: We need to stop pywebsocket from starting the closing handshake. Otherwise, pywebsocket waits for a close frame to arrive and this test will time out.
  • http/tests/websocket/tests/server-close-expected.txt: Added.
  • http/tests/websocket/tests/server-close.html: Added. Test server-initiated close.
  • http/tests/websocket/tests/server-close_wsh.py: Added.
  • http/tests/websocket/tests/websocket-event-target-expected.txt: Add a new console message.

2011-05-26 Yuta Kitamura <yutak@chromium.org>

Reviewed by Kent Tamura.

WebSocket closing handshake
https://bugs.webkit.org/show_bug.cgi?id=35721

Implement WebSocket closing handshake based on Ian Hickson's
WebSocket protocol draft 76.

Tests: http/tests/websocket/tests/client-close.html

http/tests/websocket/tests/server-close.html

  • platform/network/SocketStreamHandleBase.cpp: (WebCore::SocketStreamHandleBase::send): Do not send a message if we are in Closing state. (WebCore::SocketStreamHandleBase::close): Do not disconnect if we have pending data which have not been sent yet. In this case, the actual disconnection will happen in sendPendingData(). (WebCore::SocketStreamHandleBase::disconnect): Renamed from close(). Disconnect the connection immediately. (WebCore::SocketStreamHandleBase::sendPendingData):
  • platform/network/SocketStreamHandleBase.h:
  • websockets/ThreadableWebSocketChannelClientWrapper.cpp: Add didStartClosingHandshake(). Add a function argument (ClosingHandshakeCompletionStatus) to didClose(). (WebCore::ThreadableWebSocketChannelClientWrapper::didStartClosingHandshake): (WebCore::ThreadableWebSocketChannelClientWrapper::didClose): (WebCore::ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback): (WebCore::ThreadableWebSocketChannelClientWrapper::didCloseCallback):
  • websockets/ThreadableWebSocketChannelClientWrapper.h:
  • websockets/WebSocket.cpp: (WebCore::WebSocket::send): (WebCore::WebSocket::close): Fail if close() is attempted before the connection is established. Otherwise, set the state to CLOSING and start the closing handshake. (WebCore::WebSocket::bufferedAmount): If the state is CLOSING, we need to consider buffered data in m_channel and sent after close(). (WebCore::WebSocket::didConnect): (WebCore::WebSocket::didReceiveMessage): We need to invoke message event in CLOSING state as well as OPEN state. (WebCore::WebSocket::didReceiveMessageError): (WebCore::WebSocket::didStartClosingHandshake): (WebCore::WebSocket::didClose):
  • websockets/WebSocket.h:
  • websockets/WebSocketChannel.cpp: (WebCore::WebSocketChannel::WebSocketChannel): (WebCore::WebSocketChannel::close): Start the closing handshake. (WebCore::WebSocketChannel::disconnect): Disconnect the socket stream, instead of close. (WebCore::WebSocketChannel::didClose): (WebCore::WebSocketChannel::didReceiveData): Ditto. (WebCore::WebSocketChannel::didFail): Ditto. (WebCore::WebSocketChannel::processBuffer): Ditto. Handle 0xFF 0x00 byte sequence, and discard received data once the closing handshake has started. (WebCore::WebSocketChannel::startClosingHandshake): Send 0xFF 0x00 byte sequence. (WebCore::WebSocketChannel::closingTimerFired): Disconnect the socket stream if the closing handshake has timed out.
  • websockets/WebSocketChannel.h: m_closing is true if "the WebSocket closing handshake has started" (as stated in the protocol specification).
  • websockets/WebSocketChannelClient.h: (WebCore::WebSocketChannelClient::didStartClosingHandshake): Added. (WebCore::WebSocketChannelClient::didClose): Add closingHandshakeCompletion parameter.
  • websockets/WorkerThreadableWebSocketChannel.cpp: Add closingHandshakeCompletion parameter to didClose(), and add didStartClosingHandshake(). (WebCore::WorkerThreadableWebSocketChannel::Peer::close): (WebCore::workerContextDidStartClosingHandshake): (WebCore::WorkerThreadableWebSocketChannel::Peer::didStartClosingHandshake): (WebCore::workerContextDidClose): (WebCore::WorkerThreadableWebSocketChannel::Peer::didClose):
  • websockets/WorkerThreadableWebSocketChannel.h:

2011-05-26 Yuta Kitamura <yutak@chromium.org>

Reviewed by Kent Tamura.

WebSocket closing handshake
https://bugs.webkit.org/show_bug.cgi?id=35721

  • Scripts/webkitpy/thirdparty/init.py: Pull in pywebsocket 0.6b1. We need to update pywebsocket to get the right behavior of closing handshake.
Location:
trunk
Files:
2 added
21 edited
4 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r87461 r87464  
     12011-05-26  Yuta Kitamura  <yutak@chromium.org>
     2
     3        Reviewed by Kent Tamura.
     4
     5        WebSocket closing handshake
     6        https://bugs.webkit.org/show_bug.cgi?id=35721
     7
     8        * http/tests/websocket/tests/client-close-expected.txt: Added.
     9        * http/tests/websocket/tests/client-close.html: Added. Test client-initiated close.
     10        * http/tests/websocket/tests/client-close_wsh.py: Added.
     11        * http/tests/websocket/tests/close-before-open-expected.txt: Add a new console message.
     12        * http/tests/websocket/tests/close-event-expected.txt:
     13        * http/tests/websocket/tests/close-event.html: Test if closeEvent.wasClean is true.
     14        * http/tests/websocket/tests/close-unref-websocket-expected.txt: Add a new console message.
     15        * http/tests/websocket/tests/frame-length-longer-than-buffer_wsh.py:
     16        We need to stop pywebsocket from starting the closing handshake. Otherwise, pywebsocket
     17        waits for a close frame to arrive and this test will time out.
     18        * http/tests/websocket/tests/server-close-expected.txt: Added.
     19        * http/tests/websocket/tests/server-close.html: Added. Test server-initiated close.
     20        * http/tests/websocket/tests/server-close_wsh.py: Added.
     21        * http/tests/websocket/tests/websocket-event-target-expected.txt: Add a new console message.
     22
    1232011-05-26  MORITA Hajime  <morrita@google.com>
    224
  • trunk/LayoutTests/http/tests/websocket/tests/client-close-expected.txt

    r87463 r87464  
    1 Test if Web Socket fires close event when WebSocket is opened and closed fore open event is received.
     1WebSocket: Test client-initiated close.
    22
    33On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
    44
    5 closed
     5Connected
     6Closed
     7PASS closeEvent.wasClean is true
    68PASS successfullyParsed is true
    79
  • trunk/LayoutTests/http/tests/websocket/tests/client-close.html

    r87463 r87464  
    99<div id="console"></div>
    1010<script type="text/javascript">
    11 description("Make sure WebSocket fires CloseEvent when closed.");
     11description("WebSocket: Test client-initiated close.");
    1212
    1313window.jsTestIsAsync = true;
    1414
    15 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/simple");
     15var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/client-close");
     16var closeEvent;
    1617
    1718ws.onopen = function()
    1819{
    19     debug("WebSocket is open");
     20    debug("Connected");
     21    ws.close();
    2022};
    2123
    2224ws.onmessage = function(messageEvent)
    2325{
    24     debug("Received: '" + messageEvent.data + "'");
     26    debug("Received: " + messageEvent.data);
    2527};
    2628
    27 var closeEvent;
    28 var closeEventType;
    2929ws.onclose = function(event)
    3030{
    3131    debug("Closed");
    3232    closeEvent = event;
    33     closeEventType = closeEvent.type;
    34     shouldBe("closeEventType", '"close"')
    35     shouldBeTrue("'wasClean' in closeEvent");
    36     shouldBeTrue("Object.getPrototypeOf(closeEvent) === CloseEvent.prototype");
    37     shouldBeTrue("Object.getPrototypeOf(closeEvent) !== Event.prototype");
     33    shouldBeTrue("closeEvent.wasClean");
    3834    finishJSTest();
    3935};
  • trunk/LayoutTests/http/tests/websocket/tests/close-before-open-expected.txt

    r87135 r87464  
     1CONSOLE MESSAGE: line 0: WebSocket is closed before the connection is established.
    12Test if Web Socket fires close event when WebSocket is opened and closed fore open event is received.
    23
  • trunk/LayoutTests/http/tests/websocket/tests/close-event-expected.txt

    r86315 r87464  
    88PASS closeEventType is "close"
    99PASS 'wasClean' in closeEvent is true
     10PASS closeEvent.wasClean is true
    1011PASS Object.getPrototypeOf(closeEvent) === CloseEvent.prototype is true
    1112PASS Object.getPrototypeOf(closeEvent) !== Event.prototype is true
  • trunk/LayoutTests/http/tests/websocket/tests/close-event.html

    r86315 r87464  
    3434    shouldBe("closeEventType", '"close"')
    3535    shouldBeTrue("'wasClean' in closeEvent");
     36    shouldBeTrue("closeEvent.wasClean");
    3637    shouldBeTrue("Object.getPrototypeOf(closeEvent) === CloseEvent.prototype");
    3738    shouldBeTrue("Object.getPrototypeOf(closeEvent) !== Event.prototype");
  • trunk/LayoutTests/http/tests/websocket/tests/close-unref-websocket-expected.txt

    r82088 r87464  
     1CONSOLE MESSAGE: line 0: WebSocket is closed before the connection is established.
    12Test if Web Socket is closed while handshaking and unreferenced, it should fire close event at most once.
    23
  • trunk/LayoutTests/http/tests/websocket/tests/frame-length-longer-than-buffer_wsh.py

    r51829 r87464  
    11def web_socket_do_extra_handshake(request):
    2   pass
     2    pass
    33
    44def web_socket_transfer_data(request):
    5   msg = "\0hello\xff"
    6   msg += "\x80\x81\x01"  # skip 1*128+1 bytes.
    7   msg += "\x01\xff"
    8   msg += "\0should be skipped\xff"
    9   request.connection.write(msg)
    10   print msg
     5    msg = "\0hello\xff"
     6    msg += "\x80\x81\x01" # Skip 1*128+1 bytes.
     7    msg += "\x01\xff"
     8    msg += "\0should be skipped\xff"
     9    request.connection.write(msg)
     10    raise Exception("Abort the connection") # Prevents pywebsocket from starting closing handshake.
  • trunk/LayoutTests/http/tests/websocket/tests/server-close-expected.txt

    r87463 r87464  
    1 Test if Web Socket fires close event when WebSocket is opened and closed fore open event is received.
     1WebSocket: Test server-initiated close.
    22
    33On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
    44
    5 closed
     5Connected
     6Closed
     7PASS closeEvent.wasClean is true
    68PASS successfullyParsed is true
    79
  • trunk/LayoutTests/http/tests/websocket/tests/server-close.html

    r87463 r87464  
    99<div id="console"></div>
    1010<script type="text/javascript">
    11 description("Make sure WebSocket fires CloseEvent when closed.");
     11description("WebSocket: Test server-initiated close.");
    1212
    1313window.jsTestIsAsync = true;
    1414
    15 var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/simple");
     15var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/server-close");
     16var closeEvent;
    1617
    1718ws.onopen = function()
    1819{
    19     debug("WebSocket is open");
     20    debug("Connected");
    2021};
    2122
    2223ws.onmessage = function(messageEvent)
    2324{
    24     debug("Received: '" + messageEvent.data + "'");
     25    debug("Received: " + messageEvent.data);
    2526};
    2627
    27 var closeEvent;
    28 var closeEventType;
    2928ws.onclose = function(event)
    3029{
    3130    debug("Closed");
    3231    closeEvent = event;
    33     closeEventType = closeEvent.type;
    34     shouldBe("closeEventType", '"close"')
    35     shouldBeTrue("'wasClean' in closeEvent");
    36     shouldBeTrue("Object.getPrototypeOf(closeEvent) === CloseEvent.prototype");
    37     shouldBeTrue("Object.getPrototypeOf(closeEvent) !== Event.prototype");
     32    shouldBeTrue("closeEvent.wasClean");
    3833    finishJSTest();
    3934};
  • trunk/LayoutTests/http/tests/websocket/tests/websocket-event-target-expected.txt

    r53676 r87464  
     1CONSOLE MESSAGE: line 0: WebSocket is closed before the connection is established.
    12Make sure WebSocket object acts as EventTarget.
    23
  • trunk/Source/WebCore/ChangeLog

    r87462 r87464  
     12011-05-26  Yuta Kitamura  <yutak@chromium.org>
     2
     3        Reviewed by Kent Tamura.
     4
     5        WebSocket closing handshake
     6        https://bugs.webkit.org/show_bug.cgi?id=35721
     7
     8        Implement WebSocket closing handshake based on Ian Hickson's
     9        WebSocket protocol draft 76.
     10
     11        Tests: http/tests/websocket/tests/client-close.html
     12               http/tests/websocket/tests/server-close.html
     13
     14        * platform/network/SocketStreamHandleBase.cpp:
     15        (WebCore::SocketStreamHandleBase::send):
     16        Do not send a message if we are in Closing state.
     17        (WebCore::SocketStreamHandleBase::close):
     18        Do not disconnect if we have pending data which have not been sent yet.
     19        In this case, the actual disconnection will happen in sendPendingData().
     20        (WebCore::SocketStreamHandleBase::disconnect):
     21        Renamed from close(). Disconnect the connection immediately.
     22        (WebCore::SocketStreamHandleBase::sendPendingData):
     23        * platform/network/SocketStreamHandleBase.h:
     24        * websockets/ThreadableWebSocketChannelClientWrapper.cpp:
     25        Add didStartClosingHandshake(). Add a function argument (ClosingHandshakeCompletionStatus)
     26        to didClose().
     27        (WebCore::ThreadableWebSocketChannelClientWrapper::didStartClosingHandshake):
     28        (WebCore::ThreadableWebSocketChannelClientWrapper::didClose):
     29        (WebCore::ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback):
     30        (WebCore::ThreadableWebSocketChannelClientWrapper::didCloseCallback):
     31        * websockets/ThreadableWebSocketChannelClientWrapper.h:
     32        * websockets/WebSocket.cpp:
     33        (WebCore::WebSocket::send):
     34        (WebCore::WebSocket::close):
     35        Fail if close() is attempted before the connection is established.
     36        Otherwise, set the state to CLOSING and start the closing handshake.
     37        (WebCore::WebSocket::bufferedAmount):
     38        If the state is CLOSING, we need to consider buffered data in m_channel and sent after close().
     39        (WebCore::WebSocket::didConnect):
     40        (WebCore::WebSocket::didReceiveMessage):
     41        We need to invoke message event in CLOSING state as well as OPEN state.
     42        (WebCore::WebSocket::didReceiveMessageError):
     43        (WebCore::WebSocket::didStartClosingHandshake):
     44        (WebCore::WebSocket::didClose):
     45        * websockets/WebSocket.h:
     46        * websockets/WebSocketChannel.cpp:
     47        (WebCore::WebSocketChannel::WebSocketChannel):
     48        (WebCore::WebSocketChannel::close):
     49        Start the closing handshake.
     50        (WebCore::WebSocketChannel::disconnect):
     51        Disconnect the socket stream, instead of close.
     52        (WebCore::WebSocketChannel::didClose):
     53        (WebCore::WebSocketChannel::didReceiveData): Ditto.
     54        (WebCore::WebSocketChannel::didFail): Ditto.
     55        (WebCore::WebSocketChannel::processBuffer):
     56        Ditto.
     57        Handle 0xFF 0x00 byte sequence, and discard received data once the closing handshake has started.
     58        (WebCore::WebSocketChannel::startClosingHandshake):
     59        Send 0xFF 0x00 byte sequence.
     60        (WebCore::WebSocketChannel::closingTimerFired):
     61        Disconnect the socket stream if the closing handshake has timed out.
     62        * websockets/WebSocketChannel.h:
     63        m_closing is true if "the WebSocket closing handshake has started" (as stated in the protocol
     64        specification).
     65        * websockets/WebSocketChannelClient.h:
     66        (WebCore::WebSocketChannelClient::didStartClosingHandshake): Added.
     67        (WebCore::WebSocketChannelClient::didClose): Add closingHandshakeCompletion parameter.
     68        * websockets/WorkerThreadableWebSocketChannel.cpp:
     69        Add closingHandshakeCompletion parameter to didClose(), and add didStartClosingHandshake().
     70        (WebCore::WorkerThreadableWebSocketChannel::Peer::close):
     71        (WebCore::workerContextDidStartClosingHandshake):
     72        (WebCore::WorkerThreadableWebSocketChannel::Peer::didStartClosingHandshake):
     73        (WebCore::workerContextDidClose):
     74        (WebCore::WorkerThreadableWebSocketChannel::Peer::didClose):
     75        * websockets/WorkerThreadableWebSocketChannel.h:
     76
    1772011-05-26  David Levin  <levin@chromium.org>
    278
  • trunk/Source/WebCore/platform/network/SocketStreamHandleBase.cpp

    r51934 r87464  
    5353bool SocketStreamHandleBase::send(const char* data, int length)
    5454{
    55     if (m_state == Connecting)
     55    if (m_state == Connecting || m_state == Closing)
    5656        return false;
    5757    if (!m_buffer.isEmpty()) {
     
    7979void SocketStreamHandleBase::close()
    8080{
     81    if (m_state == Closed)
     82        return;
     83    m_state = Closing;
     84    if (!m_buffer.isEmpty())
     85        return;
     86    disconnect();
     87}
     88
     89void SocketStreamHandleBase::disconnect()
     90{
    8191    RefPtr<SocketStreamHandle> protect(static_cast<SocketStreamHandle*>(this)); // platformClose calls the client, which may make the handle get deallocated immediately.
    8292
     
    93103bool SocketStreamHandleBase::sendPendingData()
    94104{
    95     if (m_state != Open)
     105    if (m_state != Open && m_state != Closing)
    96106        return false;
    97     if (m_buffer.isEmpty())
    98         return false;
     107    if (m_buffer.isEmpty()) {
     108        if (m_state == Open)
     109            return false;
     110        if (m_state == Closing) {
     111            disconnect();
     112            return false;
     113        }
     114    }
    99115    int bytesWritten = platformSend(m_buffer.data(), m_buffer.size());
    100116    if (bytesWritten <= 0)
  • trunk/Source/WebCore/platform/network/SocketStreamHandleBase.h

    r86732 r87464  
    4949
    5050        bool send(const char* data, int length);
    51         void close();
     51        void close(); // Disconnect after all data in buffer are sent.
     52        void disconnect();
    5253        int bufferedAmount() const { return m_buffer.size(); }
    5354
  • trunk/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.cpp

    r86829 r87464  
    111111}
    112112
    113 void ThreadableWebSocketChannelClientWrapper::didClose(unsigned long unhandledBufferedAmount)
     113void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshake()
    114114{
    115     m_pendingTasks.append(createCallbackTask(&ThreadableWebSocketChannelClientWrapper::didCloseCallback, AllowCrossThreadAccess(this), unhandledBufferedAmount));
     115    m_pendingTasks.append(createCallbackTask(&ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback, AllowCrossThreadAccess(this)));
     116    if (!m_suspended)
     117        processPendingTasks();
     118}
     119
     120void ThreadableWebSocketChannelClientWrapper::didClose(unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion)
     121{
     122    m_pendingTasks.append(createCallbackTask(&ThreadableWebSocketChannelClientWrapper::didCloseCallback, AllowCrossThreadAccess(this), unhandledBufferedAmount, closingHandshakeCompletion));
    116123    if (!m_suspended)
    117124        processPendingTasks();
     
    152159}
    153160
    154 void ThreadableWebSocketChannelClientWrapper::didCloseCallback(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long unhandledBufferedAmount)
     161void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
    155162{
    156163    ASSERT_UNUSED(context, !context);
    157164    if (wrapper->m_client)
    158         wrapper->m_client->didClose(unhandledBufferedAmount);
     165        wrapper->m_client->didStartClosingHandshake();
     166}
     167
     168void ThreadableWebSocketChannelClientWrapper::didCloseCallback(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion)
     169{
     170    ASSERT_UNUSED(context, !context);
     171    if (wrapper->m_client)
     172        wrapper->m_client->didClose(unhandledBufferedAmount, closingHandshakeCompletion);
    159173}
    160174
  • trunk/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h

    r86829 r87464  
    3636#include "PlatformString.h"
    3737#include "ScriptExecutionContext.h"
     38#include "WebSocketChannelClient.h"
    3839#include <wtf/Forward.h>
    3940#include <wtf/OwnPtr.h>
     
    6364    void didConnect();
    6465    void didReceiveMessage(const String& message);
    65     void didClose(unsigned long unhandledBufferedAmount);
     66    void didStartClosingHandshake();
     67    void didClose(unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus);
    6668
    6769    void suspend();
     
    7476    static void didConnectCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>);
    7577    static void didReceiveMessageCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>, String message);
    76     static void didCloseCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>, unsigned long unhandledBufferedAmount);
     78    static void didStartClosingHandshakeCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>);
     79    static void didCloseCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus);
    7780
    7881    WebSocketChannelClient* m_client;
  • trunk/Source/WebCore/websockets/WebSocket.cpp

    r86542 r87464  
    165165    }
    166166    // No exception is raised if the connection was once established but has subsequently been closed.
    167     if (m_state == CLOSED) {
     167    if (m_state == CLOSING || m_state == CLOSED) {
    168168        m_bufferedAmountAfterClose += message.utf8().length() + 2; // 2 for frameing
    169169        return false;
     
    177177{
    178178    LOG(Network, "WebSocket %p close", this);
    179     if (m_state == CLOSED)
    180         return;
    181     m_state = CLOSED;
     179    if (m_state == CLOSING || m_state == CLOSED)
     180        return;
     181    if (m_state == CONNECTING) {
     182        m_state = CLOSING;
     183        m_channel->fail("WebSocket is closed before the connection is established.");
     184        return;
     185    }
     186    m_state = CLOSING;
    182187    m_bufferedAmountAfterClose = m_channel->bufferedAmount();
    183188    // didClose notification may be already queued, which we will inadvertently process while waiting for bufferedAmount() to return.
     
    201206    if (m_state == OPEN)
    202207        return m_channel->bufferedAmount();
     208    else if (m_state == CLOSING)
     209        return m_channel->bufferedAmount() + m_bufferedAmountAfterClose;
    203210    return m_bufferedAmountAfterClose;
    204211}
     
    250257    LOG(Network, "WebSocket %p didConnect", this);
    251258    if (m_state != CONNECTING) {
    252         didClose(0);
     259        didClose(0, ClosingHandshakeIncomplete);
    253260        return;
    254261    }
     
    261268{
    262269    LOG(Network, "WebSocket %p didReceiveMessage %s", this, msg.utf8().data());
    263     if (m_state != OPEN)
     270    if (m_state != OPEN && m_state != CLOSING)
    264271        return;
    265272    ASSERT(scriptExecutionContext());
     
    272279{
    273280    LOG(Network, "WebSocket %p didReceiveErrorMessage", this);
    274     if (m_state != OPEN)
     281    if (m_state != OPEN && m_state != CLOSING)
    275282        return;
    276283    ASSERT(scriptExecutionContext());
     
    278285}
    279286
    280 void WebSocket::didClose(unsigned long unhandledBufferedAmount)
     287void WebSocket::didStartClosingHandshake()
     288{
     289    LOG(Network, "WebSocket %p didStartClosingHandshake", this);
     290    m_state = CLOSING;
     291}
     292
     293void WebSocket::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion)
    281294{
    282295    LOG(Network, "WebSocket %p didClose", this);
    283296    if (!m_channel)
    284297        return;
     298    bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && closingHandshakeCompletion == ClosingHandshakeComplete;
    285299    m_state = CLOSED;
    286300    m_bufferedAmountAfterClose += unhandledBufferedAmount;
    287301    ASSERT(scriptExecutionContext());
    288302    RefPtr<CloseEvent> event = CloseEvent::create(false);
    289     event->initCloseEvent(eventNames().closeEvent, false, false, false);
     303    event->initCloseEvent(eventNames().closeEvent, false, false, wasClean);
    290304    dispatchEvent(event);
    291305    if (m_channel) {
  • trunk/Source/WebCore/websockets/WebSocket.h

    r86732 r87464  
    9696        virtual void didReceiveMessage(const String& message);
    9797        virtual void didReceiveMessageError();
    98         virtual void didClose(unsigned long unhandledBufferedAmount);
     98        virtual void didStartClosingHandshake();
     99        virtual void didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus);
    99100
    100101    private:
  • trunk/Source/WebCore/websockets/WebSocketChannel.cpp

    r87282 r87464  
    5757namespace WebCore {
    5858
     59const double TCPMaximumSegmentLifetime = 2 * 60.0;
     60
    5961WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol)
    6062    : m_context(context)
     
    6567    , m_resumeTimer(this, &WebSocketChannel::resumeTimerFired)
    6668    , m_suspended(false)
     69    , m_closing(false)
     70    , m_receivedClosingHandshake(false)
     71    , m_closingTimer(this, &WebSocketChannel::closingTimerFired)
    6772    , m_closed(false)
    6873    , m_shouldDiscardReceivedData(false)
     
    118123    LOG(Network, "WebSocketChannel %p close", this);
    119124    ASSERT(!m_suspended);
    120     if (m_handle)
    121         m_handle->close();  // will call didClose()
     125    if (!m_handle)
     126        return;
     127    startClosingHandshake();
     128    if (m_closing && !m_closingTimer.isActive())
     129        m_closingTimer.startOneShot(2 * TCPMaximumSegmentLifetime);
    122130}
    123131
     
    141149    m_context = 0;
    142150    if (m_handle)
    143         m_handle->close();
     151        m_handle->disconnect();
    144152}
    145153
     
    176184    ASSERT_UNUSED(handle, handle == m_handle || !m_handle);
    177185    m_closed = true;
     186    if (m_closingTimer.isActive())
     187        m_closingTimer.stop();
    178188    if (m_handle) {
    179189        m_unhandledBufferedAmount = m_handle->bufferedAmount();
     
    185195        m_handle = 0;
    186196        if (client)
    187             client->didClose(m_unhandledBufferedAmount);
     197            client->didClose(m_unhandledBufferedAmount, m_receivedClosingHandshake ? WebSocketChannelClient::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete);
    188198    }
    189199    deref();
     
    198208        return;
    199209    }
     210    if (len <= 0) {
     211        handle->disconnect();
     212        return;
     213    }
    200214    if (!m_client) {
    201215        m_shouldDiscardReceivedData = true;
    202         handle->close();
     216        handle->disconnect();
    203217        return;
    204218    }
     
    234248    }
    235249    m_shouldDiscardReceivedData = true;
    236     handle->close();
     250    handle->disconnect();
    237251}
    238252
     
    282296    ASSERT(m_client);
    283297    ASSERT(m_buffer);
     298    LOG(Network, "WebSocketChannel %p processBuffer %lu", this, static_cast<unsigned long>(m_bufferSize));
     299
    284300    if (m_shouldDiscardReceivedData)
    285301        return false;
     302
     303    if (m_receivedClosingHandshake) {
     304        skipBuffer(m_bufferSize);
     305        return false;
     306    }
     307
     308    RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
    286309
    287310    if (m_handshake.mode() == WebSocketHandshake::Incomplete) {
     
    312335        m_shouldDiscardReceivedData = true;
    313336        if (!m_closed)
    314             m_handle->close();
     337            m_handle->disconnect();
    315338        return false;
    316339    }
     
    363386        }
    364387        ASSERT(p + length >= p);
    365         if (p + length < end) {
     388        if (p + length <= end) {
    366389            p += length;
    367390            nextFrame = p;
    368391            ASSERT(nextFrame > m_buffer);
    369392            skipBuffer(nextFrame - m_buffer);
    370             m_client->didReceiveMessageError();
     393            if (frameByte == 0xff && !length) {
     394                m_receivedClosingHandshake = true;
     395                startClosingHandshake();
     396                if (m_closing)
     397                    m_handle->close(); // close after sending FF 00.
     398            } else
     399                m_client->didReceiveMessageError();
    371400            return m_buffer;
    372401        }
     
    406435}
    407436
     437void WebSocketChannel::startClosingHandshake()
     438{
     439    LOG(Network, "WebSocketChannel %p closing %d %d", this, m_closing, m_receivedClosingHandshake);
     440    if (m_closing)
     441        return;
     442    ASSERT(m_handle);
     443    Vector<char> buf;
     444    buf.append('\xff');
     445    buf.append('\0');
     446    if (!m_handle->send(buf.data(), buf.size())) {
     447        m_handle->disconnect();
     448        return;
     449    }
     450    m_closing = true;
     451    if (m_client)
     452        m_client->didStartClosingHandshake();
     453}
     454
     455void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer)
     456{
     457    LOG(Network, "WebSocketChannel %p closing timer", this);
     458    ASSERT_UNUSED(timer, &m_closingTimer == timer);
     459    if (m_handle)
     460        m_handle->disconnect();
     461}
     462
    408463}  // namespace WebCore
    409464
  • trunk/Source/WebCore/websockets/WebSocketChannel.h

    r87139 r87464  
    5858        virtual bool send(const String& message);
    5959        virtual unsigned long bufferedAmount() const;
    60         virtual void close();
     60        virtual void close(); // Start closing handshake.
    6161        virtual void fail(const String& reason);
    6262        virtual void disconnect();
     
    8686        bool processBuffer();
    8787        void resumeTimerFired(Timer<WebSocketChannel>* timer);
     88        void startClosingHandshake();
     89        void closingTimerFired(Timer<WebSocketChannel>*);
    8890
    8991        ScriptExecutionContext* m_context;
     
    9698        Timer<WebSocketChannel> m_resumeTimer;
    9799        bool m_suspended;
     100        bool m_closing;
     101        bool m_receivedClosingHandshake;
     102        Timer<WebSocketChannel> m_closingTimer;
    98103        bool m_closed;
    99104        bool m_shouldDiscardReceivedData;
  • trunk/Source/WebCore/websockets/WebSocketChannelClient.h

    r55573 r87464  
    4242        virtual void didReceiveMessage(const String&) { }
    4343        virtual void didReceiveMessageError() { }
    44         virtual void didClose(unsigned long /* unhandledBufferedAmount */) { }
     44        virtual void didStartClosingHandshake() { }
     45        enum ClosingHandshakeCompletionStatus {
     46            ClosingHandshakeIncomplete,
     47            ClosingHandshakeComplete
     48        };
     49        virtual void didClose(unsigned long /* unhandledBufferedAmount */, ClosingHandshakeCompletionStatus) { }
    4550
    4651    protected:
  • trunk/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp

    r87139 r87464  
    175175        return;
    176176    m_mainWebSocketChannel->close();
    177     m_mainWebSocketChannel = 0;
    178177}
    179178
     
    235234}
    236235
    237 static void workerContextDidClose(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long unhandledBufferedAmount)
    238 {
    239     ASSERT_UNUSED(context, context->isWorkerContext());
    240     workerClientWrapper->didClose(unhandledBufferedAmount);
    241 }
    242 
    243 void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned long unhandledBufferedAmount)
     236static void workerContextDidStartClosingHandshake(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
     237{
     238    ASSERT_UNUSED(context, context->isWorkerContext());
     239    workerClientWrapper->didStartClosingHandshake();
     240}
     241
     242void WorkerThreadableWebSocketChannel::Peer::didStartClosingHandshake()
     243{
     244    ASSERT(isMainThread());
     245    m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidStartClosingHandshake, m_workerClientWrapper), m_taskMode);
     246}
     247
     248static void workerContextDidClose(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long unhandledBufferedAmount, WebSocketChannelClient::ClosingHandshakeCompletionStatus closingHandshakeCompletion)
     249{
     250    ASSERT_UNUSED(context, context->isWorkerContext());
     251    workerClientWrapper->didClose(unhandledBufferedAmount, closingHandshakeCompletion);
     252}
     253
     254void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus closingHandshakeCompletion)
    244255{
    245256    ASSERT(isMainThread());
    246257    m_mainWebSocketChannel = 0;
    247     m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidClose, m_workerClientWrapper, unhandledBufferedAmount), m_taskMode);
     258    m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidClose, m_workerClientWrapper, unhandledBufferedAmount, closingHandshakeCompletion), m_taskMode);
    248259}
    249260
  • trunk/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h

    r87139 r87464  
    100100        virtual void didConnect();
    101101        virtual void didReceiveMessage(const String& message);
    102         virtual void didClose(unsigned long unhandledBufferedAmount);
     102        virtual void didStartClosingHandshake();
     103        virtual void didClose(unsigned long unhandledBufferedAmount, ClosingHandshakeCompletionStatus);
    103104
    104105    private:
  • trunk/Tools/ChangeLog

    r87452 r87464  
     12011-05-26  Yuta Kitamura  <yutak@chromium.org>
     2
     3        Reviewed by Kent Tamura.
     4
     5        WebSocket closing handshake
     6        https://bugs.webkit.org/show_bug.cgi?id=35721
     7
     8        * Scripts/webkitpy/thirdparty/__init__.py:
     9        Pull in pywebsocket 0.6b1. We need to update pywebsocket
     10        to get the right behavior of closing handshake.
     11
    1122011-05-26  Qi Zhang  <qi.2.zhang@nokia.com>
    213
  • trunk/Tools/Scripts/webkitpy/thirdparty/__init__.py

    r85080 r87464  
    124124        pywebsocket_dir = self._fs.join(_AUTOINSTALLED_DIR, "pywebsocket")
    125125        installer = AutoInstaller(target_dir=pywebsocket_dir)
    126         installer.install(url="http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.5.2.tar.gz",
    127                           url_subpath="pywebsocket-0.5.2/src/mod_pywebsocket")
     126        installer.install(url="http://pywebsocket.googlecode.com/files/mod_pywebsocket-0.6b1.tar.gz",
     127                          url_subpath="pywebsocket-0.6b1/src/mod_pywebsocket")
    128128
    129129    def _install(self, url, url_subpath):
Note: See TracChangeset for help on using the changeset viewer.