| 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
|
|
|