summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/websockets
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-06 11:45:16 +0100
committerSteve Block <steveblock@google.com>2011-05-12 13:44:10 +0100
commitcad810f21b803229eb11403f9209855525a25d57 (patch)
tree29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/websockets
parent121b0cf4517156d0ac5111caf9830c51b69bae8f (diff)
downloadexternal_webkit-cad810f21b803229eb11403f9209855525a25d57.zip
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz
external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/websockets')
-rw-r--r--Source/WebCore/websockets/ThreadableWebSocketChannel.cpp74
-rw-r--r--Source/WebCore/websockets/ThreadableWebSocketChannel.h72
-rw-r--r--Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h173
-rw-r--r--Source/WebCore/websockets/WebSocket.cpp302
-rw-r--r--Source/WebCore/websockets/WebSocket.h120
-rw-r--r--Source/WebCore/websockets/WebSocket.idl72
-rw-r--r--Source/WebCore/websockets/WebSocketChannel.cpp402
-rw-r--r--Source/WebCore/websockets/WebSocketChannel.h107
-rw-r--r--Source/WebCore/websockets/WebSocketChannelClient.h54
-rw-r--r--Source/WebCore/websockets/WebSocketHandshake.cpp598
-rw-r--r--Source/WebCore/websockets/WebSocketHandshake.h126
-rw-r--r--Source/WebCore/websockets/WebSocketHandshakeRequest.cpp95
-rw-r--r--Source/WebCore/websockets/WebSocketHandshakeRequest.h73
-rw-r--r--Source/WebCore/websockets/WebSocketHandshakeResponse.cpp110
-rw-r--r--Source/WebCore/websockets/WebSocketHandshakeResponse.h75
-rw-r--r--Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp426
-rw-r--r--Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h168
17 files changed, 3047 insertions, 0 deletions
diff --git a/Source/WebCore/websockets/ThreadableWebSocketChannel.cpp b/Source/WebCore/websockets/ThreadableWebSocketChannel.cpp
new file mode 100644
index 0000000..28b9263
--- /dev/null
+++ b/Source/WebCore/websockets/ThreadableWebSocketChannel.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "ThreadableWebSocketChannel.h"
+
+#include "PlatformString.h"
+#include "ScriptExecutionContext.h"
+#include "ThreadableWebSocketChannelClientWrapper.h"
+#include "WebSocketChannel.h"
+#include "WebSocketChannelClient.h"
+#include "WorkerContext.h"
+#include "WorkerRunLoop.h"
+#include "WorkerThread.h"
+#include "WorkerThreadableWebSocketChannel.h"
+
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+static const char webSocketChannelMode[] = "webSocketChannelMode";
+
+PassRefPtr<ThreadableWebSocketChannel> ThreadableWebSocketChannel::create(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol)
+{
+ ASSERT(context);
+ ASSERT(client);
+
+#if ENABLE(WORKERS)
+ if (context->isWorkerContext()) {
+ WorkerContext* workerContext = static_cast<WorkerContext*>(context);
+ WorkerRunLoop& runLoop = workerContext->thread()->runLoop();
+ String mode = webSocketChannelMode;
+ mode.append(String::number(runLoop.createUniqueId()));
+ return WorkerThreadableWebSocketChannel::create(workerContext, client, mode, url, protocol);
+ }
+#endif // ENABLE(WORKERS)
+
+ ASSERT(context->isDocument());
+ return WebSocketChannel::create(context, client, url, protocol);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/ThreadableWebSocketChannel.h b/Source/WebCore/websockets/ThreadableWebSocketChannel.h
new file mode 100644
index 0000000..05b3767
--- /dev/null
+++ b/Source/WebCore/websockets/ThreadableWebSocketChannel.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ThreadableWebSocketChannel_h
+#define ThreadableWebSocketChannel_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+class KURL;
+class ScriptExecutionContext;
+class WebSocketChannelClient;
+
+class ThreadableWebSocketChannel : public Noncopyable {
+public:
+ static PassRefPtr<ThreadableWebSocketChannel> create(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String& protocol);
+
+ virtual void connect() = 0;
+ virtual bool send(const String& message) = 0;
+ virtual unsigned long bufferedAmount() const = 0;
+ virtual void close() = 0;
+ virtual void disconnect() = 0; // Will suppress didClose().
+
+ virtual void suspend() = 0;
+ virtual void resume() = 0;
+
+ void ref() { refThreadableWebSocketChannel(); }
+ void deref() { derefThreadableWebSocketChannel(); }
+
+protected:
+ virtual ~ThreadableWebSocketChannel() { }
+ virtual void refThreadableWebSocketChannel() = 0;
+ virtual void derefThreadableWebSocketChannel() = 0;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // ThreadableWebSocketChannel_h
diff --git a/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h b/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h
new file mode 100644
index 0000000..4099615
--- /dev/null
+++ b/Source/WebCore/websockets/ThreadableWebSocketChannelClientWrapper.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ThreadableWebSocketChannelClientWrapper_h
+#define ThreadableWebSocketChannelClientWrapper_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "PlatformString.h"
+#include "WebSocketChannelClient.h"
+#include <wtf/Forward.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class ThreadableWebSocketChannelClientWrapper : public ThreadSafeShared<ThreadableWebSocketChannelClientWrapper> {
+public:
+ static PassRefPtr<ThreadableWebSocketChannelClientWrapper> create(WebSocketChannelClient* client)
+ {
+ return adoptRef(new ThreadableWebSocketChannelClientWrapper(client));
+ }
+
+ void clearSyncMethodDone()
+ {
+ m_syncMethodDone = false;
+ }
+ void setSyncMethodDone()
+ {
+ m_syncMethodDone = true;
+ }
+
+ bool syncMethodDone() const
+ {
+ return m_syncMethodDone;
+ }
+
+ bool sent() const
+ {
+ return m_sent;
+ }
+ void setSent(bool sent)
+ {
+ m_sent = sent;
+ m_syncMethodDone = true;
+ }
+
+ unsigned long bufferedAmount() const
+ {
+ return m_bufferedAmount;
+ }
+ void setBufferedAmount(unsigned long bufferedAmount)
+ {
+ m_bufferedAmount = bufferedAmount;
+ m_syncMethodDone = true;
+ }
+
+ void clearClient()
+ {
+ m_client = 0;
+ }
+
+ void didConnect()
+ {
+ m_pendingConnected = true;
+ if (!m_suspended)
+ processPendingEvents();
+ }
+
+ void didReceiveMessage(const String& msg)
+ {
+ m_pendingMessages.append(msg);
+ if (!m_suspended)
+ processPendingEvents();
+ }
+
+ void didClose(unsigned long unhandledBufferedAmount)
+ {
+ m_pendingClosed = true;
+ m_bufferedAmount = unhandledBufferedAmount;
+ if (!m_suspended)
+ processPendingEvents();
+ }
+
+ void suspend()
+ {
+ m_suspended = true;
+ }
+
+ void resume()
+ {
+ m_suspended = false;
+ processPendingEvents();
+ }
+
+protected:
+ ThreadableWebSocketChannelClientWrapper(WebSocketChannelClient* client)
+ : m_client(client)
+ , m_syncMethodDone(false)
+ , m_sent(false)
+ , m_bufferedAmount(0)
+ , m_suspended(false)
+ , m_pendingConnected(false)
+ , m_pendingClosed(false)
+ {
+ }
+
+ void processPendingEvents()
+ {
+ ASSERT(!m_suspended);
+ if (m_pendingConnected) {
+ m_pendingConnected = false;
+ if (m_client)
+ m_client->didConnect();
+ }
+
+ Vector<String> messages;
+ messages.swap(m_pendingMessages);
+ for (Vector<String>::const_iterator iter = messages.begin(); iter != messages.end(); ++iter) {
+ if (m_client)
+ m_client->didReceiveMessage(*iter);
+ }
+
+ if (m_pendingClosed) {
+ m_pendingClosed = false;
+ if (m_client)
+ m_client->didClose(m_bufferedAmount);
+ }
+ }
+
+ WebSocketChannelClient* m_client;
+ bool m_syncMethodDone;
+ bool m_sent;
+ unsigned long m_bufferedAmount;
+ bool m_suspended;
+ bool m_pendingConnected;
+ Vector<String> m_pendingMessages;
+ bool m_pendingClosed;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // ThreadableWebSocketChannelClientWrapper_h
diff --git a/Source/WebCore/websockets/WebSocket.cpp b/Source/WebCore/websockets/WebSocket.cpp
new file mode 100644
index 0000000..358a742
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocket.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocket.h"
+
+#include "DOMWindow.h"
+#include "Event.h"
+#include "EventException.h"
+#include "EventListener.h"
+#include "EventNames.h"
+#include "Logging.h"
+#include "MessageEvent.h"
+#include "ScriptExecutionContext.h"
+#include "ThreadableWebSocketChannel.h"
+#include "WebSocketChannel.h"
+#include <wtf/StdLibExtras.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringConcatenate.h>
+
+namespace WebCore {
+
+static bool isValidProtocolString(const String& protocol)
+{
+ if (protocol.isNull())
+ return true;
+ if (protocol.isEmpty())
+ return false;
+ const UChar* characters = protocol.characters();
+ for (size_t i = 0; i < protocol.length(); i++) {
+ if (characters[i] < 0x20 || characters[i] > 0x7E)
+ return false;
+ }
+ return true;
+}
+
+static String encodeProtocolString(const String& protocol)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < protocol.length(); i++) {
+ if (protocol[i] < 0x20 || protocol[i] > 0x7E)
+ builder.append(String::format("\\u%04X", protocol[i]));
+ else if (protocol[i] == 0x5c)
+ builder.append("\\\\");
+ else
+ builder.append(protocol[i]);
+ }
+ return builder.toString();
+}
+
+static bool webSocketsAvailable = false;
+
+void WebSocket::setIsAvailable(bool available)
+{
+ webSocketsAvailable = available;
+}
+
+bool WebSocket::isAvailable()
+{
+ return webSocketsAvailable;
+}
+
+WebSocket::WebSocket(ScriptExecutionContext* context)
+ : ActiveDOMObject(context, this)
+ , m_state(CONNECTING)
+ , m_bufferedAmountAfterClose(0)
+{
+}
+
+WebSocket::~WebSocket()
+{
+ if (m_channel)
+ m_channel->disconnect();
+}
+
+void WebSocket::connect(const KURL& url, ExceptionCode& ec)
+{
+ connect(url, String(), ec);
+}
+
+void WebSocket::connect(const KURL& url, const String& protocol, ExceptionCode& ec)
+{
+ LOG(Network, "WebSocket %p connect to %s protocol=%s", this, url.string().utf8().data(), protocol.utf8().data());
+ m_url = url;
+ m_protocol = protocol;
+
+ if (!m_url.isValid()) {
+ scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid url for WebSocket " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString());
+ m_state = CLOSED;
+ ec = SYNTAX_ERR;
+ return;
+ }
+
+ if (!m_url.protocolIs("ws") && !m_url.protocolIs("wss")) {
+ scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong url scheme for WebSocket " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString());
+ m_state = CLOSED;
+ ec = SYNTAX_ERR;
+ return;
+ }
+ if (m_url.hasFragmentIdentifier()) {
+ scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "URL has fragment component " + url.string(), 0, scriptExecutionContext()->securityOrigin()->toString());
+ m_state = CLOSED;
+ ec = SYNTAX_ERR;
+ return;
+ }
+ if (!isValidProtocolString(m_protocol)) {
+ scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Wrong protocol for WebSocket '" + encodeProtocolString(m_protocol) + "'", 0, scriptExecutionContext()->securityOrigin()->toString());
+ m_state = CLOSED;
+ ec = SYNTAX_ERR;
+ return;
+ }
+ if (!portAllowed(url)) {
+ scriptExecutionContext()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, makeString("WebSocket port ", String::number(url.port()), " blocked"), 0, scriptExecutionContext()->securityOrigin()->toString());
+ m_state = CLOSED;
+ ec = SECURITY_ERR;
+ return;
+ }
+
+ m_channel = ThreadableWebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol);
+ m_channel->connect();
+ ActiveDOMObject::setPendingActivity(this);
+}
+
+bool WebSocket::send(const String& message, ExceptionCode& ec)
+{
+ LOG(Network, "WebSocket %p send %s", this, message.utf8().data());
+ if (m_state == CONNECTING) {
+ ec = INVALID_STATE_ERR;
+ return false;
+ }
+ // No exception is raised if the connection was once established but has subsequently been closed.
+ if (m_state == CLOSED) {
+ m_bufferedAmountAfterClose += message.utf8().length() + 2; // 2 for frameing
+ return false;
+ }
+ // FIXME: check message is valid utf8.
+ ASSERT(m_channel);
+ return m_channel->send(message);
+}
+
+void WebSocket::close()
+{
+ LOG(Network, "WebSocket %p close", this);
+ if (m_state == CLOSED)
+ return;
+ m_state = CLOSED;
+ m_bufferedAmountAfterClose = m_channel->bufferedAmount();
+ // didClose notification may be already queued, which we will inadvertently process while waiting for bufferedAmount() to return.
+ // In this case m_channel will be set to null during didClose() call, thus we need to test validness of m_channel here.
+ if (m_channel)
+ m_channel->close();
+}
+
+const KURL& WebSocket::url() const
+{
+ return m_url;
+}
+
+WebSocket::State WebSocket::readyState() const
+{
+ return m_state;
+}
+
+unsigned long WebSocket::bufferedAmount() const
+{
+ if (m_state == OPEN)
+ return m_channel->bufferedAmount();
+ return m_bufferedAmountAfterClose;
+}
+
+ScriptExecutionContext* WebSocket::scriptExecutionContext() const
+{
+ return ActiveDOMObject::scriptExecutionContext();
+}
+
+void WebSocket::contextDestroyed()
+{
+ LOG(Network, "WebSocket %p scriptExecutionContext destroyed", this);
+ ASSERT(!m_channel);
+ ASSERT(m_state == CLOSED);
+ ActiveDOMObject::contextDestroyed();
+}
+
+bool WebSocket::canSuspend() const
+{
+ return !m_channel;
+}
+
+void WebSocket::suspend(ReasonForSuspension)
+{
+ if (m_channel)
+ m_channel->suspend();
+}
+
+void WebSocket::resume()
+{
+ if (m_channel)
+ m_channel->resume();
+}
+
+void WebSocket::stop()
+{
+ bool pending = hasPendingActivity();
+ if (m_channel)
+ m_channel->disconnect();
+ m_channel = 0;
+ m_state = CLOSED;
+ ActiveDOMObject::stop();
+ if (pending)
+ ActiveDOMObject::unsetPendingActivity(this);
+}
+
+void WebSocket::didConnect()
+{
+ LOG(Network, "WebSocket %p didConnect", this);
+ if (m_state != CONNECTING) {
+ didClose(0);
+ return;
+ }
+ ASSERT(scriptExecutionContext());
+ m_state = OPEN;
+ dispatchEvent(Event::create(eventNames().openEvent, false, false));
+}
+
+void WebSocket::didReceiveMessage(const String& msg)
+{
+ LOG(Network, "WebSocket %p didReceiveMessage %s", this, msg.utf8().data());
+ if (m_state != OPEN)
+ return;
+ ASSERT(scriptExecutionContext());
+ RefPtr<MessageEvent> evt = MessageEvent::create();
+ evt->initMessageEvent(eventNames().messageEvent, false, false, SerializedScriptValue::create(msg), "", "", 0, 0);
+ dispatchEvent(evt);
+}
+
+void WebSocket::didReceiveMessageError()
+{
+ LOG(Network, "WebSocket %p didReceiveErrorMessage", this);
+ if (m_state != OPEN)
+ return;
+ ASSERT(scriptExecutionContext());
+ dispatchEvent(Event::create(eventNames().errorEvent, false, false));
+}
+
+void WebSocket::didClose(unsigned long unhandledBufferedAmount)
+{
+ LOG(Network, "WebSocket %p didClose", this);
+ if (!m_channel)
+ return;
+ m_state = CLOSED;
+ m_bufferedAmountAfterClose += unhandledBufferedAmount;
+ ASSERT(scriptExecutionContext());
+ dispatchEvent(Event::create(eventNames().closeEvent, false, false));
+ m_channel = 0;
+ if (hasPendingActivity())
+ ActiveDOMObject::unsetPendingActivity(this);
+}
+
+EventTargetData* WebSocket::eventTargetData()
+{
+ return &m_eventTargetData;
+}
+
+EventTargetData* WebSocket::ensureEventTargetData()
+{
+ return &m_eventTargetData;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/WebCore/websockets/WebSocket.h b/Source/WebCore/websockets/WebSocket.h
new file mode 100644
index 0000000..7f63cbf
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocket.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocket_h
+#define WebSocket_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "ActiveDOMObject.h"
+#include "EventListener.h"
+#include "EventNames.h"
+#include "EventTarget.h"
+#include "KURL.h"
+#include "WebSocketChannelClient.h"
+#include <wtf/Forward.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+ class ThreadableWebSocketChannel;
+
+ class WebSocket : public RefCounted<WebSocket>, public EventTarget, public ActiveDOMObject, public WebSocketChannelClient {
+ public:
+ static void setIsAvailable(bool);
+ static bool isAvailable();
+ static PassRefPtr<WebSocket> create(ScriptExecutionContext* context) { return adoptRef(new WebSocket(context)); }
+ virtual ~WebSocket();
+
+ enum State {
+ CONNECTING = 0,
+ OPEN = 1,
+ CLOSED = 2
+ };
+
+ void connect(const KURL&, ExceptionCode&);
+ void connect(const KURL&, const String& protocol, ExceptionCode&);
+
+ bool send(const String& message, ExceptionCode&);
+
+ void close();
+
+ const KURL& url() const;
+ State readyState() const;
+ unsigned long bufferedAmount() const;
+
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(open);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(error);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(close);
+
+ // EventTarget
+ virtual WebSocket* toWebSocket() { return this; }
+
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
+ virtual void contextDestroyed();
+ virtual bool canSuspend() const;
+ virtual void suspend(ReasonForSuspension);
+ virtual void resume();
+ virtual void stop();
+
+ using RefCounted<WebSocket>::ref;
+ using RefCounted<WebSocket>::deref;
+
+ // WebSocketChannelClient
+ virtual void didConnect();
+ virtual void didReceiveMessage(const String& message);
+ virtual void didReceiveMessageError();
+ virtual void didClose(unsigned long unhandledBufferedAmount);
+
+ private:
+ WebSocket(ScriptExecutionContext*);
+
+ virtual void refEventTarget() { ref(); }
+ virtual void derefEventTarget() { deref(); }
+ virtual EventTargetData* eventTargetData();
+ virtual EventTargetData* ensureEventTargetData();
+
+ RefPtr<ThreadableWebSocketChannel> m_channel;
+
+ State m_state;
+ KURL m_url;
+ String m_protocol;
+ EventTargetData m_eventTargetData;
+ unsigned long m_bufferedAmountAfterClose;
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocket_h
diff --git a/Source/WebCore/websockets/WebSocket.idl b/Source/WebCore/websockets/WebSocket.idl
new file mode 100644
index 0000000..6850d36
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocket.idl
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ * Copyright (C) 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+module websockets {
+
+ interface [
+ Conditional=WEB_SOCKETS,
+ CanBeConstructed,
+ CustomConstructFunction,
+ ConstructorParameters=1,
+ V8CustomConstructor,
+ EventTarget,
+ NoStaticTables
+ ] WebSocket {
+ readonly attribute DOMString URL;
+
+ // ready state
+ const unsigned short CONNECTING = 0;
+ const unsigned short OPEN = 1;
+ const unsigned short CLOSED = 2;
+ readonly attribute unsigned short readyState;
+
+ readonly attribute unsigned long bufferedAmount;
+
+ // networking
+ attribute EventListener onopen;
+ attribute EventListener onmessage;
+ attribute EventListener onerror;
+ attribute EventListener onclose;
+
+ [RequiresAllArguments] boolean send(in DOMString data) raises(DOMException);
+ void close();
+
+ // EventTarget interface
+ void addEventListener(in DOMString type,
+ in EventListener listener,
+ in boolean useCapture);
+ void removeEventListener(in DOMString type,
+ in EventListener listener,
+ in boolean useCapture);
+ boolean dispatchEvent(in Event evt)
+ raises(EventException);
+ };
+}
diff --git a/Source/WebCore/websockets/WebSocketChannel.cpp b/Source/WebCore/websockets/WebSocketChannel.cpp
new file mode 100644
index 0000000..09fcd6b
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketChannel.cpp
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketChannel.h"
+
+#include "CookieJar.h"
+#include "Document.h"
+#include "InspectorInstrumentation.h"
+#include "Logging.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "ProgressTracker.h"
+#include "ScriptExecutionContext.h"
+#include "SocketStreamError.h"
+#include "SocketStreamHandle.h"
+#include "WebSocketChannelClient.h"
+#include "WebSocketHandshake.h"
+
+#include <wtf/text/CString.h>
+#include <wtf/text/StringConcatenate.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/Deque.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/HashMap.h>
+
+namespace WebCore {
+
+WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol)
+ : m_context(context)
+ , m_client(client)
+ , m_handshake(url, protocol, context)
+ , m_buffer(0)
+ , m_bufferSize(0)
+ , m_resumeTimer(this, &WebSocketChannel::resumeTimerFired)
+ , m_suspended(false)
+ , m_closed(false)
+ , m_shouldDiscardReceivedData(false)
+ , m_unhandledBufferedAmount(0)
+ , m_identifier(0)
+{
+ if (m_context->isDocument())
+ if (Page* page = static_cast<Document*>(m_context)->page())
+ m_identifier = page->progress()->createUniqueIdentifier();
+
+ if (m_identifier)
+ InspectorInstrumentation::didCreateWebSocket(m_context, m_identifier, url, m_context->url());
+}
+
+WebSocketChannel::~WebSocketChannel()
+{
+ fastFree(m_buffer);
+}
+
+void WebSocketChannel::connect()
+{
+ LOG(Network, "WebSocketChannel %p connect", this);
+ ASSERT(!m_handle);
+ ASSERT(!m_suspended);
+ m_handshake.reset();
+ ref();
+ m_handle = SocketStreamHandle::create(m_handshake.url(), this);
+}
+
+bool WebSocketChannel::send(const String& msg)
+{
+ LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data());
+ ASSERT(m_handle);
+ ASSERT(!m_suspended);
+ Vector<char> buf;
+ buf.append('\0'); // frame type
+ CString utf8 = msg.utf8();
+ buf.append(utf8.data(), utf8.length());
+ buf.append('\xff'); // frame end
+ return m_handle->send(buf.data(), buf.size());
+}
+
+unsigned long WebSocketChannel::bufferedAmount() const
+{
+ LOG(Network, "WebSocketChannel %p bufferedAmount", this);
+ ASSERT(m_handle);
+ ASSERT(!m_suspended);
+ return m_handle->bufferedAmount();
+}
+
+void WebSocketChannel::close()
+{
+ LOG(Network, "WebSocketChannel %p close", this);
+ ASSERT(!m_suspended);
+ if (m_handle)
+ m_handle->close(); // will call didClose()
+}
+
+void WebSocketChannel::disconnect()
+{
+ LOG(Network, "WebSocketChannel %p disconnect", this);
+ if (m_identifier && m_context)
+ InspectorInstrumentation::didCloseWebSocket(m_context, m_identifier);
+ m_handshake.clearScriptExecutionContext();
+ m_client = 0;
+ m_context = 0;
+ if (m_handle)
+ m_handle->close();
+}
+
+void WebSocketChannel::suspend()
+{
+ m_suspended = true;
+}
+
+void WebSocketChannel::resume()
+{
+ m_suspended = false;
+ if ((m_buffer || m_closed) && m_client && !m_resumeTimer.isActive())
+ m_resumeTimer.startOneShot(0);
+}
+
+void WebSocketChannel::didOpen(SocketStreamHandle* handle)
+{
+ LOG(Network, "WebSocketChannel %p didOpen", this);
+ ASSERT(handle == m_handle);
+ if (!m_context)
+ return;
+ if (m_identifier)
+ InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_context, m_identifier, m_handshake.clientHandshakeRequest());
+ CString handshakeMessage = m_handshake.clientHandshakeMessage();
+ if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error sending handshake message.", 0, m_handshake.clientOrigin());
+ handle->close();
+ }
+}
+
+void WebSocketChannel::didClose(SocketStreamHandle* handle)
+{
+ LOG(Network, "WebSocketChannel %p didClose", this);
+ if (m_identifier && m_context)
+ InspectorInstrumentation::didCloseWebSocket(m_context, m_identifier);
+ ASSERT_UNUSED(handle, handle == m_handle || !m_handle);
+ m_closed = true;
+ if (m_handle) {
+ m_unhandledBufferedAmount = m_handle->bufferedAmount();
+ if (m_suspended)
+ return;
+ WebSocketChannelClient* client = m_client;
+ m_client = 0;
+ m_context = 0;
+ m_handle = 0;
+ if (client)
+ client->didClose(m_unhandledBufferedAmount);
+ }
+ deref();
+}
+
+void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* data, int len)
+{
+ LOG(Network, "WebSocketChannel %p didReceiveData %d", this, len);
+ RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
+ ASSERT(handle == m_handle);
+ if (!m_context) {
+ return;
+ }
+ if (!m_client) {
+ m_shouldDiscardReceivedData = true;
+ handle->close();
+ return;
+ }
+ if (m_shouldDiscardReceivedData)
+ return;
+ if (!appendToBuffer(data, len)) {
+ m_shouldDiscardReceivedData = true;
+ handle->close();
+ return;
+ }
+ while (!m_suspended && m_client && m_buffer)
+ if (!processBuffer())
+ break;
+}
+
+void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamError& error)
+{
+ LOG(Network, "WebSocketChannel %p didFail", this);
+ ASSERT(handle == m_handle || !m_handle);
+ if (m_context) {
+ String message;
+ if (error.isNull())
+ message = "WebSocket network error";
+ else if (error.localizedDescription().isNull())
+ message = makeString("WebSocket network error: error code ", String::number(error.errorCode()));
+ else
+ message = makeString("WebSocket network error: ", error.localizedDescription());
+ m_context->addMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, 0, error.failingURL());
+ }
+ m_shouldDiscardReceivedData = true;
+ handle->close();
+}
+
+void WebSocketChannel::didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)
+{
+}
+
+void WebSocketChannel::didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)
+{
+}
+
+bool WebSocketChannel::appendToBuffer(const char* data, size_t len)
+{
+ size_t newBufferSize = m_bufferSize + len;
+ if (newBufferSize < m_bufferSize) {
+ LOG(Network, "WebSocket buffer overflow (%lu+%lu)", static_cast<unsigned long>(m_bufferSize), static_cast<unsigned long>(len));
+ return false;
+ }
+ char* newBuffer = 0;
+ if (tryFastMalloc(newBufferSize).getValue(newBuffer)) {
+ if (m_buffer)
+ memcpy(newBuffer, m_buffer, m_bufferSize);
+ memcpy(newBuffer + m_bufferSize, data, len);
+ fastFree(m_buffer);
+ m_buffer = newBuffer;
+ m_bufferSize = newBufferSize;
+ return true;
+ }
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, makeString("WebSocket frame (at ", String::number(static_cast<unsigned long>(newBufferSize)), " bytes) is too long."), 0, m_handshake.clientOrigin());
+ return false;
+}
+
+void WebSocketChannel::skipBuffer(size_t len)
+{
+ ASSERT(len <= m_bufferSize);
+ m_bufferSize -= len;
+ if (!m_bufferSize) {
+ fastFree(m_buffer);
+ m_buffer = 0;
+ return;
+ }
+ memmove(m_buffer, m_buffer + len, m_bufferSize);
+}
+
+bool WebSocketChannel::processBuffer()
+{
+ ASSERT(!m_suspended);
+ ASSERT(m_client);
+ ASSERT(m_buffer);
+ if (m_shouldDiscardReceivedData)
+ return false;
+
+ if (m_handshake.mode() == WebSocketHandshake::Incomplete) {
+ int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize);
+ if (headerLength <= 0)
+ return false;
+ if (m_handshake.mode() == WebSocketHandshake::Connected) {
+ if (m_identifier)
+ InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(m_context, m_identifier, m_handshake.serverHandshakeResponse());
+ if (!m_handshake.serverSetCookie().isEmpty()) {
+ if (m_context->isDocument()) {
+ Document* document = static_cast<Document*>(m_context);
+ if (cookiesEnabled(document)) {
+ ExceptionCode ec; // Exception (for sandboxed documents) ignored.
+ document->setCookie(m_handshake.serverSetCookie(), ec);
+ }
+ }
+ }
+ // FIXME: handle set-cookie2.
+ LOG(Network, "WebSocketChannel %p connected", this);
+ skipBuffer(headerLength);
+ m_client->didConnect();
+ LOG(Network, "remaining in read buf %lu", static_cast<unsigned long>(m_bufferSize));
+ return m_buffer;
+ }
+ LOG(Network, "WebSocketChannel %p connection failed", this);
+ skipBuffer(headerLength);
+ m_shouldDiscardReceivedData = true;
+ if (!m_closed)
+ m_handle->close();
+ return false;
+ }
+ if (m_handshake.mode() != WebSocketHandshake::Connected)
+ return false;
+
+ const char* nextFrame = m_buffer;
+ const char* p = m_buffer;
+ const char* end = p + m_bufferSize;
+
+ unsigned char frameByte = static_cast<unsigned char>(*p++);
+ if ((frameByte & 0x80) == 0x80) {
+ size_t length = 0;
+ bool errorFrame = false;
+ while (p < end) {
+ if (length > std::numeric_limits<size_t>::max() / 128) {
+ LOG(Network, "frame length overflow %lu", static_cast<unsigned long>(length));
+ errorFrame = true;
+ break;
+ }
+ size_t newLength = length * 128;
+ unsigned char msgByte = static_cast<unsigned char>(*p);
+ unsigned int lengthMsgByte = msgByte & 0x7f;
+ if (newLength > std::numeric_limits<size_t>::max() - lengthMsgByte) {
+ LOG(Network, "frame length overflow %lu+%u", static_cast<unsigned long>(newLength), lengthMsgByte);
+ errorFrame = true;
+ break;
+ }
+ newLength += lengthMsgByte;
+ if (newLength < length) { // sanity check
+ LOG(Network, "frame length integer wrap %lu->%lu", static_cast<unsigned long>(length), static_cast<unsigned long>(newLength));
+ errorFrame = true;
+ break;
+ }
+ length = newLength;
+ ++p;
+ if (!(msgByte & 0x80))
+ break;
+ }
+ if (p + length < p) {
+ LOG(Network, "frame buffer pointer wrap %p+%lu->%p", p, static_cast<unsigned long>(length), p + length);
+ errorFrame = true;
+ }
+ if (errorFrame) {
+ skipBuffer(m_bufferSize); // Save memory.
+ m_shouldDiscardReceivedData = true;
+ m_client->didReceiveMessageError();
+ if (!m_client)
+ return false;
+ if (!m_closed)
+ m_handle->close();
+ return false;
+ }
+ ASSERT(p + length >= p);
+ if (p + length < end) {
+ p += length;
+ nextFrame = p;
+ ASSERT(nextFrame > m_buffer);
+ skipBuffer(nextFrame - m_buffer);
+ m_client->didReceiveMessageError();
+ return m_buffer;
+ }
+ return false;
+ }
+
+ const char* msgStart = p;
+ while (p < end && *p != '\xff')
+ ++p;
+ if (p < end && *p == '\xff') {
+ int msgLength = p - msgStart;
+ ++p;
+ nextFrame = p;
+ if (frameByte == 0x00) {
+ String msg = String::fromUTF8(msgStart, msgLength);
+ skipBuffer(nextFrame - m_buffer);
+ m_client->didReceiveMessage(msg);
+ } else {
+ skipBuffer(nextFrame - m_buffer);
+ m_client->didReceiveMessageError();
+ }
+ return m_buffer;
+ }
+ return false;
+}
+
+void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer)
+{
+ ASSERT_UNUSED(timer, timer == &m_resumeTimer);
+
+ RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
+ while (!m_suspended && m_client && m_buffer)
+ if (!processBuffer())
+ break;
+ if (!m_suspended && m_client && m_closed && m_handle)
+ didClose(m_handle.get());
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/WebSocketChannel.h b/Source/WebCore/websockets/WebSocketChannel.h
new file mode 100644
index 0000000..9c52377
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketChannel.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketChannel_h
+#define WebSocketChannel_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "SocketStreamHandleClient.h"
+#include "ThreadableWebSocketChannel.h"
+#include "Timer.h"
+#include "WebSocketHandshake.h"
+#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+ class ScriptExecutionContext;
+ class SocketStreamHandle;
+ class SocketStreamError;
+ class WebSocketChannelClient;
+
+ class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient, public ThreadableWebSocketChannel {
+ public:
+ static PassRefPtr<WebSocketChannel> create(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) { return adoptRef(new WebSocketChannel(context, client, url, protocol)); }
+ virtual ~WebSocketChannel();
+
+ virtual void connect();
+ virtual bool send(const String& message);
+ virtual unsigned long bufferedAmount() const;
+ virtual void close();
+ virtual void disconnect(); // Will suppress didClose().
+
+ virtual void suspend();
+ virtual void resume();
+
+ virtual void didOpen(SocketStreamHandle*);
+ virtual void didClose(SocketStreamHandle*);
+ virtual void didReceiveData(SocketStreamHandle*, const char*, int);
+ virtual void didFail(SocketStreamHandle*, const SocketStreamError&);
+ virtual void didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&);
+ virtual void didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&);
+
+ using RefCounted<WebSocketChannel>::ref;
+ using RefCounted<WebSocketChannel>::deref;
+
+ protected:
+ virtual void refThreadableWebSocketChannel() { ref(); }
+ virtual void derefThreadableWebSocketChannel() { deref(); }
+
+ private:
+ WebSocketChannel(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String& protocol);
+
+ bool appendToBuffer(const char* data, size_t len);
+ void skipBuffer(size_t len);
+ bool processBuffer();
+ void resumeTimerFired(Timer<WebSocketChannel>* timer);
+
+ ScriptExecutionContext* m_context;
+ WebSocketChannelClient* m_client;
+ WebSocketHandshake m_handshake;
+ RefPtr<SocketStreamHandle> m_handle;
+ char* m_buffer;
+ size_t m_bufferSize;
+
+ Timer<WebSocketChannel> m_resumeTimer;
+ bool m_suspended;
+ bool m_closed;
+ bool m_shouldDiscardReceivedData;
+ unsigned long m_unhandledBufferedAmount;
+
+ unsigned long m_identifier; // m_identifier == 0 means that we could not obtain a valid identifier.
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketChannel_h
diff --git a/Source/WebCore/websockets/WebSocketChannelClient.h b/Source/WebCore/websockets/WebSocketChannelClient.h
new file mode 100644
index 0000000..1551073
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketChannelClient.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketChannelClient_h
+#define WebSocketChannelClient_h
+
+#if ENABLE(WEB_SOCKETS)
+
+namespace WebCore {
+
+ class WebSocketChannelClient {
+ public:
+ virtual ~WebSocketChannelClient() { }
+ virtual void didConnect() { }
+ virtual void didReceiveMessage(const String&) { }
+ virtual void didReceiveMessageError() { }
+ virtual void didClose(unsigned long /* unhandledBufferedAmount */) { }
+
+ protected:
+ WebSocketChannelClient() { }
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketChannelClient_h
diff --git a/Source/WebCore/websockets/WebSocketHandshake.cpp b/Source/WebCore/websockets/WebSocketHandshake.cpp
new file mode 100644
index 0000000..5049098
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketHandshake.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketHandshake.h"
+
+#include "CharacterNames.h"
+#include "Cookie.h"
+#include "CookieJar.h"
+#include "Document.h"
+#include "HTTPHeaderMap.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
+
+#include <wtf/MD5.h>
+#include <wtf/RandomNumber.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringExtras.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringConcatenate.h>
+
+namespace WebCore {
+
+static const char randomCharacterInSecWebSocketKey[] = "!\"#$%&'()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
+
+static String resourceName(const KURL& url)
+{
+ String name = url.path();
+ if (name.isEmpty())
+ name = "/";
+ if (!url.query().isNull())
+ name += "?" + url.query();
+ ASSERT(!name.isEmpty());
+ ASSERT(!name.contains(' '));
+ return name;
+}
+
+static String hostName(const KURL& url, bool secure)
+{
+ ASSERT(url.protocolIs("wss") == secure);
+ StringBuilder builder;
+ builder.append(url.host().lower());
+ if (url.port() && ((!secure && url.port() != 80) || (secure && url.port() != 443))) {
+ builder.append(':');
+ builder.append(String::number(url.port()));
+ }
+ return builder.toString();
+}
+
+static const size_t maxConsoleMessageSize = 128;
+static String trimConsoleMessage(const char* p, size_t len)
+{
+ String s = String(p, std::min<size_t>(len, maxConsoleMessageSize));
+ if (len > maxConsoleMessageSize)
+ s.append(horizontalEllipsis);
+ return s;
+}
+
+static void generateSecWebSocketKey(uint32_t& number, String& key)
+{
+ uint32_t space = static_cast<uint32_t>(randomNumber() * 12) + 1;
+ uint32_t max = 4294967295U / space;
+ number = static_cast<uint32_t>(randomNumber() * max);
+ uint32_t product = number * space;
+
+ String s = String::number(product);
+ int n = static_cast<int>(randomNumber() * 12) + 1;
+ DEFINE_STATIC_LOCAL(String, randomChars, (randomCharacterInSecWebSocketKey));
+ for (int i = 0; i < n; i++) {
+ int pos = static_cast<int>(randomNumber() * (s.length() + 1));
+ int chpos = static_cast<int>(randomNumber() * randomChars.length());
+ s.insert(randomChars.substring(chpos, 1), pos);
+ }
+ DEFINE_STATIC_LOCAL(String, spaceChar, (" "));
+ for (uint32_t i = 0; i < space; i++) {
+ int pos = static_cast<int>(randomNumber() * (s.length() - 1)) + 1;
+ s.insert(spaceChar, pos);
+ }
+ ASSERT(s[0] != ' ');
+ ASSERT(s[s.length() - 1] != ' ');
+ key = s;
+}
+
+static void generateKey3(unsigned char key3[8])
+{
+ for (int i = 0; i < 8; i++)
+ key3[i] = randomNumber() * 256;
+}
+
+static void setChallengeNumber(unsigned char* buf, uint32_t number)
+{
+ unsigned char* p = buf + 3;
+ for (int i = 0; i < 4; i++) {
+ *p = number & 0xFF;
+ --p;
+ number >>= 8;
+ }
+}
+
+static void generateExpectedChallengeResponse(uint32_t number1, uint32_t number2, unsigned char key3[8], unsigned char expectedChallenge[16])
+{
+ unsigned char challenge[16];
+ setChallengeNumber(&challenge[0], number1);
+ setChallengeNumber(&challenge[4], number2);
+ memcpy(&challenge[8], key3, 8);
+ MD5 md5;
+ md5.addBytes(challenge, sizeof(challenge));
+ Vector<uint8_t, 16> digest;
+ md5.checksum(digest);
+ memcpy(expectedChallenge, digest.data(), 16);
+}
+
+WebSocketHandshake::WebSocketHandshake(const KURL& url, const String& protocol, ScriptExecutionContext* context)
+ : m_url(url)
+ , m_clientProtocol(protocol)
+ , m_secure(m_url.protocolIs("wss"))
+ , m_context(context)
+ , m_mode(Incomplete)
+{
+ uint32_t number1;
+ uint32_t number2;
+ generateSecWebSocketKey(number1, m_secWebSocketKey1);
+ generateSecWebSocketKey(number2, m_secWebSocketKey2);
+ generateKey3(m_key3);
+ generateExpectedChallengeResponse(number1, number2, m_key3, m_expectedChallengeResponse);
+}
+
+WebSocketHandshake::~WebSocketHandshake()
+{
+}
+
+const KURL& WebSocketHandshake::url() const
+{
+ return m_url;
+}
+
+void WebSocketHandshake::setURL(const KURL& url)
+{
+ m_url = url.copy();
+}
+
+const String WebSocketHandshake::host() const
+{
+ return m_url.host().lower();
+}
+
+const String& WebSocketHandshake::clientProtocol() const
+{
+ return m_clientProtocol;
+}
+
+void WebSocketHandshake::setClientProtocol(const String& protocol)
+{
+ m_clientProtocol = protocol;
+}
+
+bool WebSocketHandshake::secure() const
+{
+ return m_secure;
+}
+
+String WebSocketHandshake::clientOrigin() const
+{
+ return m_context->securityOrigin()->toString();
+}
+
+String WebSocketHandshake::clientLocation() const
+{
+ StringBuilder builder;
+ builder.append(m_secure ? "wss" : "ws");
+ builder.append("://");
+ builder.append(hostName(m_url, m_secure));
+ builder.append(resourceName(m_url));
+ return builder.toString();
+}
+
+CString WebSocketHandshake::clientHandshakeMessage() const
+{
+ // Keep the following consistent with clientHandshakeRequest().
+ StringBuilder builder;
+
+ builder.append("GET ");
+ builder.append(resourceName(m_url));
+ builder.append(" HTTP/1.1\r\n");
+
+ Vector<String> fields;
+ fields.append("Upgrade: WebSocket");
+ fields.append("Connection: Upgrade");
+ fields.append("Host: " + hostName(m_url, m_secure));
+ fields.append("Origin: " + clientOrigin());
+ if (!m_clientProtocol.isEmpty())
+ fields.append("Sec-WebSocket-Protocol: " + m_clientProtocol);
+
+ KURL url = httpURLForAuthenticationAndCookies();
+ if (m_context->isDocument()) {
+ Document* document = static_cast<Document*>(m_context);
+ String cookie = cookieRequestHeaderFieldValue(document, url);
+ if (!cookie.isEmpty())
+ fields.append("Cookie: " + cookie);
+ // Set "Cookie2: <cookie>" if cookies 2 exists for url?
+ }
+
+ fields.append("Sec-WebSocket-Key1: " + m_secWebSocketKey1);
+ fields.append("Sec-WebSocket-Key2: " + m_secWebSocketKey2);
+
+ // Fields in the handshake are sent by the client in a random order; the
+ // order is not meaningful. Thus, it's ok to send the order we constructed
+ // the fields.
+
+ for (size_t i = 0; i < fields.size(); i++) {
+ builder.append(fields[i]);
+ builder.append("\r\n");
+ }
+
+ builder.append("\r\n");
+
+ CString handshakeHeader = builder.toString().utf8();
+ char* characterBuffer = 0;
+ CString msg = CString::newUninitialized(handshakeHeader.length() + sizeof(m_key3), characterBuffer);
+ memcpy(characterBuffer, handshakeHeader.data(), handshakeHeader.length());
+ memcpy(characterBuffer + handshakeHeader.length(), m_key3, sizeof(m_key3));
+ return msg;
+}
+
+WebSocketHandshakeRequest WebSocketHandshake::clientHandshakeRequest() const
+{
+ // Keep the following consistent with clientHandshakeMessage().
+ // FIXME: do we need to store m_secWebSocketKey1, m_secWebSocketKey2 and
+ // m_key3 in WebSocketHandshakeRequest?
+ WebSocketHandshakeRequest request("GET", m_url);
+ request.addHeaderField("Upgrade", "WebSocket");
+ request.addHeaderField("Connection", "Upgrade");
+ request.addHeaderField("Host", hostName(m_url, m_secure));
+ request.addHeaderField("Origin", clientOrigin());
+ if (!m_clientProtocol.isEmpty())
+ request.addHeaderField("Sec-WebSocket-Protocol:", m_clientProtocol);
+
+ KURL url = httpURLForAuthenticationAndCookies();
+ if (m_context->isDocument()) {
+ Document* document = static_cast<Document*>(m_context);
+ String cookie = cookieRequestHeaderFieldValue(document, url);
+ if (!cookie.isEmpty())
+ request.addHeaderField("Cookie", cookie);
+ // Set "Cookie2: <cookie>" if cookies 2 exists for url?
+ }
+
+ request.addHeaderField("Sec-WebSocket-Key1", m_secWebSocketKey1);
+ request.addHeaderField("Sec-WebSocket-Key2", m_secWebSocketKey2);
+ request.setKey3(m_key3);
+
+ return request;
+}
+
+void WebSocketHandshake::reset()
+{
+ m_mode = Incomplete;
+
+ m_wsOrigin = String();
+ m_wsLocation = String();
+ m_wsProtocol = String();
+ m_setCookie = String();
+ m_setCookie2 = String();
+}
+
+void WebSocketHandshake::clearScriptExecutionContext()
+{
+ m_context = 0;
+}
+
+int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
+{
+ m_mode = Incomplete;
+ int statusCode;
+ String statusText;
+ int lineLength = readStatusLine(header, len, statusCode, statusText);
+ if (lineLength == -1)
+ return -1;
+ if (statusCode == -1) {
+ m_mode = Failed;
+ return len;
+ }
+ LOG(Network, "response code: %d", statusCode);
+ m_response.setStatusCode(statusCode);
+ m_response.setStatusText(statusText);
+ if (statusCode != 101) {
+ m_mode = Failed;
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, makeString("Unexpected response code: ", String::number(statusCode)), 0, clientOrigin());
+ return len;
+ }
+ m_mode = Normal;
+ if (!strnstr(header, "\r\n\r\n", len)) {
+ // Just hasn't been received fully yet.
+ m_mode = Incomplete;
+ return -1;
+ }
+ const char* p = readHTTPHeaders(header + lineLength, header + len);
+ if (!p) {
+ LOG(Network, "readHTTPHeaders failed");
+ m_mode = Failed;
+ return len;
+ }
+ processHeaders();
+ if (!checkResponseHeaders()) {
+ LOG(Network, "header process failed");
+ m_mode = Failed;
+ return p - header;
+ }
+ if (len < static_cast<size_t>(p - header + sizeof(m_expectedChallengeResponse))) {
+ // Just hasn't been received /expected/ yet.
+ m_mode = Incomplete;
+ return -1;
+ }
+ m_response.setChallengeResponse(static_cast<const unsigned char*>(static_cast<const void*>(p)));
+ if (memcmp(p, m_expectedChallengeResponse, sizeof(m_expectedChallengeResponse))) {
+ m_mode = Failed;
+ return (p - header) + sizeof(m_expectedChallengeResponse);
+ }
+ m_mode = Connected;
+ return (p - header) + sizeof(m_expectedChallengeResponse);
+}
+
+WebSocketHandshake::Mode WebSocketHandshake::mode() const
+{
+ return m_mode;
+}
+
+const String& WebSocketHandshake::serverWebSocketOrigin() const
+{
+ return m_wsOrigin;
+}
+
+void WebSocketHandshake::setServerWebSocketOrigin(const String& webSocketOrigin)
+{
+ m_wsOrigin = webSocketOrigin;
+}
+
+const String& WebSocketHandshake::serverWebSocketLocation() const
+{
+ return m_wsLocation;
+}
+
+void WebSocketHandshake::setServerWebSocketLocation(const String& webSocketLocation)
+{
+ m_wsLocation = webSocketLocation;
+}
+
+const String& WebSocketHandshake::serverWebSocketProtocol() const
+{
+ return m_wsProtocol;
+}
+
+void WebSocketHandshake::setServerWebSocketProtocol(const String& webSocketProtocol)
+{
+ m_wsProtocol = webSocketProtocol;
+}
+
+const String& WebSocketHandshake::serverSetCookie() const
+{
+ return m_setCookie;
+}
+
+void WebSocketHandshake::setServerSetCookie(const String& setCookie)
+{
+ m_setCookie = setCookie;
+}
+
+const String& WebSocketHandshake::serverSetCookie2() const
+{
+ return m_setCookie2;
+}
+
+void WebSocketHandshake::setServerSetCookie2(const String& setCookie2)
+{
+ m_setCookie2 = setCookie2;
+}
+
+const WebSocketHandshakeResponse& WebSocketHandshake::serverHandshakeResponse() const
+{
+ return m_response;
+}
+
+KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
+{
+ KURL url = m_url.copy();
+ bool couldSetProtocol = url.setProtocol(m_secure ? "https" : "http");
+ ASSERT_UNUSED(couldSetProtocol, couldSetProtocol);
+ return url;
+}
+
+// Returns the header length (including "\r\n"), or -1 if we have not received enough data yet.
+// If the line is malformed or the status code is not a 3-digit number,
+// statusCode and statusText will be set to -1 and a null string, respectively.
+int WebSocketHandshake::readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText)
+{
+ statusCode = -1;
+ statusText = String();
+
+ const char* space1 = 0;
+ const char* space2 = 0;
+ const char* p;
+ size_t consumedLength;
+
+ for (p = header, consumedLength = 0; consumedLength < headerLength; p++, consumedLength++) {
+ if (*p == ' ') {
+ if (!space1)
+ space1 = p;
+ else if (!space2)
+ space2 = p;
+ } else if (*p == '\n')
+ break;
+ }
+ if (consumedLength == headerLength)
+ return -1; // We have not received '\n' yet.
+
+ const char* end = p + 1;
+ if (end - header > INT_MAX) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line is too long: " + trimConsoleMessage(header, maxConsoleMessageSize + 1), 0, clientOrigin());
+ return INT_MAX;
+ }
+ int lineLength = end - header;
+
+ if (!space1 || !space2) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "No response code found: " + trimConsoleMessage(header, lineLength - 1), 0, clientOrigin());
+ return lineLength;
+ }
+
+ // The line must end with "\r\n".
+ if (*(end - 2) != '\r') {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Status line does not end with CRLF", 0, clientOrigin());
+ return lineLength;
+ }
+
+ String statusCodeString(space1 + 1, space2 - space1 - 1);
+ if (statusCodeString.length() != 3) // Status code must consist of three digits.
+ return lineLength;
+ for (int i = 0; i < 3; ++i)
+ if (statusCodeString[i] < '0' || statusCodeString[i] > '9') {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Invalid status code: " + statusCodeString, 0, clientOrigin());
+ return lineLength;
+ }
+
+ bool ok = false;
+ statusCode = statusCodeString.toInt(&ok);
+ ASSERT(ok);
+
+ statusText = String(space2 + 1, end - space2 - 3); // Exclude "\r\n".
+ return lineLength;
+}
+
+const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end)
+{
+ m_response.clearHeaderFields();
+
+ Vector<char> name;
+ Vector<char> value;
+ for (const char* p = start; p < end; p++) {
+ name.clear();
+ value.clear();
+
+ for (; p < end; p++) {
+ switch (*p) {
+ case '\r':
+ if (name.isEmpty()) {
+ if (p + 1 < end && *(p + 1) == '\n')
+ return p + 2;
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF at " + trimConsoleMessage(p, end - p), 0, clientOrigin());
+ return 0;
+ }
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected CR in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin());
+ return 0;
+ case '\n':
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in name at " + trimConsoleMessage(name.data(), name.size()), 0, clientOrigin());
+ return 0;
+ case ':':
+ break;
+ default:
+ name.append(*p);
+ continue;
+ }
+ if (*p == ':') {
+ ++p;
+ break;
+ }
+ }
+
+ for (; p < end && *p == 0x20; p++) { }
+
+ for (; p < end; p++) {
+ switch (*p) {
+ case '\r':
+ break;
+ case '\n':
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Unexpected LF in value at " + trimConsoleMessage(value.data(), value.size()), 0, clientOrigin());
+ return 0;
+ default:
+ value.append(*p);
+ }
+ if (*p == '\r') {
+ ++p;
+ break;
+ }
+ }
+ if (p >= end || *p != '\n') {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "CR doesn't follow LF after value at " + trimConsoleMessage(p, end - p), 0, clientOrigin());
+ return 0;
+ }
+ AtomicString nameStr(String::fromUTF8(name.data(), name.size()));
+ String valueStr = String::fromUTF8(value.data(), value.size());
+ if (nameStr.isNull()) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "invalid UTF-8 sequence in header name", 0, clientOrigin());
+ return 0;
+ }
+ if (valueStr.isNull()) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "invalid UTF-8 sequence in header value", 0, clientOrigin());
+ return 0;
+ }
+ LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data());
+ m_response.addHeaderField(nameStr, valueStr);
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void WebSocketHandshake::processHeaders()
+{
+ ASSERT(m_mode == Normal);
+ const HTTPHeaderMap& headers = m_response.headerFields();
+ m_wsOrigin = headers.get("sec-websocket-origin");
+ m_wsLocation = headers.get("sec-websocket-location");
+ m_wsProtocol = headers.get("sec-websocket-protocol");
+ m_setCookie = headers.get("set-cookie");
+ m_setCookie2 = headers.get("set-cookie2");
+}
+
+bool WebSocketHandshake::checkResponseHeaders()
+{
+ if (m_wsOrigin.isNull()) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'sec-websocket-origin' header is missing", 0, clientOrigin());
+ return false;
+ }
+ if (m_wsLocation.isNull()) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: 'sec-websocket-location' header is missing", 0, clientOrigin());
+ return false;
+ }
+
+ if (clientOrigin() != m_wsOrigin) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: origin mismatch: " + clientOrigin() + " != " + m_wsOrigin, 0, clientOrigin());
+ return false;
+ }
+ if (clientLocation() != m_wsLocation) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: location mismatch: " + clientLocation() + " != " + m_wsLocation, 0, clientOrigin());
+ return false;
+ }
+ if (!m_clientProtocol.isEmpty() && m_clientProtocol != m_wsProtocol) {
+ m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error during WebSocket handshake: protocol mismatch: " + m_clientProtocol + " != " + m_wsProtocol, 0, clientOrigin());
+ return false;
+ }
+ return true;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/WebSocketHandshake.h b/Source/WebCore/websockets/WebSocketHandshake.h
new file mode 100644
index 0000000..a5b4260
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketHandshake.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2009 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketHandshake_h
+#define WebSocketHandshake_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "KURL.h"
+#include "PlatformString.h"
+#include "WebSocketHandshakeRequest.h"
+#include "WebSocketHandshakeResponse.h"
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+ class ScriptExecutionContext;
+
+ class WebSocketHandshake : public Noncopyable {
+ public:
+ enum Mode {
+ Incomplete, Normal, Failed, Connected
+ };
+ WebSocketHandshake(const KURL&, const String& protocol, ScriptExecutionContext*);
+ ~WebSocketHandshake();
+
+ const KURL& url() const;
+ void setURL(const KURL&);
+ const String host() const;
+
+ const String& clientProtocol() const;
+ void setClientProtocol(const String& protocol);
+
+ bool secure() const;
+
+ String clientOrigin() const;
+ String clientLocation() const;
+
+ CString clientHandshakeMessage() const;
+ WebSocketHandshakeRequest clientHandshakeRequest() const;
+
+ void reset();
+ void clearScriptExecutionContext();
+
+ int readServerHandshake(const char* header, size_t len);
+ Mode mode() const;
+
+ const String& serverWebSocketOrigin() const;
+ void setServerWebSocketOrigin(const String& webSocketOrigin);
+
+ const String& serverWebSocketLocation() const;
+ void setServerWebSocketLocation(const String& webSocketLocation);
+
+ const String& serverWebSocketProtocol() const;
+ void setServerWebSocketProtocol(const String& webSocketProtocol);
+
+ const String& serverSetCookie() const;
+ void setServerSetCookie(const String& setCookie);
+ const String& serverSetCookie2() const;
+ void setServerSetCookie2(const String& setCookie2);
+
+ const WebSocketHandshakeResponse& serverHandshakeResponse() const;
+
+ private:
+ KURL httpURLForAuthenticationAndCookies() const;
+
+ int readStatusLine(const char* header, size_t headerLength, int& statusCode, String& statusText);
+
+ // Reads all headers except for the two predefined ones.
+ const char* readHTTPHeaders(const char* start, const char* end);
+ void processHeaders();
+ bool checkResponseHeaders();
+
+ KURL m_url;
+ String m_clientProtocol;
+ bool m_secure;
+ ScriptExecutionContext* m_context;
+
+ Mode m_mode;
+
+ String m_wsOrigin;
+ String m_wsLocation;
+ String m_wsProtocol;
+ String m_setCookie;
+ String m_setCookie2;
+
+ String m_secWebSocketKey1;
+ String m_secWebSocketKey2;
+ unsigned char m_key3[8];
+ unsigned char m_expectedChallengeResponse[16];
+
+ WebSocketHandshakeResponse m_response;
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketHandshake_h
diff --git a/Source/WebCore/websockets/WebSocketHandshakeRequest.cpp b/Source/WebCore/websockets/WebSocketHandshakeRequest.cpp
new file mode 100644
index 0000000..1132a44
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketHandshakeRequest.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketHandshakeRequest.h"
+
+#include <cstring>
+
+using namespace std;
+
+namespace WebCore {
+
+WebSocketHandshakeRequest::Key3::Key3()
+{
+ memset(value, 0, sizeof(value));
+}
+
+void WebSocketHandshakeRequest::Key3::set(const unsigned char key3[8])
+{
+ memcpy(value, key3, sizeof(value));
+}
+
+WebSocketHandshakeRequest::WebSocketHandshakeRequest(const String& requestMethod, const KURL& url)
+ : m_url(url)
+ , m_requestMethod(requestMethod)
+{
+}
+
+WebSocketHandshakeRequest::~WebSocketHandshakeRequest()
+{
+}
+
+String WebSocketHandshakeRequest::requestMethod() const
+{
+ return m_requestMethod;
+}
+
+KURL WebSocketHandshakeRequest::url() const
+{
+ return m_url;
+}
+
+void WebSocketHandshakeRequest::addHeaderField(const char* name, const String& value)
+{
+ m_headerFields.add(name, value);
+}
+
+const HTTPHeaderMap& WebSocketHandshakeRequest::headerFields() const
+{
+ return m_headerFields;
+}
+
+WebSocketHandshakeRequest::Key3 WebSocketHandshakeRequest::key3() const
+{
+ return m_key3;
+}
+
+void WebSocketHandshakeRequest::setKey3(const unsigned char key3[8])
+{
+ m_key3.set(key3);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/WebSocketHandshakeRequest.h b/Source/WebCore/websockets/WebSocketHandshakeRequest.h
new file mode 100644
index 0000000..792f67e
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketHandshakeRequest.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketHandshakeRequest_h
+#define WebSocketHandshakeRequest_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "HTTPHeaderMap.h"
+#include "KURL.h"
+#include "PlatformString.h"
+
+namespace WebCore {
+
+class WebSocketHandshakeRequest {
+public:
+ WebSocketHandshakeRequest(const String& requestMethod, const KURL&);
+ ~WebSocketHandshakeRequest();
+
+ String requestMethod() const;
+ KURL url() const;
+
+ const HTTPHeaderMap& headerFields() const;
+ void addHeaderField(const char* name, const String& value);
+
+ struct Key3 {
+ unsigned char value[8];
+
+ Key3();
+ void set(const unsigned char key3[8]);
+ };
+ Key3 key3() const;
+ void setKey3(const unsigned char key3[8]);
+
+private:
+ KURL m_url;
+ String m_requestMethod;
+ HTTPHeaderMap m_headerFields;
+ Key3 m_key3;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketHandshakeRequest_h
diff --git a/Source/WebCore/websockets/WebSocketHandshakeResponse.cpp b/Source/WebCore/websockets/WebSocketHandshakeResponse.cpp
new file mode 100644
index 0000000..2e0dad7
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketHandshakeResponse.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "WebSocketHandshakeResponse.h"
+
+#include <wtf/Assertions.h>
+#include <wtf/text/AtomicString.h>
+
+using namespace std;
+
+namespace WebCore {
+
+WebSocketHandshakeResponse::ChallengeResponse::ChallengeResponse()
+{
+ memset(value, 0, sizeof(value));
+}
+
+void WebSocketHandshakeResponse::ChallengeResponse::set(const unsigned char challengeResponse[16])
+{
+ memcpy(value, challengeResponse, sizeof(value));
+}
+
+WebSocketHandshakeResponse::WebSocketHandshakeResponse()
+{
+}
+
+WebSocketHandshakeResponse::~WebSocketHandshakeResponse()
+{
+}
+
+int WebSocketHandshakeResponse::statusCode() const
+{
+ return m_statusCode;
+}
+
+void WebSocketHandshakeResponse::setStatusCode(int statusCode)
+{
+ ASSERT(statusCode >= 100 && statusCode < 600);
+ m_statusCode = statusCode;
+}
+
+const String& WebSocketHandshakeResponse::statusText() const
+{
+ return m_statusText;
+}
+
+void WebSocketHandshakeResponse::setStatusText(const String& statusText)
+{
+ m_statusText = statusText;
+}
+
+const HTTPHeaderMap& WebSocketHandshakeResponse::headerFields() const
+{
+ return m_headerFields;
+}
+
+void WebSocketHandshakeResponse::addHeaderField(const AtomicString& name, const String& value)
+{
+ m_headerFields.add(name, value);
+}
+
+void WebSocketHandshakeResponse::clearHeaderFields()
+{
+ m_headerFields.clear();
+}
+
+const WebSocketHandshakeResponse::ChallengeResponse& WebSocketHandshakeResponse::challengeResponse() const
+{
+ return m_challengeResponse;
+}
+
+void WebSocketHandshakeResponse::setChallengeResponse(const unsigned char challengeResponse[16])
+{
+ m_challengeResponse.set(challengeResponse);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/WebSocketHandshakeResponse.h b/Source/WebCore/websockets/WebSocketHandshakeResponse.h
new file mode 100644
index 0000000..fe435ee
--- /dev/null
+++ b/Source/WebCore/websockets/WebSocketHandshakeResponse.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebSocketHandshakeResponse_h
+#define WebSocketHandshakeResponse_h
+
+#if ENABLE(WEB_SOCKETS)
+
+#include "HTTPHeaderMap.h"
+#include "PlatformString.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class WebSocketHandshakeResponse {
+public:
+ WebSocketHandshakeResponse();
+ ~WebSocketHandshakeResponse();
+
+ int statusCode() const;
+ void setStatusCode(int statusCode);
+ const String& statusText() const;
+ void setStatusText(const String& statusText);
+ const HTTPHeaderMap& headerFields() const;
+ void addHeaderField(const AtomicString& name, const String& value);
+ void clearHeaderFields();
+
+ struct ChallengeResponse {
+ unsigned char value[16];
+
+ ChallengeResponse();
+ void set(const unsigned char challengeResponse[16]);
+ };
+ const ChallengeResponse& challengeResponse() const;
+ void setChallengeResponse(const unsigned char challengeResponse[16]);
+
+private:
+ int m_statusCode;
+ String m_statusText;
+ HTTPHeaderMap m_headerFields;
+ ChallengeResponse m_challengeResponse;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketHandshakeResponse_h
diff --git a/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp
new file mode 100644
index 0000000..fd82591
--- /dev/null
+++ b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.cpp
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_SOCKETS) && ENABLE(WORKERS)
+
+#include "WorkerThreadableWebSocketChannel.h"
+
+#include "CrossThreadTask.h"
+#include "PlatformString.h"
+#include "ScriptExecutionContext.h"
+#include "ThreadableWebSocketChannelClientWrapper.h"
+#include "WebSocketChannel.h"
+#include "WebSocketChannelClient.h"
+#include "WorkerContext.h"
+#include "WorkerLoaderProxy.h"
+#include "WorkerRunLoop.h"
+#include "WorkerThread.h"
+
+#include <wtf/PassRefPtr.h>
+
+namespace WebCore {
+
+WorkerThreadableWebSocketChannel::WorkerThreadableWebSocketChannel(WorkerContext* context, WebSocketChannelClient* client, const String& taskMode, const KURL& url, const String& protocol)
+ : m_workerContext(context)
+ , m_workerClientWrapper(ThreadableWebSocketChannelClientWrapper::create(client))
+ , m_bridge(Bridge::create(m_workerClientWrapper, m_workerContext, taskMode, url, protocol))
+{
+}
+
+WorkerThreadableWebSocketChannel::~WorkerThreadableWebSocketChannel()
+{
+ if (m_bridge)
+ m_bridge->disconnect();
+}
+
+void WorkerThreadableWebSocketChannel::connect()
+{
+ if (m_bridge)
+ m_bridge->connect();
+}
+
+bool WorkerThreadableWebSocketChannel::send(const String& message)
+{
+ if (!m_bridge)
+ return false;
+ return m_bridge->send(message);
+}
+
+unsigned long WorkerThreadableWebSocketChannel::bufferedAmount() const
+{
+ if (!m_bridge)
+ return 0;
+ return m_bridge->bufferedAmount();
+}
+
+void WorkerThreadableWebSocketChannel::close()
+{
+ if (m_bridge)
+ m_bridge->close();
+}
+
+void WorkerThreadableWebSocketChannel::disconnect()
+{
+ m_bridge->disconnect();
+ m_bridge.clear();
+}
+
+void WorkerThreadableWebSocketChannel::suspend()
+{
+ m_workerClientWrapper->suspend();
+ if (m_bridge)
+ m_bridge->suspend();
+}
+
+void WorkerThreadableWebSocketChannel::resume()
+{
+ m_workerClientWrapper->resume();
+ if (m_bridge)
+ m_bridge->resume();
+}
+
+WorkerThreadableWebSocketChannel::Peer::Peer(RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext* context, const String& taskMode, const KURL& url, const String& protocol)
+ : m_workerClientWrapper(clientWrapper)
+ , m_loaderProxy(loaderProxy)
+ , m_mainWebSocketChannel(WebSocketChannel::create(context, this, url, protocol))
+ , m_taskMode(taskMode)
+{
+ ASSERT(isMainThread());
+}
+
+WorkerThreadableWebSocketChannel::Peer::~Peer()
+{
+ ASSERT(isMainThread());
+ if (m_mainWebSocketChannel)
+ m_mainWebSocketChannel->disconnect();
+}
+
+void WorkerThreadableWebSocketChannel::Peer::connect()
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel)
+ return;
+ m_mainWebSocketChannel->connect();
+}
+
+static void workerContextDidSend(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, bool sent)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->setSent(sent);
+}
+
+void WorkerThreadableWebSocketChannel::Peer::send(const String& message)
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel || !m_workerClientWrapper)
+ return;
+ bool sent = m_mainWebSocketChannel->send(message);
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidSend, m_workerClientWrapper, sent), m_taskMode);
+}
+
+static void workerContextDidGetBufferedAmount(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long bufferedAmount)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->setBufferedAmount(bufferedAmount);
+}
+
+void WorkerThreadableWebSocketChannel::Peer::bufferedAmount()
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel || !m_workerClientWrapper)
+ return;
+ unsigned long bufferedAmount = m_mainWebSocketChannel->bufferedAmount();
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidGetBufferedAmount, m_workerClientWrapper, bufferedAmount), m_taskMode);
+}
+
+void WorkerThreadableWebSocketChannel::Peer::close()
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel)
+ return;
+ m_mainWebSocketChannel->close();
+ m_mainWebSocketChannel = 0;
+}
+
+void WorkerThreadableWebSocketChannel::Peer::disconnect()
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel)
+ return;
+ m_mainWebSocketChannel->disconnect();
+ m_mainWebSocketChannel = 0;
+}
+
+void WorkerThreadableWebSocketChannel::Peer::suspend()
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel)
+ return;
+ m_mainWebSocketChannel->suspend();
+}
+
+void WorkerThreadableWebSocketChannel::Peer::resume()
+{
+ ASSERT(isMainThread());
+ if (!m_mainWebSocketChannel)
+ return;
+ m_mainWebSocketChannel->resume();
+}
+
+static void workerContextDidConnect(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didConnect();
+}
+
+void WorkerThreadableWebSocketChannel::Peer::didConnect()
+{
+ ASSERT(isMainThread());
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidConnect, m_workerClientWrapper), m_taskMode);
+}
+
+static void workerContextDidReceiveMessage(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, const String& message)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didReceiveMessage(message);
+}
+
+void WorkerThreadableWebSocketChannel::Peer::didReceiveMessage(const String& message)
+{
+ ASSERT(isMainThread());
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidReceiveMessage, m_workerClientWrapper, message), m_taskMode);
+}
+
+static void workerContextDidClose(ScriptExecutionContext* context, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, unsigned long unhandledBufferedAmount)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ workerClientWrapper->didClose(unhandledBufferedAmount);
+}
+
+void WorkerThreadableWebSocketChannel::Peer::didClose(unsigned long unhandledBufferedAmount)
+{
+ ASSERT(isMainThread());
+ m_mainWebSocketChannel = 0;
+ m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&workerContextDidClose, m_workerClientWrapper, unhandledBufferedAmount), m_taskMode);
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::setWebSocketChannel(ScriptExecutionContext* context, Bridge* thisPtr, Peer* peer, RefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper)
+{
+ ASSERT_UNUSED(context, context->isWorkerContext());
+ thisPtr->m_peer = peer;
+ workerClientWrapper->setSyncMethodDone();
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::mainThreadCreateWebSocketChannel(ScriptExecutionContext* context, Bridge* thisPtr, RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, const String& taskMode, const KURL& url, const String& protocol)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+
+ Peer* peer = Peer::create(clientWrapper, thisPtr->m_loaderProxy, context, taskMode, url, protocol);
+ thisPtr->m_loaderProxy.postTaskForModeToWorkerContext(createCallbackTask(&Bridge::setWebSocketChannel, thisPtr, peer, clientWrapper), taskMode);
+}
+
+WorkerThreadableWebSocketChannel::Bridge::Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, PassRefPtr<WorkerContext> workerContext, const String& taskMode, const KURL& url, const String& protocol)
+ : m_workerClientWrapper(workerClientWrapper)
+ , m_workerContext(workerContext)
+ , m_loaderProxy(m_workerContext->thread()->workerLoaderProxy())
+ , m_taskMode(taskMode)
+ , m_peer(0)
+{
+ ASSERT(m_workerClientWrapper.get());
+ setMethodNotCompleted();
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&Bridge::mainThreadCreateWebSocketChannel, this, m_workerClientWrapper, m_taskMode, url, protocol));
+ waitForMethodCompletion();
+ ASSERT(m_peer);
+}
+
+WorkerThreadableWebSocketChannel::Bridge::~Bridge()
+{
+ disconnect();
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadConnect(ScriptExecutionContext* context, Peer* peer)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ peer->connect();
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::connect()
+{
+ ASSERT(m_workerClientWrapper);
+ ASSERT(m_peer);
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadConnect, m_peer));
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadSend(ScriptExecutionContext* context, Peer* peer, const String& message)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ peer->send(message);
+}
+
+bool WorkerThreadableWebSocketChannel::Bridge::send(const String& message)
+{
+ if (!m_workerClientWrapper)
+ return false;
+ ASSERT(m_peer);
+ setMethodNotCompleted();
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSend, m_peer, message));
+ RefPtr<Bridge> protect(this);
+ waitForMethodCompletion();
+ ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
+ return clientWrapper && clientWrapper->sent();
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadBufferedAmount(ScriptExecutionContext* context, Peer* peer)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ peer->bufferedAmount();
+}
+
+unsigned long WorkerThreadableWebSocketChannel::Bridge::bufferedAmount()
+{
+ if (!m_workerClientWrapper)
+ return 0;
+ ASSERT(m_peer);
+ setMethodNotCompleted();
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadBufferedAmount, m_peer));
+ RefPtr<Bridge> protect(this);
+ waitForMethodCompletion();
+ ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
+ if (clientWrapper)
+ return clientWrapper->bufferedAmount();
+ return 0;
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadClose(ScriptExecutionContext* context, Peer* peer)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ peer->close();
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::close()
+{
+ ASSERT(m_peer);
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadClose, m_peer));
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadDestroy(ScriptExecutionContext* context, Peer* peer)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ delete peer;
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::disconnect()
+{
+ clearClientWrapper();
+ if (m_peer) {
+ Peer* peer = m_peer;
+ m_peer = 0;
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&mainThreadDestroy, peer));
+ }
+ m_workerContext = 0;
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadSuspend(ScriptExecutionContext* context, Peer* peer)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ peer->suspend();
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::suspend()
+{
+ ASSERT(m_peer);
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadSuspend, m_peer));
+}
+
+void WorkerThreadableWebSocketChannel::mainThreadResume(ScriptExecutionContext* context, Peer* peer)
+{
+ ASSERT(isMainThread());
+ ASSERT_UNUSED(context, context->isDocument());
+ ASSERT(peer);
+
+ peer->resume();
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::resume()
+{
+ ASSERT(m_peer);
+ m_loaderProxy.postTaskToLoader(createCallbackTask(&WorkerThreadableWebSocketChannel::mainThreadResume, m_peer));
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::clearClientWrapper()
+{
+ m_workerClientWrapper->clearClient();
+}
+
+void WorkerThreadableWebSocketChannel::Bridge::setMethodNotCompleted()
+{
+ ASSERT(m_workerClientWrapper);
+ m_workerClientWrapper->clearSyncMethodDone();
+}
+
+// Caller of this function should hold a reference to the bridge, because this function may call WebSocket::didClose() in the end,
+// which causes the bridge to get disconnected from the WebSocket and deleted if there is no other reference.
+void WorkerThreadableWebSocketChannel::Bridge::waitForMethodCompletion()
+{
+ if (!m_workerContext)
+ return;
+ WorkerRunLoop& runLoop = m_workerContext->thread()->runLoop();
+ MessageQueueWaitResult result = MessageQueueMessageReceived;
+ ThreadableWebSocketChannelClientWrapper* clientWrapper = m_workerClientWrapper.get();
+ while (m_workerContext && clientWrapper && !clientWrapper->syncMethodDone() && result != MessageQueueTerminated) {
+ result = runLoop.runInMode(m_workerContext.get(), m_taskMode); // May cause this bridge to get disconnected, which makes m_workerContext become null.
+ clientWrapper = m_workerClientWrapper.get();
+ }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h
new file mode 100644
index 0000000..366e4fa
--- /dev/null
+++ b/Source/WebCore/websockets/WorkerThreadableWebSocketChannel.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2009, 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WorkerThreadableWebSocketChannel_h
+#define WorkerThreadableWebSocketChannel_h
+
+#if ENABLE(WEB_SOCKETS) && ENABLE(WORKERS)
+
+#include "PlatformString.h"
+#include "ThreadableWebSocketChannel.h"
+#include "WebSocketChannelClient.h"
+
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+#include <wtf/Threading.h>
+
+namespace WebCore {
+
+class KURL;
+class ScriptExecutionContext;
+class ThreadableWebSocketChannelClientWrapper;
+class WorkerContext;
+class WorkerLoaderProxy;
+class WorkerRunLoop;
+
+class WorkerThreadableWebSocketChannel : public RefCounted<WorkerThreadableWebSocketChannel>, public ThreadableWebSocketChannel {
+public:
+ static PassRefPtr<ThreadableWebSocketChannel> create(WorkerContext* workerContext, WebSocketChannelClient* client, const String& taskMode, const KURL& url, const String& protocol)
+ {
+ return adoptRef(new WorkerThreadableWebSocketChannel(workerContext, client, taskMode, url, protocol));
+ }
+ virtual ~WorkerThreadableWebSocketChannel();
+
+ virtual void connect();
+ virtual bool send(const String& message);
+ virtual unsigned long bufferedAmount() const;
+ virtual void close();
+ virtual void disconnect(); // Will suppress didClose().
+ virtual void suspend();
+ virtual void resume();
+
+ using RefCounted<WorkerThreadableWebSocketChannel>::ref;
+ using RefCounted<WorkerThreadableWebSocketChannel>::deref;
+
+protected:
+ virtual void refThreadableWebSocketChannel() { ref(); }
+ virtual void derefThreadableWebSocketChannel() { deref(); }
+
+private:
+ // Generated by the bridge. The Peer and its bridge should have identical
+ // lifetimes.
+ class Peer : public WebSocketChannelClient, public Noncopyable {
+ public:
+ static Peer* create(RefPtr<ThreadableWebSocketChannelClientWrapper> clientWrapper, WorkerLoaderProxy& loaderProxy, ScriptExecutionContext* context, const String& taskMode, const KURL& url, const String& protocol)
+ {
+ return new Peer(clientWrapper, loaderProxy, context, taskMode, url, protocol);
+ }
+ ~Peer();
+
+ void connect();
+ void send(const String& message);
+ void bufferedAmount();
+ void close();
+ void disconnect();
+ void suspend();
+ void resume();
+
+ virtual void didConnect();
+ virtual void didReceiveMessage(const String& message);
+ virtual void didClose(unsigned long unhandledBufferedAmount);
+
+ private:
+ Peer(RefPtr<ThreadableWebSocketChannelClientWrapper>, WorkerLoaderProxy&, ScriptExecutionContext*, const String& taskMode, const KURL&, const String& protocol);
+
+ RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
+ WorkerLoaderProxy& m_loaderProxy;
+ RefPtr<ThreadableWebSocketChannel> m_mainWebSocketChannel;
+ String m_taskMode;
+ };
+
+ // Bridge for Peer. Running on the worker thread.
+ class Bridge : public RefCounted<Bridge> {
+ public:
+ static PassRefPtr<Bridge> create(PassRefPtr<ThreadableWebSocketChannelClientWrapper> workerClientWrapper, PassRefPtr<WorkerContext> workerContext, const String& taskMode, const KURL& url, const String& protocol)
+ {
+ return adoptRef(new Bridge(workerClientWrapper, workerContext, taskMode, url, protocol));
+ }
+ ~Bridge();
+ void connect();
+ bool send(const String& message);
+ unsigned long bufferedAmount();
+ void close();
+ void disconnect();
+ void suspend();
+ void resume();
+
+ using RefCounted<Bridge>::ref;
+ using RefCounted<Bridge>::deref;
+
+ private:
+ Bridge(PassRefPtr<ThreadableWebSocketChannelClientWrapper>, PassRefPtr<WorkerContext>, const String& taskMode, const KURL&, const String& protocol);
+
+ static void setWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, Peer*, RefPtr<ThreadableWebSocketChannelClientWrapper>);
+
+ // Executed on the main thread to create a Peer for this bridge.
+ static void mainThreadCreateWebSocketChannel(ScriptExecutionContext*, Bridge* thisPtr, RefPtr<ThreadableWebSocketChannelClientWrapper>, const String& taskMode, const KURL&, const String& protocol);
+
+ // Executed on the worker context's thread.
+ void clearClientWrapper();
+
+ void setMethodNotCompleted();
+ void waitForMethodCompletion();
+
+ RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
+ RefPtr<WorkerContext> m_workerContext;
+ WorkerLoaderProxy& m_loaderProxy;
+ String m_taskMode;
+ Peer* m_peer;
+ };
+
+ WorkerThreadableWebSocketChannel(WorkerContext*, WebSocketChannelClient*, const String& taskMode, const KURL&, const String& protocol);
+
+ static void mainThreadConnect(ScriptExecutionContext*, Peer*);
+ static void mainThreadSend(ScriptExecutionContext*, Peer*, const String& message);
+ static void mainThreadBufferedAmount(ScriptExecutionContext*, Peer*);
+ static void mainThreadClose(ScriptExecutionContext*, Peer*);
+ static void mainThreadDestroy(ScriptExecutionContext*, Peer*);
+ static void mainThreadSuspend(ScriptExecutionContext*, Peer*);
+ static void mainThreadResume(ScriptExecutionContext*, Peer*);
+
+ RefPtr<WorkerContext> m_workerContext;
+ RefPtr<ThreadableWebSocketChannelClientWrapper> m_workerClientWrapper;
+ RefPtr<Bridge> m_bridge;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WorkerThreadableWebSocketChannel_h