Index: WebCore/websockets/WebSocketChannel.cpp |
diff --git a/WebCore/websockets/WebSocketChannel.cpp b/WebCore/websockets/WebSocketChannel.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..37abd5d056c7a3d614e13949551892c70d12766e |
--- /dev/null |
+++ b/WebCore/websockets/WebSocketChannel.cpp |
@@ -0,0 +1,219 @@ |
+/* |
+ * 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" |
+#include "WebSocketChannel.h" |
+ |
+#include "CString.h" |
+#include "Logging.h" |
+#include "PlatformString.h" |
+#include "ScriptExecutionContext.h" |
+#include "SharedBuffer.h" |
+#include "SocketStreamHandle.h" |
+#include "StringHash.h" |
+#include "WebSocketChannelClient.h" |
+ |
+#include <wtf/HashMap.h> |
+#include <wtf/Deque.h> |
+ |
+#include "NotImplemented.h" |
+ |
+#undef LOG |
+#define LOG(channel, ...) do { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } while (0); |
+ |
+namespace { |
+ |
+bool CheckWebSocketHandshake(const WebCore::WebSocketRequest &req, const WebCore::WebSocketResponse& resp) |
+{ |
+ if (!resp.isValidHeader()) { |
+ LOG(Network, "Wrong response header"); |
+ return false; |
+ } |
+ if (req.origin() != resp.websocket_origin()) { |
+ LOG(Network, "Mismatch origin: %s != %s", req.origin().utf8().data(), resp.websocket_origin().utf8().data()); |
+ return false; |
+ } |
+ if (req.location() != resp.websocket_location()) { |
+ LOG(Network, "Mismatch location: %s != %s", req.location().utf8().data(), resp.websocket_location().utf8().data()); |
+ return false; |
+ } |
+ if (!req.protocol().isEmpty() && req.protocol() != resp.websocket_protocol()) { |
+ LOG(Network, "Mismatch protocol: %s != %s", req.protocol().utf8().data(), resp.websocket_protocol().utf8().data()); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} // anonymous namespace |
+ |
+namespace WebCore { |
+ |
+WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const WebSocketRequest& request) |
+ : m_context(context) |
+ , m_client(client) |
+ , m_request(request) |
+ , m_handshaked(false) |
+ , m_buffered_amount(0) |
+ , m_buffer(SharedBuffer::create()) |
+{ |
+ // TODO(ukai): additional parameters? |
+} |
+ |
+WebSocketChannel::~WebSocketChannel() |
+{ |
+} |
+ |
+void WebSocketChannel::connect() |
+{ |
+ LOG(Network, "WebSocketChannel %p connect", this); |
+ |
+ // ??? defer connection in SocketStreamHandle |
+ m_handle = SocketStreamHandle::create(m_request.url(), this); |
+} |
+ |
+bool WebSocketChannel::send(const String& msg) |
+{ |
+ LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data()); |
+ static const char frame_type[1] = {'\0'}; |
+ static const char frame_end[1] = {'\xff'}; |
+ RefPtr<SharedBuffer> buf(SharedBuffer::create()); |
+ buf->append(frame_type, sizeof(frame_type)); |
+ buf->append(msg.utf8().data(), msg.utf8().length()); |
+ buf->append(frame_end, sizeof(frame_end)); |
+ m_buffered_amount += buf->size(); |
+ return m_handle->send(buf->data(), buf->size()); |
+} |
+ |
+unsigned long WebSocketChannel::bufferedAmount() const |
+{ |
+ LOG(Network, "WebSocketChannel %p bufferedAmount", this); |
+ return m_buffered_amount; |
+} |
+ |
+void WebSocketChannel::close() |
+{ |
+ LOG(Network, "WebSocketChannel %p close", this); |
+ if (m_handle.get()) { |
+ m_handle->close(); // will call didClose() |
+ } |
+} |
+ |
+void WebSocketChannel::didOpen(SocketStreamHandle* handle) |
+{ |
+ LOG(Network, "WebSocketChannel %p didConnect", this); |
+ ASSERT(handle == m_handle.get() || m_handle.get() == 0); |
+ if (m_handle.get() == 0) |
+ return; |
+ const CString& request_message = m_request.flattenToString(); |
+ m_handle->send(request_message.data(), request_message.length()); |
+} |
+ |
+void WebSocketChannel::didClose(SocketStreamHandle* handle) |
+{ |
+ LOG(Network, "WebSocketChannel %p didClose", this); |
+ ASSERT(handle == m_handle.get() || m_handle.get() == 0); |
+ if (m_handle.get() == 0) |
+ return; |
+ m_handle = NULL; |
+ WebSocketChannelClient* client = m_client; |
+ m_client = NULL; |
+ client->didClose(); |
+} |
+ |
+void WebSocketChannel::didReceivedData(SocketStreamHandle* handle, const char* data, int len) |
+{ |
+ LOG(Network, "WebSocketChannel %p didReceiveData %d", this, len); |
+ ASSERT(handle == m_handle.get() || m_handle.get() == 0); |
+ if (m_handle.get() == 0) |
+ return; |
+ m_buffer->append(data, len); |
+ if (!m_handshaked) { |
+ int header_len = m_response.readHandshakeResponse(m_buffer->data(), m_buffer->size()); |
+ if (header_len <= 0) |
+ return; |
+ m_handshaked = CheckWebSocketHandshake(m_request, m_response); |
+ if (m_handshaked) { |
+ LOG(Network, "WebSocketChannel %p connected", this); |
+ m_client->didConnect(); |
+ } else { |
+ LOG(Network, "WebSocketChannel %p connection failed", this); |
+ didClose(handle); |
+ } |
+ if (header_len == len) |
+ return; |
+ RefPtr<SharedBuffer> buf(SharedBuffer::create()); |
+ buf->append(m_buffer->data() + header_len, m_buffer->size() - header_len); |
+ m_buffer.swap(buf); |
+ } |
+ RefPtr<SharedBuffer> buf(SharedBuffer::create()); |
+ buf.swap(m_buffer); |
+ |
+ const char *next_frame = buf->data(); |
+ const char *p = buf->data(); |
+ const char *end = p + buf->size(); |
+ while (p < end) { |
+ unsigned char frame_byte = static_cast<unsigned char>(*p++); |
+ if ((frame_byte & 0x80) == 0x80) { |
+ // TODO(ukai): overflow check |
+ int length = 0; |
+ while (p < end && (*p & 0x80) == 0x80) { |
+ length = length * 127 + *p & 0x7f; |
+ ++p; |
+ } |
+ if (p + length < end) { |
+ p += length; |
+ next_frame = p; |
+ } |
+ } else { |
+ const char *msg_start = p; |
+ while (p < end && *p != '\xff') { |
+ ++p; |
+ } |
+ if (p < end && *p == '\xff') { |
+ if (frame_byte == 0x00) |
+ m_client->didReceiveMessage(String::fromUTF8(msg_start, p - msg_start)); |
+ ++p; |
+ next_frame = p; |
+ } |
+ } |
+ } |
+ if (next_frame < end) { |
+ m_buffer->append(next_frame, end - next_frame); |
+ } |
+} |
+ |
+void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamHandleError& err) |
+{ |
+ LOG(Network, "WebSocketChannel %p didFaile", this); |
+ ASSERT(handle == m_handle.get() || m_handle.get() == 0); |
+ didClose(handle); |
+} |
+ |
+} // namespace WebCore |