LayoutTests/ChangeLog

 12011-05-25 Yuta Kitamura <yutak@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 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-25 Adam Klein <adamk@chromium.org>
224
325 Unreviewed. Fix to previous update, mark drag-not-loaded-image crash as DEBUG-only.

LayoutTests/http/tests/websocket/tests/client-close-expected.txt

 1WebSocket: Test client-initiated close.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5Connected
 6Closed
 7PASS closeEvent.wasClean is true
 8PASS successfullyParsed is true
 9
 10TEST COMPLETE
 11

LayoutTests/http/tests/websocket/tests/client-close.html

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../../js-test-resources/js-test-style.css">
 5<script src="../../../js-test-resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<div id="description"></div>
 9<div id="console"></div>
 10<script type="text/javascript">
 11description("WebSocket: Test client-initiated close.");
 12
 13window.jsTestIsAsync = true;
 14
 15var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/client-close");
 16var closeEvent;
 17
 18ws.onopen = function()
 19{
 20 debug("Connected");
 21 ws.close();
 22};
 23
 24ws.onmessage = function(messageEvent)
 25{
 26 debug("Received: " + messageEvent.data);
 27};
 28
 29ws.onclose = function(event)
 30{
 31 debug("Closed");
 32 closeEvent = event;
 33 shouldBeTrue("closeEvent.wasClean");
 34 finishJSTest();
 35};
 36
 37var successfullyParsed = true;
 38</script>
 39<script src="../../../js-test-resources/js-test-post.js"></script>
 40</body>
 41</html>

LayoutTests/http/tests/websocket/tests/client-close_wsh.py

 1from mod_pywebsocket import msgutil
 2
 3
 4def web_socket_do_extra_handshake(request):
 5 pass
 6
 7
 8def web_socket_transfer_data(request):
 9 # Wait for a close frame sent from the client.
 10 close_frame = request.ws_stream.receive_bytes(2)
 11
 12 # If the following assertion fails, AssertionError will be raised,
 13 # which will prevent pywebsocket from sending a close frame.
 14 # In this case, the client will fail to finish closing handshake, thus
 15 # closeEvent.wasClean will become false.
 16 assert close_frame == '\xff\x00'
 17
 18 # Pretend we have received a close frame from the client.
 19 # After this function exits, pywebsocket will send a close frame automatically.
 20 request.client_terminated = True

LayoutTests/http/tests/websocket/tests/close-before-open-expected.txt

 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
34On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

LayoutTests/http/tests/websocket/tests/close-event-expected.txt

@@Received: 'Hello from Simple WSH.'
77Closed
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
1213PASS successfullyParsed is true

LayoutTests/http/tests/websocket/tests/close-event.html

@@ws.onclose = function(event)
3333 closeEventType = closeEvent.type;
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");
3839 finishJSTest();

LayoutTests/http/tests/websocket/tests/close-unref-websocket-expected.txt

 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
34On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

LayoutTests/http/tests/websocket/tests/frame-length-longer-than-buffer_wsh.py

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.

LayoutTests/http/tests/websocket/tests/server-close-expected.txt

 1WebSocket: Test server-initiated close.
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5Connected
 6Closed
 7PASS closeEvent.wasClean is true
 8PASS successfullyParsed is true
 9
 10TEST COMPLETE
 11

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

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../../js-test-resources/js-test-style.css">
 5<script src="../../../js-test-resources/js-test-pre.js"></script>
 6</head>
 7<body>
 8<div id="description"></div>
 9<div id="console"></div>
 10<script type="text/javascript">
 11description("WebSocket: Test server-initiated close.");
 12
 13window.jsTestIsAsync = true;
 14
 15var ws = new WebSocket("ws://127.0.0.1:8880/websocket/tests/server-close");
 16var closeEvent;
 17
 18ws.onopen = function()
 19{
 20 debug("Connected");
 21};
 22
 23ws.onmessage = function(messageEvent)
 24{
 25 debug("Received: " + messageEvent.data);
 26};
 27
 28ws.onclose = function(event)
 29{
 30 debug("Closed");
 31 closeEvent = event;
 32 shouldBeTrue("closeEvent.wasClean");
 33 finishJSTest();
 34};
 35
 36var successfullyParsed = true;
 37</script>
 38<script src="../../../js-test-resources/js-test-post.js"></script>
 39</body>
 40</html>

