Home | History | Annotate | Download | only in websockets
      1 /*
      2  * Copyright (C) 2009 Google Inc.  All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 
     33 #if ENABLE(WEB_SOCKETS)
     34 
     35 #include "WebSocketChannel.h"
     36 
     37 #include "CString.h"
     38 #include "CookieJar.h"
     39 #include "Document.h"
     40 #include "Logging.h"
     41 #include "PlatformString.h"
     42 #include "ScriptExecutionContext.h"
     43 #include "SocketStreamError.h"
     44 #include "SocketStreamHandle.h"
     45 #include "StringHash.h"
     46 #include "WebSocketChannelClient.h"
     47 
     48 #include <wtf/Deque.h>
     49 #include <wtf/FastMalloc.h>
     50 #include <wtf/HashMap.h>
     51 
     52 namespace WebCore {
     53 
     54 WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol)
     55     : m_context(context)
     56     , m_client(client)
     57     , m_handshake(url, protocol, context)
     58     , m_buffer(0)
     59     , m_bufferSize(0)
     60 {
     61 }
     62 
     63 WebSocketChannel::~WebSocketChannel()
     64 {
     65     fastFree(m_buffer);
     66 }
     67 
     68 void WebSocketChannel::connect()
     69 {
     70     LOG(Network, "WebSocketChannel %p connect", this);
     71     ASSERT(!m_handle);
     72     m_handshake.reset();
     73     ref();
     74     m_handle = SocketStreamHandle::create(m_handshake.url(), this);
     75 }
     76 
     77 bool WebSocketChannel::send(const String& msg)
     78 {
     79     LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data());
     80     ASSERT(m_handle);
     81     Vector<char> buf;
     82     buf.append('\0');  // frame type
     83     buf.append(msg.utf8().data(), msg.utf8().length());
     84     buf.append('\xff');  // frame end
     85     return m_handle->send(buf.data(), buf.size());
     86 }
     87 
     88 unsigned long WebSocketChannel::bufferedAmount() const
     89 {
     90     LOG(Network, "WebSocketChannel %p bufferedAmount", this);
     91     ASSERT(m_handle);
     92     return m_handle->bufferedAmount();
     93 }
     94 
     95 void WebSocketChannel::close()
     96 {
     97     LOG(Network, "WebSocketChannel %p close", this);
     98     if (m_handle)
     99         m_handle->close();  // will call didClose()
    100 }
    101 
    102 void WebSocketChannel::disconnect()
    103 {
    104     LOG(Network, "WebSocketChannel %p disconnect", this);
    105     m_client = 0;
    106     if (m_handle)
    107         m_handle->close();
    108 }
    109 
    110 void WebSocketChannel::didOpen(SocketStreamHandle* handle)
    111 {
    112     LOG(Network, "WebSocketChannel %p didOpen", this);
    113     ASSERT(handle == m_handle);
    114     const CString& handshakeMessage = m_handshake.clientHandshakeMessage();
    115     if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) {
    116         m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, "Error sending handshake message.", 0, m_handshake.clientOrigin());
    117         handle->close();
    118     }
    119 }
    120 
    121 void WebSocketChannel::didClose(SocketStreamHandle* handle)
    122 {
    123     LOG(Network, "WebSocketChannel %p didClose", this);
    124     ASSERT_UNUSED(handle, handle == m_handle || !m_handle);
    125     if (m_handle) {
    126         unsigned long unhandledBufferedAmount = m_handle->bufferedAmount();
    127         WebSocketChannelClient* client = m_client;
    128         m_client = 0;
    129         m_handle = 0;
    130         if (client)
    131             client->didClose(unhandledBufferedAmount);
    132     }
    133     deref();
    134 }
    135 
    136 void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* data, int len)
    137 {
    138     LOG(Network, "WebSocketChannel %p didReceiveData %d", this, len);
    139     RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference.
    140     ASSERT(handle == m_handle);
    141     if (!appendToBuffer(data, len)) {
    142         handle->close();
    143         return;
    144     }
    145     if (!m_client) {
    146         handle->close();
    147         return;
    148     }
    149     if (m_handshake.mode() == WebSocketHandshake::Incomplete) {
    150         int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize);
    151         if (headerLength <= 0)
    152             return;
    153         switch (m_handshake.mode()) {
    154         case WebSocketHandshake::Connected:
    155             if (!m_handshake.serverSetCookie().isEmpty()) {
    156                 if (m_context->isDocument()) {
    157                     Document* document = static_cast<Document*>(m_context);
    158                     if (cookiesEnabled(document)) {
    159                         ExceptionCode ec; // Exception (for sandboxed documents) ignored.
    160                         document->setCookie(m_handshake.serverSetCookie(), ec);
    161                     }
    162                 }
    163             }
    164             // FIXME: handle set-cookie2.
    165             LOG(Network, "WebSocketChannel %p connected", this);
    166             m_client->didConnect();
    167             break;
    168         default:
    169             LOG(Network, "WebSocketChannel %p connection failed", this);
    170             handle->close();
    171             return;
    172         }
    173         skipBuffer(headerLength);
    174         if (!m_buffer)
    175             return;
    176         LOG(Network, "remaining in read buf %ul", m_bufferSize);
    177     }
    178     if (m_handshake.mode() != WebSocketHandshake::Connected)
    179         return;
    180 
    181     const char* nextFrame = m_buffer;
    182     const char* p = m_buffer;
    183     const char* end = p + m_bufferSize;
    184     while (p < end) {
    185         unsigned char frameByte = static_cast<unsigned char>(*p++);
    186         if ((frameByte & 0x80) == 0x80) {
    187             int length = 0;
    188             while (p < end) {
    189                 if (length > std::numeric_limits<int>::max() / 128) {
    190                     LOG(Network, "frame length overflow %d", length);
    191                     handle->close();
    192                     return;
    193                 }
    194                 char msgByte = *p;
    195                 length = length * 128 + (msgByte & 0x7f);
    196                 ++p;
    197                 if (!(msgByte & 0x80))
    198                     break;
    199             }
    200             if (p + length < end) {
    201                 p += length;
    202                 nextFrame = p;
    203             } else
    204                 break;
    205         } else {
    206             const char* msgStart = p;
    207             while (p < end && *p != '\xff')
    208                 ++p;
    209             if (p < end && *p == '\xff') {
    210                 if (frameByte == 0x00)
    211                     m_client->didReceiveMessage(String::fromUTF8(msgStart, p - msgStart));
    212                 ++p;
    213                 nextFrame = p;
    214             }
    215         }
    216     }
    217     skipBuffer(nextFrame - m_buffer);
    218 }
    219 
    220 void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamError&)
    221 {
    222     LOG(Network, "WebSocketChannel %p didFail", this);
    223     ASSERT(handle == m_handle || !m_handle);
    224     handle->close();
    225 }
    226 
    227 void WebSocketChannel::didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)
    228 {
    229 }
    230 
    231 void WebSocketChannel::didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&)
    232 {
    233 }
    234 
    235 bool WebSocketChannel::appendToBuffer(const char* data, int len)
    236 {
    237     char* newBuffer = 0;
    238     if (tryFastMalloc(m_bufferSize + len).getValue(newBuffer)) {
    239         if (m_buffer)
    240             memcpy(newBuffer, m_buffer, m_bufferSize);
    241         memcpy(newBuffer + m_bufferSize, data, len);
    242         fastFree(m_buffer);
    243         m_buffer = newBuffer;
    244         m_bufferSize += len;
    245         return true;
    246     }
    247     m_context->addMessage(ConsoleDestination, JSMessageSource, LogMessageType, ErrorMessageLevel, String::format("WebSocket frame (at %d bytes) is too long.", m_bufferSize + len), 0, m_handshake.clientOrigin());
    248     return false;
    249 }
    250 
    251 void WebSocketChannel::skipBuffer(int len)
    252 {
    253     ASSERT(len <= m_bufferSize);
    254     m_bufferSize -= len;
    255     if (!m_bufferSize) {
    256         fastFree(m_buffer);
    257         m_buffer = 0;
    258         return;
    259     }
    260     memmove(m_buffer, m_buffer + len, m_bufferSize);
    261 }
    262 
    263 }  // namespace WebCore
    264 
    265 #endif  // ENABLE(WEB_SOCKETS)
    266