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