LayoutTests/http/tests/websocket/tests/server-close_wsh.py

 1def web_socket_do_extra_handshake(request):
 2 pass
 3
 4
 5def web_socket_transfer_data(request):
 6 # After this handler exits, pywebsocket initiates the closing handshake.
 7 pass

LayoutTests/http/tests/websocket/tests/websocket-event-target-expected.txt

 1CONSOLE MESSAGE: line 0: WebSocket is closed before the connection is established.
12Make sure WebSocket object acts as EventTarget.
23
34On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

Source/WebCore/ChangeLog

 12011-05-25 Yuta Kitamura <yutak@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 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 (receiveClosingHandshake)
 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 receivedClosingHandshake parameter.
 68 * websockets/WorkerThreadableWebSocketChannel.cpp:
 69 Add receivedClosingHandshake parameter to close(), 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-25 Martin Robinson <mrobinson@igalia.com>
278
379 Reviewed by Dirk Schulze.

Source/WebCore/platform/network/SocketStreamHandleBase.cpp

@@SocketStreamHandleBase::SocketStreamState SocketStreamHandleBase::state() const
5252
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()) {
5858 if (m_buffer.size() + length > bufferSize) {

@@bool SocketStreamHandleBase::send(const char* data, int length)
7878
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
8393 platformClose();

@@void SocketStreamHandleBase::setClient(SocketStreamHandleClient* client)
92102
93103bool SocketStreamHandleBase::sendPendingData()
94104{
95  if (m_state != Open)
96  return false;
97  if (m_buffer.isEmpty())
 105 if (m_state != Open && m_state != Closing)
98106 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)
101117 return false;

Source/WebCore/platform/network/SocketStreamHandleBase.h

@@namespace WebCore {
4848 SocketStreamState state() const;
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
5455 SocketStreamHandleClient* client() const { return m_client; }

Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.cpp

@@void ThreadableWebSocketChannelClientWrapper::didReceiveMessage(const String& me
110110 processPendingTasks();
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, bool receiveClosingHandshake)
 121{
 122 m_pendingTasks.append(createCallbackTask(&ThreadableWebSocketChannelClientWrapper::didCloseCallback, AllowCrossThreadAccess(this), unhandledBufferedAmount, receiveClosingHandshake));
116123 if (!m_suspended)
117124 processPendingTasks();
118125}

@@void ThreadableWebSocketChannelClientWrapper::didReceiveMessageCallback(ScriptEx
151158 wrapper->m_client->didReceiveMessage(message);
152159}
153160
154 void ThreadableWebSocketChannelClientWrapper::didCloseCallback(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long unhandledBufferedAmount)
 161void ThreadableWebSocketChannelClientWrapper::didStartClosingHandshakeCallback(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> wrapper)
 162{
 163 ASSERT_UNUSED(context, !context);
 164 if (wrapper->m_client)
 165 wrapper->m_client->didStartClosingHandshake();
 166}
 167
 168void ThreadableWebSocketChannelClientWrapper::didCloseCallback(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> wrapper, unsigned long unhandledBufferedAmount, bool receiveClosingHandshake)
155169{
156170 ASSERT_UNUSED(context, !context);
157171 if (wrapper->m_client)
158  wrapper->m_client->didClose(unhandledBufferedAmount);
 172 wrapper->m_client->didClose(unhandledBufferedAmount, receiveClosingHandshake);
159173}
160174
161175} // namespace WebCore

Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h

@@public:
6262
6363 void didConnect();
6464 void didReceiveMessage(const String& message);
65  void didClose(unsigned long unhandledBufferedAmount);
 65 void didStartClosingHandshake();
 66 void didClose(unsigned long unhandledBufferedAmount, bool receiveClosingHandshake);
6667
6768 void suspend();
6869 void resume();

@@protected:
7374 void processPendingTasks();
7475 static void didConnectCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>);
7576 static void didReceiveMessageCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>, String message);
76  static void didCloseCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>, unsigned long unhandledBufferedAmount);
 77 static void didStartClosingHandshakeCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>);
 78 static void didCloseCallback(ScriptExecutionContext*, RefPtr<ThreadableWebSocketChannelClientWrapper>, unsigned long unhandledBufferedAmount, bool receiveClosingHandshake);
