summaryrefslogtreecommitdiffstats
path: root/WebCore/websockets
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/websockets')
-rw-r--r--WebCore/websockets/WebSocket.cpp115
-rw-r--r--WebCore/websockets/WebSocket.h117
-rw-r--r--WebCore/websockets/WebSocket.idl6
-rw-r--r--WebCore/websockets/WebSocketChannel.cpp246
-rw-r--r--WebCore/websockets/WebSocketChannel.h86
-rw-r--r--WebCore/websockets/WebSocketChannelClient.h53
-rw-r--r--WebCore/websockets/WebSocketHandshake.cpp462
-rw-r--r--WebCore/websockets/WebSocketHandshake.h113
8 files changed, 1099 insertions, 99 deletions
diff --git a/WebCore/websockets/WebSocket.cpp b/WebCore/websockets/WebSocket.cpp
index 999c1ff..16211a7 100644
--- a/WebCore/websockets/WebSocket.cpp
+++ b/WebCore/websockets/WebSocket.cpp
@@ -34,13 +34,58 @@
#include "WebSocket.h"
+#include "CString.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 "SecurityOrigin.h"
+#include "WebSocketChannel.h"
#include <wtf/StdLibExtras.h>
namespace WebCore {
+class ProcessWebSocketEventTask : public ScriptExecutionContext::Task {
+public:
+ typedef void (WebSocket::*Method)(Event*);
+ static PassRefPtr<ProcessWebSocketEventTask> create(PassRefPtr<WebSocket> webSocket, PassRefPtr<Event> event)
+ {
+ return adoptRef(new ProcessWebSocketEventTask(webSocket, event));
+ }
+ virtual void performTask(ScriptExecutionContext*)
+ {
+ ExceptionCode ec = 0;
+ m_webSocket->dispatchEvent(m_event.get(), ec);
+ ASSERT(!ec);
+ }
+
+ private:
+ ProcessWebSocketEventTask(PassRefPtr<WebSocket> webSocket, PassRefPtr<Event> event)
+ : m_webSocket(webSocket)
+ , m_event(event) { }
+
+ RefPtr<WebSocket> m_webSocket;
+ RefPtr<Event> m_event;
+};
+
+static bool isValidProtocolString(const WebCore::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] < 0x21 || characters[i] > 0x7E)
+ return false;
+ }
+ return true;
+}
+
WebSocket::WebSocket(ScriptExecutionContext* context)
: ActiveDOMObject(context, this)
, m_state(CONNECTING)
@@ -59,39 +104,49 @@ void WebSocket::connect(const KURL& url, ExceptionCode& 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.protocolIs("ws") && !m_url.protocolIs("wss")) {
+ LOG_ERROR("Error: wrong url for WebSocket %s", url.string().utf8().data());
m_state = CLOSED;
ec = SYNTAX_ERR;
return;
}
- if (!m_protocol.isNull() && m_protocol.isEmpty()) {
+ if (!isValidProtocolString(m_protocol)) {
+ LOG_ERROR("Error: wrong protocol for WebSocket %s", m_protocol.utf8().data());
m_state = CLOSED;
ec = SYNTAX_ERR;
return;
}
- // FIXME: Check protocol is valid form.
- // FIXME: Connect WebSocketChannel.
+ // FIXME: if m_url.port() is blocking port, raise SECURITY_ERR.
+
+ m_channel = WebSocketChannel::create(scriptExecutionContext(), this, m_url, m_protocol);
+ m_channel->connect();
}
-bool WebSocket::send(const String&, ExceptionCode& ec)
+bool WebSocket::send(const String& message, ExceptionCode& ec)
{
- if (m_state != OPEN) {
+ LOG(Network, "WebSocket %p send %s", this, message.utf8().data());
+ if (m_state == CONNECTING) {
ec = INVALID_STATE_ERR;
return false;
}
- // FIXME: send message on WebSocketChannel.
- return false;
+ // No exception is raised if the connection was once established but has subsequently been closed.
+ if (m_state == CLOSED)
+ return false;
+ // FIXME: check message is valid utf8.
+ return m_channel->send(message);
}
void WebSocket::close()
{
+ LOG(Network, "WebSocket %p close", this);
if (m_state == CLOSED)
return;
m_state = CLOSED;
- // FIXME: close WebSocketChannel.
+ m_channel->close();
}
const KURL& WebSocket::url() const
@@ -106,7 +161,8 @@ WebSocket::State WebSocket::readyState() const
unsigned long WebSocket::bufferedAmount() const
{
- // FIXME: ask platform code to get buffered amount to be sent.
+ if (m_state == OPEN)
+ return m_channel->bufferedAmount();
return 0;
}
@@ -115,58 +171,43 @@ ScriptExecutionContext* WebSocket::scriptExecutionContext() const
return ActiveDOMObject::scriptExecutionContext();
}
-void WebSocket::addEventListener(const AtomicString&, PassRefPtr<EventListener>, bool)
-{
- // FIXME: implement this.
-}
-
-void WebSocket::removeEventListener(const AtomicString&, EventListener*, bool)
-{
- // FIXME: implement this.
-}
-
-bool WebSocket::dispatchEvent(PassRefPtr<Event>, ExceptionCode&)
-{
- // FIXME: implement this.
- return false;
-}
-
void WebSocket::didConnect()
{
+ LOG(Network, "WebSocket %p didConnect", this);
if (m_state != CONNECTING) {
didClose();
return;
}
m_state = OPEN;
- dispatchOpenEvent();
+ scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, 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;
- dispatchMessageEvent(msg);
+ RefPtr<MessageEvent> evt = MessageEvent::create();
+ // FIXME: origin, lastEventId, source, messagePort.
+ evt->initMessageEvent(eventNames().messageEvent, false, false, SerializedScriptValue::create(msg), "", "", 0, 0);
+ scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, evt));
}
void WebSocket::didClose()
{
+ LOG(Network, "WebSocket %p didClose", this);
m_state = CLOSED;
- dispatchCloseEvent();
-}
-
-void WebSocket::dispatchOpenEvent()
-{
- // FIXME: implement this.
+ scriptExecutionContext()->postTask(ProcessWebSocketEventTask::create(this, Event::create(eventNames().closeEvent, false, false)));
}
-void WebSocket::dispatchMessageEvent(const String&)
+EventTargetData* WebSocket::eventTargetData()
{
- // FIXME: implement this.
+ return &m_eventTargetData;
}
-void WebSocket::dispatchCloseEvent()
+EventTargetData* WebSocket::ensureEventTargetData()
{
- // FIXME: implement this.
+ return &m_eventTargetData;
}
} // namespace WebCore
diff --git a/WebCore/websockets/WebSocket.h b/WebCore/websockets/WebSocket.h
index 485e2cc..c5b7ee7 100644
--- a/WebCore/websockets/WebSocket.h
+++ b/WebCore/websockets/WebSocket.h
@@ -31,93 +31,92 @@
#ifndef WebSocket_h
#define WebSocket_h
+#if ENABLE(WEB_SOCKETS)
+
#include "ActiveDOMObject.h"
#include "AtomicStringHash.h"
#include "EventListener.h"
+#include "EventNames.h"
#include "EventTarget.h"
#include "KURL.h"
+#include "WebSocketChannelClient.h"
#include <wtf/OwnPtr.h>
#include <wtf/RefCounted.h>
namespace WebCore {
-class String;
-
-class WebSocket : public RefCounted<WebSocket>, public EventTarget, public ActiveDOMObject {
-public:
- static PassRefPtr<WebSocket> create(ScriptExecutionContext* context) { return adoptRef(new WebSocket(context)); }
- virtual ~WebSocket();
-
- enum State {
- CONNECTING = 0,
- OPEN = 1,
- CLOSED = 2
- };
+ class String;
+ class WebSocketChannel;
- void connect(const KURL& url, ExceptionCode&);
- void connect(const KURL& url, const String& protocol, ExceptionCode&);
+ class WebSocket : public RefCounted<WebSocket>, public EventTarget, public ActiveDOMObject, public WebSocketChannelClient {
+ public:
+ static PassRefPtr<WebSocket> create(ScriptExecutionContext* context) { return adoptRef(new WebSocket(context)); }
+ virtual ~WebSocket();
- bool send(const String& message, ExceptionCode&);
+ enum State {
+ CONNECTING = 0,
+ OPEN = 1,
+ CLOSED = 2
+ };
- void close();
+ void connect(const KURL&, ExceptionCode&);
+ void connect(const KURL&, const String& protocol, ExceptionCode&);
- const KURL& url() const;
- State readyState() const;
- unsigned long bufferedAmount() const;
+ bool send(const String& message, ExceptionCode&);
- void setOnopen(PassRefPtr<EventListener> eventListener) { m_onopen = eventListener; }
- EventListener* onopen() const { return m_onopen.get(); }
- void setOnmessage(PassRefPtr<EventListener> eventListener) { m_onmessage = eventListener; }
- EventListener* onmessage() const { return m_onmessage.get(); }
- void setOnclose(PassRefPtr<EventListener> eventListener) { m_onclose = eventListener; }
- EventListener* onclose() const { return m_onclose.get(); }
+ void close();
- // EventTarget
- virtual WebSocket* toWebSocket() { return this; }
+ const KURL& url() const;
+ State readyState() const;
+ unsigned long bufferedAmount() const;
- virtual ScriptExecutionContext* scriptExecutionContext() const;
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(open);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(message);
+ DEFINE_ATTRIBUTE_EVENT_LISTENER(close);
- virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture);
- virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture);
- virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&);
+ // EventTarget
+ virtual WebSocket* toWebSocket() { return this; }
- // ActiveDOMObject
- // virtual bool hasPendingActivity() const;
- // virtual void contextDestroyed();
- // virtual bool canSuspend() const;
- // virtual void suspend();
- // virtual void resume();
- // virtual void stop();
+ virtual ScriptExecutionContext* scriptExecutionContext() const;
- using RefCounted<WebSocket>::ref;
- using RefCounted<WebSocket>::deref;
+ // ActiveDOMObject
+ // virtual bool hasPendingActivity() const;
+ // virtual void contextDestroyed();
+ // virtual bool canSuspend() const;
+ // virtual void suspend();
+ // virtual void resume();
+ // virtual void stop();
-private:
- WebSocket(ScriptExecutionContext*);
+ using RefCounted<WebSocket>::ref;
+ using RefCounted<WebSocket>::deref;
- virtual void refEventTarget() { ref(); }
- virtual void derefEventTarget() { deref(); }
+ // WebSocketChannelClient
+ virtual void didConnect();
+ virtual void didReceiveMessage(const String& msg);
+ virtual void didClose();
- // WebSocketChannelClient
- void didConnect();
- void didReceiveMessage(const String& msg);
- void didClose();
+ private:
+ WebSocket(ScriptExecutionContext*);
- void dispatchOpenEvent();
- void dispatchMessageEvent(const String& msg);
- void dispatchCloseEvent();
+ virtual void refEventTarget() { ref(); }
+ virtual void derefEventTarget() { deref(); }
+ virtual EventTargetData* eventTargetData();
+ virtual EventTargetData* ensureEventTargetData();
- // FIXME: add WebSocketChannel.
+ void dispatchOpenEvent(Event*);
+ void dispatchMessageEvent(Event*);
+ void dispatchCloseEvent(Event*);
- RefPtr<EventListener> m_onopen;
- RefPtr<EventListener> m_onmessage;
- RefPtr<EventListener> m_onclose;
+ RefPtr<WebSocketChannel> m_channel;
- State m_state;
- KURL m_url;
- String m_protocol;
-};
+ State m_state;
+ KURL m_url;
+ String m_protocol;
+ EventTargetData m_eventTargetData;
+ };
} // namespace WebCore
+#endif // ENABLE(WEB_SOCKETS)
+
#endif // WebSocket_h
diff --git a/WebCore/websockets/WebSocket.idl b/WebCore/websockets/WebSocket.idl
index cdb916f..04606fe 100644
--- a/WebCore/websockets/WebSocket.idl
+++ b/WebCore/websockets/WebSocket.idl
@@ -31,9 +31,9 @@
module websockets {
interface [
- CustomMarkFunction,
- NoStaticTables,
- Conditional=WEB_SOCKETS
+ Conditional=WEB_SOCKETS,
+ EventTarget,
+ NoStaticTables
] WebSocket {
readonly attribute DOMString URL;
diff --git a/WebCore/websockets/WebSocketChannel.cpp b/WebCore/websockets/WebSocketChannel.cpp
new file mode 100644
index 0000000..145cd34
--- /dev/null
+++ b/WebCore/websockets/WebSocketChannel.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "CString.h"
+#include "CookieJar.h"
+#include "Document.h"
+#include "Logging.h"
+#include "PlatformString.h"
+#include "ScriptExecutionContext.h"
+#include "SocketStreamError.h"
+#include "SocketStreamHandle.h"
+#include "StringHash.h"
+#include "WebSocketChannelClient.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_unhandledBufferSize(0)
+{
+}
+
+WebSocketChannel::~WebSocketChannel()
+{
+ fastFree(m_buffer);
+}
+
+void WebSocketChannel::connect()
+{
+ LOG(Network, "WebSocketChannel %p connect", this);
+ ASSERT(!m_handle.get());
+ m_handshake.reset();
+ m_handle = SocketStreamHandle::create(m_handshake.url(), this);
+}
+
+bool WebSocketChannel::send(const String& msg)
+{
+ LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data());
+ Vector<char> buf;
+ buf.append('\0'); // frame type
+ buf.append(msg.utf8().data(), msg.utf8().length());
+ buf.append('\xff'); // frame end
+ if (!m_handle.get()) {
+ m_unhandledBufferSize += buf.size();
+ return false;
+ }
+ return m_handle->send(buf.data(), buf.size());
+}
+
+unsigned long WebSocketChannel::bufferedAmount() const
+{
+ LOG(Network, "WebSocketChannel %p bufferedAmount", this);
+ if (!m_handle.get())
+ return m_unhandledBufferSize;
+ return m_handle->bufferedAmount();
+}
+
+void WebSocketChannel::close()
+{
+ LOG(Network, "WebSocketChannel %p close", this);
+ if (m_handle.get())
+ m_handle->close(); // will call didClose()
+}
+
+void WebSocketChannel::willOpenStream(SocketStreamHandle*, const KURL&)
+{
+}
+
+void WebSocketChannel::willSendData(SocketStreamHandle*, const char*, int)
+{
+}
+
+void WebSocketChannel::didOpen(SocketStreamHandle* handle)
+{
+ LOG(Network, "WebSocketChannel %p didOpen", this);
+ ASSERT(handle == m_handle.get());
+ const CString& handshakeMessage = m_handshake.clientHandshakeMessage();
+ if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) {
+ LOG(Network, "Error in sending handshake message.");
+ handle->close();
+ }
+}
+
+void WebSocketChannel::didClose(SocketStreamHandle* handle)
+{
+ LOG(Network, "WebSocketChannel %p didClose", this);
+ ASSERT(handle == m_handle.get() || !m_handle.get());
+ if (!m_handle.get())
+ return;
+ m_unhandledBufferSize = handle->bufferedAmount();
+ WebSocketChannelClient* client = m_client;
+ m_client = 0;
+ m_handle = 0;
+ client->didClose();
+}
+
+void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* data, int len)
+{
+ LOG(Network, "WebSocketChannel %p didReceiveData %d", this, len);
+ ASSERT(handle == m_handle.get());
+ if (!appendToBuffer(data, len)) {
+ handle->close();
+ return;
+ }
+ if (m_handshake.mode() != WebSocketHandshake::Connected) {
+ int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize);
+ if (headerLength <= 0)
+ return;
+ switch (m_handshake.mode()) {
+ case WebSocketHandshake::Connected:
+ if (!m_handshake.serverSetCookie().isEmpty()) {
+ if (m_context->isDocument()) {
+ Document* document = static_cast<Document*>(m_context);
+ if (cookiesEnabled(document))
+ document->setCookie(m_handshake.serverSetCookie());
+ }
+ }
+ // FIXME: handle set-cookie2.
+ LOG(Network, "WebSocketChannel %p connected", this);
+ m_client->didConnect();
+ break;
+ default:
+ LOG(Network, "WebSocketChannel %p connection failed", this);
+ handle->close();
+ return;
+ }
+ skipBuffer(headerLength);
+ if (!m_buffer)
+ return;
+ LOG(Network, "remaining in read buf %ul", m_bufferSize);
+ }
+
+ const char* nextFrame = m_buffer;
+ const char* p = m_buffer;
+ const char* end = p + m_bufferSize;
+ while (p < end) {
+ unsigned char frameByte = static_cast<unsigned char>(*p++);
+ if ((frameByte & 0x80) == 0x80) {
+ int length = 0;
+ while (p < end && (*p & 0x80) == 0x80) {
+ if (length > std::numeric_limits<int>::max() / 128) {
+ LOG(Network, "frame length overflow %d", length);
+ handle->close();
+ return;
+ }
+ length = length * 128 + *p & 0x7f;
+ ++p;
+ }
+ if (p + length < end) {
+ p += length;
+ nextFrame = p;
+ }
+ } else {
+ const char* msgStart = p;
+ while (p < end && *p != '\xff')
+ ++p;
+ if (p < end && *p == '\xff') {
+ if (frameByte == 0x00)
+ m_client->didReceiveMessage(String::fromUTF8(msgStart, p - msgStart));
+ ++p;
+ nextFrame = p;
+ }
+ }
+ }
+ skipBuffer(nextFrame - m_buffer);
+}
+
+void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamError&)
+{
+ LOG(Network, "WebSocketChannel %p didFail", this);
+ ASSERT(handle == m_handle.get() || !m_handle.get());
+ handle->close();
+}
+
+bool WebSocketChannel::appendToBuffer(const char* data, int len)
+{
+ char* newBuffer = 0;
+ if (tryFastMalloc(m_bufferSize + len).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 += len;
+ return true;
+ }
+ LOG(Network, "Too long WebSocket frame %d", m_bufferSize + len);
+ return false;
+}
+
+void WebSocketChannel::skipBuffer(int 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);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/WebCore/websockets/WebSocketChannel.h b/WebCore/websockets/WebSocketChannel.h
new file mode 100644
index 0000000..75f41f6
--- /dev/null
+++ b/WebCore/websockets/WebSocketChannel.h
@@ -0,0 +1,86 @@
+/*
+ * 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 "WebSocketHandshake.h"
+#include <wtf/RefCounted.h>
+
+namespace WebCore {
+
+ class ScriptExecutionContext;
+ class String;
+ class SocketStreamHandle;
+ class SocketStreamError;
+ class WebSocketChannelClient;
+
+ class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient {
+ public:
+ static PassRefPtr<WebSocketChannel> create(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) { return new WebSocketChannel(context, client, url, protocol); }
+ virtual ~WebSocketChannel();
+
+ virtual void connect();
+
+ virtual bool send(const String& msg);
+ virtual unsigned long bufferedAmount() const;
+
+ virtual void close();
+
+ virtual void willOpenStream(SocketStreamHandle*, const KURL&);
+ virtual void willSendData(SocketStreamHandle*, const char*, int);
+ virtual void didOpen(SocketStreamHandle*);
+ virtual void didClose(SocketStreamHandle*);
+ virtual void didReceiveData(SocketStreamHandle*, const char*, int);
+ virtual void didFail(SocketStreamHandle*, const SocketStreamError&);
+
+ private:
+ WebSocketChannel(ScriptExecutionContext*, WebSocketChannelClient*, const KURL&, const String&);
+
+ bool appendToBuffer(const char* data, int len);
+ void skipBuffer(int len);
+
+ ScriptExecutionContext* m_context;
+ WebSocketChannelClient* m_client;
+ WebSocketHandshake m_handshake;
+ RefPtr<SocketStreamHandle> m_handle;
+ char* m_buffer;
+ int m_bufferSize;
+ unsigned long m_unhandledBufferSize;
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketChannel_h
diff --git a/WebCore/websockets/WebSocketChannelClient.h b/WebCore/websockets/WebSocketChannelClient.h
new file mode 100644
index 0000000..463cada
--- /dev/null
+++ b/WebCore/websockets/WebSocketChannelClient.h
@@ -0,0 +1,53 @@
+/*
+ * 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 didClose() { }
+
+ protected:
+ WebSocketChannelClient() { }
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketChannelClient_h
diff --git a/WebCore/websockets/WebSocketHandshake.cpp b/WebCore/websockets/WebSocketHandshake.cpp
new file mode 100644
index 0000000..691fa1c
--- /dev/null
+++ b/WebCore/websockets/WebSocketHandshake.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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 "AtomicString.h"
+#include "CString.h"
+#include "CookieJar.h"
+#include "Document.h"
+#include "HTTPHeaderMap.h"
+#include "KURL.h"
+#include "Logging.h"
+#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
+#include "StringBuilder.h"
+#include <wtf/StringExtras.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+const char webSocketServerHandshakeHeader[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n";
+const char webSocketUpgradeHeader[] = "Upgrade: WebSocket\r\n";
+const char webSocketConnectionHeader[] = "Connection: Upgrade\r\n";
+
+static String extractResponseCode(const char* header, int len)
+{
+ const char* space1 = 0;
+ const char* space2 = 0;
+ const char* p;
+ for (p = header; p - header < len; p++) {
+ if (*p == ' ') {
+ if (!space1)
+ space1 = p;
+ else if (!space2)
+ space2 = p;
+ } else if (*p == '\n')
+ break;
+ }
+ if (p - header == len)
+ return String();
+ if (!space1 || !space2)
+ return "";
+ return String(space1 + 1, space2 - space1 - 1);
+}
+
+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)
+{
+}
+
+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;
+}
+
+void WebSocketHandshake::setSecure(bool secure)
+{
+ m_secure = 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(m_url.host().lower());
+ if (m_url.port()) {
+ if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) {
+ builder.append(":");
+ builder.append(String::number(m_url.port()));
+ }
+ }
+ builder.append(m_url.path());
+ return builder.toString();
+}
+
+CString WebSocketHandshake::clientHandshakeMessage() const
+{
+ StringBuilder builder;
+
+ builder.append("GET ");
+ builder.append(m_url.path());
+ if (!m_url.query().isEmpty()) {
+ builder.append("?");
+ builder.append(m_url.query());
+ }
+ builder.append(" HTTP/1.1\r\n");
+ builder.append("Upgrade: WebSocket\r\n");
+ builder.append("Connection: Upgrade\r\n");
+ builder.append("Host: ");
+ builder.append(m_url.host().lower());
+ if (m_url.port()) {
+ if ((!m_secure && m_url.port() != 80) || (m_secure && m_url.port() != 443)) {
+ builder.append(":");
+ builder.append(String::number(m_url.port()));
+ }
+ }
+ builder.append("\r\n");
+ builder.append("Origin: ");
+ builder.append(clientOrigin());
+ builder.append("\r\n");
+ if (!m_clientProtocol.isEmpty()) {
+ builder.append("WebSocket-Protocol: ");
+ builder.append(m_clientProtocol);
+ builder.append("\r\n");
+ }
+ KURL url = httpURLForAuthenticationAndCookies();
+ // FIXME: set authentication information or cookies for url.
+ // Set "Authorization: <credentials>" if authentication information exists for url.
+ if (m_context->isDocument()) {
+ Document* document = static_cast<Document*>(m_context);
+ String cookie = cookies(document, url);
+ if (!cookie.isEmpty()) {
+ builder.append("Cookie: ");
+ builder.append(cookie);
+ builder.append("\r\n");
+ }
+ // Set "Cookie2: <cookie>" if cookies 2 exists for url?
+ }
+ builder.append("\r\n");
+ return builder.toString().utf8();
+}
+
+void WebSocketHandshake::reset()
+{
+ m_mode = Incomplete;
+
+ m_wsOrigin = String();
+ m_wsLocation = String();
+ m_wsProtocol = String();
+ m_setCookie = String();
+ m_setCookie2 = String();
+}
+
+int WebSocketHandshake::readServerHandshake(const char* header, size_t len)
+{
+ m_mode = Incomplete;
+ if (len < sizeof(webSocketServerHandshakeHeader) - 1) {
+ // Just hasn't been received fully yet.
+ return -1;
+ }
+ if (!memcmp(header, webSocketServerHandshakeHeader, sizeof(webSocketServerHandshakeHeader) - 1))
+ m_mode = Normal;
+ else {
+ const String& code = extractResponseCode(header, len);
+ if (code.isNull()) {
+ LOG(Network, "short server handshake: %s", header);
+ return -1;
+ }
+ if (code.isEmpty()) {
+ LOG(Network, "no response code found: %s", header);
+ return len;
+ }
+ LOG(Network, "response code: %s", code.utf8().data());
+ if (code == "401") {
+ LOG(Network, "Authentication required");
+ return len;
+ } else {
+ LOG(Network, "Mismatch server handshake: %s", header);
+ return len;
+ }
+ }
+ const char* p = header + sizeof(webSocketServerHandshakeHeader) - 1;
+ const char* end = header + len + 1;
+
+ if (m_mode == Normal) {
+ size_t headerSize = end - p;
+ if (headerSize < sizeof(webSocketUpgradeHeader) - 1)
+ return 0;
+ if (memcmp(p, webSocketUpgradeHeader, sizeof(webSocketUpgradeHeader) - 1)) {
+ LOG(Network, "Bad upgrade header: %s", p);
+ return p - header + sizeof(webSocketUpgradeHeader) - 1;
+ }
+ p += sizeof(webSocketUpgradeHeader) - 1;
+
+ headerSize = end - p;
+ if (headerSize < sizeof(webSocketConnectionHeader) - 1)
+ return -1;
+ if (memcmp(p, webSocketConnectionHeader, sizeof(webSocketConnectionHeader) - 1)) {
+ LOG(Network, "Bad connection header: %s", p);
+ return p - header + sizeof(webSocketConnectionHeader) - 1;
+ }
+ p += sizeof(webSocketConnectionHeader) - 1;
+ }
+
+ if (!strnstr(p, "\r\n\r\n", end - p)) {
+ // Just hasn't been received fully yet.
+ return -1;
+ }
+ HTTPHeaderMap headers;
+ p = readHTTPHeaders(p, end, &headers);
+ if (!p) {
+ LOG(Network, "readHTTPHeaders failed");
+ m_mode = Failed;
+ return len;
+ }
+ if (!processHeaders(headers)) {
+ LOG(Network, "header process failed");
+ m_mode = Failed;
+ return p - header;
+ }
+ switch (m_mode) {
+ case Normal:
+ checkResponseHeaders();
+ break;
+ default:
+ m_mode = Failed;
+ break;
+ }
+ return p - header;
+}
+
+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;
+}
+
+KURL WebSocketHandshake::httpURLForAuthenticationAndCookies() const
+{
+ KURL url = m_url.copy();
+ url.setProtocol(m_secure ? "https" : "http");
+ return url;
+}
+
+const char* WebSocketHandshake::readHTTPHeaders(const char* start, const char* end, HTTPHeaderMap* headers)
+{
+ 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;
+ LOG(Network, "CR doesn't follow LF p=%p end=%p", p, end);
+ return 0;
+ }
+ LOG(Network, "Unexpected CR in name");
+ return 0;
+ case '\n':
+ LOG(Network, "Unexpected LF in name");
+ return 0;
+ case ':':
+ break;
+ default:
+ if (*p >= 0x41 && *p <= 0x5a)
+ name.append(*p + 0x20);
+ else
+ 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':
+ LOG(Network, "Unexpected LF in value");
+ return 0;
+ default:
+ value.append(*p);
+ }
+ if (*p == '\r') {
+ ++p;
+ break;
+ }
+ }
+ if (p >= end || *p != '\n') {
+ LOG(Network, "CR doesn't follow LF after value p=%p end=%p", p, end);
+ return 0;
+ }
+ AtomicString nameStr(String::fromUTF8(name.data(), name.size()));
+ String valueStr = String::fromUTF8(value.data(), value.size());
+ LOG(Network, "name=%s value=%s", nameStr.string().utf8().data(), valueStr.utf8().data());
+ headers->add(nameStr, valueStr);
+ }
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+bool WebSocketHandshake::processHeaders(const HTTPHeaderMap& headers)
+{
+ for (HTTPHeaderMap::const_iterator it = headers.begin(); it != headers.end(); ++it) {
+ switch (m_mode) {
+ case Normal:
+ if (it->first == "websocket-origin")
+ m_wsOrigin = it->second;
+ else if (it->first == "websocket-location")
+ m_wsLocation = it->second;
+ else if (it->first == "websocket-protocol")
+ m_wsProtocol = it->second;
+ else if (it->first == "set-cookie")
+ m_setCookie = it->second;
+ else if (it->first == "set-cookie2")
+ m_setCookie2 = it->second;
+ continue;
+ case Incomplete:
+ case Failed:
+ case Connected:
+ ASSERT_NOT_REACHED();
+ }
+ ASSERT_NOT_REACHED();
+ }
+ return true;
+}
+
+void WebSocketHandshake::checkResponseHeaders()
+{
+ ASSERT(m_mode == Normal);
+ m_mode = Failed;
+ if (m_wsOrigin.isNull() || m_wsLocation.isNull())
+ return;
+
+ if (clientOrigin() != m_wsOrigin) {
+ LOG(Network, "Mismatch origin: %s != %s", clientOrigin().utf8().data(), m_wsOrigin.utf8().data());
+ return;
+ }
+ if (clientLocation() != m_wsLocation) {
+ LOG(Network, "Mismatch location: %s != %s", clientLocation().utf8().data(), m_wsLocation.utf8().data());
+ return;
+ }
+ if (!m_clientProtocol.isEmpty() && m_clientProtocol != m_wsProtocol) {
+ LOG(Network, "Mismatch protocol: %s != %s", m_clientProtocol.utf8().data(), m_wsProtocol.utf8().data());
+ return;
+ }
+ m_mode = Connected;
+ return;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
diff --git a/WebCore/websockets/WebSocketHandshake.h b/WebCore/websockets/WebSocketHandshake.h
new file mode 100644
index 0000000..d5dbe68
--- /dev/null
+++ b/WebCore/websockets/WebSocketHandshake.h
@@ -0,0 +1,113 @@
+/*
+ * 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 <wtf/Noncopyable.h>
+
+namespace WebCore {
+
+ class ScriptExecutionContext;
+ class HTTPHeaderMap;
+
+ 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;
+ void setSecure(bool secure);
+
+ String clientOrigin() const;
+ String clientLocation() const;
+
+ CString clientHandshakeMessage() const;
+
+ void reset();
+
+ 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);
+
+ private:
+ KURL httpURLForAuthenticationAndCookies() const;
+
+ // Reads all headers except for the two predefined ones.
+ const char* readHTTPHeaders(const char* start, const char* end, HTTPHeaderMap* headers);
+ bool processHeaders(const HTTPHeaderMap& headers);
+ void 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;
+ };
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_SOCKETS)
+
+#endif // WebSocketHandshake_h