7779
7880 WebSocketChannelClient* m_client;
7981 bool m_syncMethodDone;

Source/WebCore/websockets/WebSocket.cpp

@@bool WebSocket::send(const String& message, ExceptionCode& ec)
164164 return false;
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;
170170 }

@@bool WebSocket::send(const String& message, ExceptionCode& ec)
176176void WebSocket::close()
177177{
178178 LOG(Network, "WebSocket %p close", this);
179  if (m_state == CLOSED)
 179 if (m_state == CLOSING || m_state == CLOSED)
180180 return;
181  m_state = CLOSED;
 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.
184189 // In this case m_channel will be set to null during didClose() call, thus we need to test validness of m_channel here.

@@unsigned long WebSocket::bufferedAmount() const
200205{
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}
205212

@@void WebSocket::didConnect()
249256{
250257 LOG(Network, "WebSocket %p didConnect", this);
251258 if (m_state != CONNECTING) {
252  didClose(0);
 259 didClose(0, false);
253260 return;
254261 }
255262 ASSERT(scriptExecutionContext());

@@void WebSocket::didConnect()
260267void WebSocket::didReceiveMessage(const String& msg)
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());
266273 RefPtr<MessageEvent> evt = MessageEvent::create();

@@void WebSocket::didReceiveMessage(const String& msg)
271278void WebSocket::didReceiveMessageError()
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());
277284 dispatchEvent(Event::create(eventNames().errorEvent, false, false));
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, bool receivedClosingHandshake)
281294{
282295 LOG(Network, "WebSocket %p didClose", this);
283296 if (!m_channel)
284297 return;
 298 bool wasClean = m_state == CLOSING && !unhandledBufferedAmount && receivedClosingHandshake;
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) {
292306 m_channel->disconnect();

Source/WebCore/websockets/WebSocket.h

@@namespace WebCore {
9595 virtual void didConnect();
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, bool receivedClosingHandshake);
99100
100101 private:
101102 WebSocket(ScriptExecutionContext*);

Source/WebCore/websockets/WebSocketChannel.cpp

@@WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketCha
6464 , m_bufferSize(0)
6565 , m_resumeTimer(this, &WebSocketChannel::resumeTimerFired)
6666 , m_suspended(false)
 67 , m_closing(false)
 68 , m_receivedClosingHandshake(false)
 69 , m_closingTimer(this, &WebSocketChannel::closingTimerFired)
6770 , m_closed(false)
6871 , m_shouldDiscardReceivedData(false)
6972 , m_unhandledBufferedAmount(0)

@@void WebSocketChannel::close()
117120{
118121 LOG(Network, "WebSocketChannel %p close", this);
119122 ASSERT(!m_suspended);
120  if (m_handle)
121  m_handle->close(); // will call didClose()
 123 if (!m_handle)
 124 return;
 125 startClosingHandshake();
 126 if (m_closing && !m_closingTimer.isActive())
 127 m_closingTimer.startOneShot(2 * 2 * 60.0); // 2 * TCP Maximum Segment Lifetime.
122128}
123129
124130void WebSocketChannel::fail(const String& reason)

@@void WebSocketChannel::disconnect()
140146 m_client = 0;
141147 m_context = 0;
142148 if (m_handle)
143  m_handle->close();
 149 m_handle->disconnect();
144150}
145151
146152void WebSocketChannel::suspend()

@@void WebSocketChannel::didClose(SocketStreamHandle* handle)
175181 InspectorInstrumentation::didCloseWebSocket(m_context, m_identifier);
176182 ASSERT_UNUSED(handle, handle == m_handle || !m_handle);
177183 m_closed = true;
 184 if (m_closingTimer.isActive())
 185 m_closingTimer.stop();
178186 if (m_handle) {
179187 m_unhandledBufferedAmount = m_handle->bufferedAmount();
180188 if (m_suspended)

@@void WebSocketChannel::didClose(SocketStreamHandle* handle)
184192 m_context = 0;
185193 m_handle = 0;
186194 if (client)
187  client->didClose(m_unhandledBufferedAmount);
 195 client->didClose(m_unhandledBufferedAmount, m_receivedClosingHandshake);
188196 }
189197 deref();
190198}

@@void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* da
197205 if (!m_context) {
198206 return;
199207 }
 208 if (len <= 0) {
 209 handle->disconnect();
 210 return;
 211 }
200212 if (!m_client) {
201213 m_shouldDiscardReceivedData = true;
202  handle->close();
 214 handle->disconnect();
203215 return;
204216 }
205217 if (m_shouldDiscardReceivedData)

@@void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamErr
233245 m_context->addMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, 0, failingURL, 0);
234246 }
235247 m_shouldDiscardReceivedData = true;
236  handle->close();
 248 handle->disconnect();
237249}
238250
239251void WebSocketChannel::didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)

@@bool WebSocketChannel::processBuffer()
281293 ASSERT(!m_suspended);
282294 ASSERT(m_client);
283295 ASSERT(m_buffer);
 296 LOG(Network, "WebSocketChannel %p processBuffer %lu", this, static_cast<unsigned long>(m_bufferSize));
 297
284298 if (m_shouldDiscardReceivedData)
285299 return false;
286300
 301 if (m_receivedClosingHandshake) {
 302 skipBuffer(m_bufferSize);
 303 return false;
 304 }
 305
 306 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
 307
287308 if (m_handshake.mode() == WebSocketHandshake::Incomplete) {
288309 int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize);
289310 if (headerLength <= 0)

@@bool WebSocketChannel::processBuffer()
311332 skipBuffer(headerLength);
312333 m_shouldDiscardReceivedData = true;
313334 if (!m_closed)
314  m_handle->close();
 335 m_handle->disconnect();
315336 return false;
316337 }
317338 if (m_handshake.mode() != WebSocketHandshake::Connected)

@@bool WebSocketChannel::processBuffer()
362383 return false;
363384 }
364385 ASSERT(p + length >= p);
365  if (p + length < end) {
 386 if (p + length <= end) {
366387 p += length;
367388 nextFrame = p;
368389 ASSERT(nextFrame > m_buffer);
369390 skipBuffer(nextFrame - m_buffer);
370  m_client->didReceiveMessageError();
 391 if (frameByte == 0xff && !length) {
 392 m_receivedClosingHandshake = true;
 393 startClosingHandshake();
 394 if (m_closing)
 395 m_handle->close(); // close after sending FF 00.
 396 } else
 397 m_client->didReceiveMessageError();
371398 return m_buffer;
372399 }
373400 return false;

@@void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer)
405432 didClose(m_handle.get());
406433}
407434
 435void WebSocketChannel::startClosingHandshake()
 436{
 437 LOG(Network, "WebSocketChannel %p closing %d %d", this, m_closing, m_receivedClosingHandshake);
 438 if (m_closing)
 439 return;
 440 ASSERT(m_handle);
 441 Vector<char> buf;
 442 buf.append('\xff');
 443 buf.append('\0');
 444 if (!m_handle->send(buf.data(), buf.size())) {
 445 m_handle->disconnect();
 446 return;
 447 }
 448 m_closing = true;
 449 if (m_client)
 450 m_client->didStartClosingHandshake();
 451}
 452
 453void WebSocketChannel::closingTimerFired(Timer<WebSocketChannel>* timer)
 454{
 455 LOG(Network, "WebSocketChannel %p closing timer", this);
 456 ASSERT_UNUSED(timer, &m_closingTimer == timer);
 457 if (m_handle)
 458 m_handle->disconnect();
 459}
 460
408461} // namespace WebCore
409462
410463#endif // ENABLE(WEB_SOCKETS)

Source/WebCore/websockets/WebSocketChannel.h

@@namespace WebCore {
5757 virtual void connect();
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();
6363

@@namespace WebCore {
8585 void skipBuffer(size_t len);
8686 bool processBuffer();
8787 void resumeTimerFired(Timer<WebSocketChannel>* timer);
 88 void startClosingHandshake();
 89 void closingTimerFired(Timer<WebSocketChannel>*);
8890
8991 ScriptExecutionContext* m_context;
9092 WebSocketChannelClient* m_client;

@@namespace WebCore {
9597
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;
100105 unsigned long m_unhandledBufferedAmount;

Source/WebCore/websockets/WebSocketChannelClient.h

@@namespace WebCore {
4141 virtual void didConnect() { }
4242 virtual void didReceiveMessage(const String&) { }
4343 virtual void didReceiveMessageError() { }
44  virtual void didClose(unsigned long /* unhandledBufferedAmount */) { }
 44 virtual void didStartClosingHandshake() { }
 45 virtual void didClose(unsigned long /* unhandledBufferedAmount */, bool /* didReceiveClosingHandshake */) { }
4546
4647 protected:
4748 WebSocketChannelClient() { }

Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp

@@void WorkerThreadableWebSocketChannel::Peer::close()
174174 if (!m_mainWebSocketChannel)
175175 return;
176176 m_mainWebSocketChannel->close();
177  m_mainWebSocketChannel = 0;
178177}
179178
180179void WorkerThreadableWebSocketChannel::Peer::fail(const String& reason)

@@void WorkerThreadableWebSocketChannel::Peer::didReceiveMessage(const String& mes
234233 m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveMessage, m_workerClientWrapper, message), m_taskMode);
235234}
236235
237 static void workerContextDidClose(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, 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, bool receiveClosingHandshake)
238249{
239250 ASSERT_UNUSED(context, context->isWorkerContext());
240  workerClientWrapper->didClose(unhandledBufferedAmount);
 251 workerClientWrapper->didClose(unhandledBufferedAmount, receiveClosingHandshake);
241252}
242253
243 void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned long unhandledBufferedAmount)
 254void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned long unhandledBufferedAmount, bool receiveClosingHandshake)
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, receiveClosingHandshake), m_taskMode);
248259}
249260
250261void WorkerThreadableWebSocketChannel::Bridge::setWebSocketChannel(ScriptExecutionContext* context, Bridge* thisPtr, Peer* peer, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)

Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h

@@private:
9999
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, bool recieveClosingHandshake);
103104
104105 private:
105106 Peer(RefPtr<ThreadableWebSocketChannelClientWrapper>, WorkerLoaderProxy&, ScriptExecutionContext*, const String& taskMode, const KURL&, const String& protocol);

Tools/ChangeLog

 12011-05-25 Yuta Kitamura <yutak@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 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-25 Jon Honeycutt <jhoneycutt@apple.com>
213
314 REGRESSION (WebKit2): Crash in Flash on USA Today photo gallery

Tools/Scripts/webkitpy/thirdparty/__init__.py

@@class AutoinstallImportHook(object):
123123 def _install_pywebsocket(self):
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):
130130 installer = AutoInstaller(target_dir=_AUTOINSTALLED_DIR)