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 "CookieJar.h" 38 #include "Document.h" 39 #include "InspectorInstrumentation.h" 40 #include "Logging.h" 41 #include "Page.h" 42 #include "PlatformString.h" 43 #include "ProgressTracker.h" 44 #include "ScriptCallStack.h" 45 #include "ScriptExecutionContext.h" 46 #include "SocketStreamError.h" 47 #include "SocketStreamHandle.h" 48 #include "WebSocketChannelClient.h" 49 #include "WebSocketHandshake.h" 50 51 #include <wtf/text/CString.h> 52 #include <wtf/text/StringConcatenate.h> 53 #include <wtf/text/StringHash.h> 54 #include <wtf/Deque.h> 55 #include <wtf/FastMalloc.h> 56 #include <wtf/HashMap.h> 57 58 namespace WebCore { 59 60 WebSocketChannel::WebSocketChannel(ScriptExecutionContext* context, WebSocketChannelClient* client, const KURL& url, const String& protocol) 61 : m_context(context) 62 , m_client(client) 63 , m_handshake(url, protocol, context) 64 , m_buffer(0) 65 , m_bufferSize(0) 66 , m_resumeTimer(this, &WebSocketChannel::resumeTimerFired) 67 , m_suspended(false) 68 , m_closed(false) 69 , m_shouldDiscardReceivedData(false) 70 , m_unhandledBufferedAmount(0) 71 , m_identifier(0) 72 { 73 if (m_context->isDocument()) 74 if (Page* page = static_cast<Document*>(m_context)->page()) 75 m_identifier = page->progress()->createUniqueIdentifier(); 76 77 if (m_identifier) 78 InspectorInstrumentation::didCreateWebSocket(m_context, m_identifier, url, m_context->url()); 79 } 80 81 WebSocketChannel::~WebSocketChannel() 82 { 83 fastFree(m_buffer); 84 } 85 86 void WebSocketChannel::connect() 87 { 88 LOG(Network, "WebSocketChannel %p connect", this); 89 ASSERT(!m_handle); 90 ASSERT(!m_suspended); 91 m_handshake.reset(); 92 ref(); 93 m_handle = SocketStreamHandle::create(m_handshake.url(), this); 94 } 95 96 bool WebSocketChannel::send(const String& msg) 97 { 98 LOG(Network, "WebSocketChannel %p send %s", this, msg.utf8().data()); 99 ASSERT(m_handle); 100 ASSERT(!m_suspended); 101 Vector<char> buf; 102 buf.append('\0'); // frame type 103 CString utf8 = msg.utf8(); 104 buf.append(utf8.data(), utf8.length()); 105 buf.append('\xff'); // frame end 106 return m_handle->send(buf.data(), buf.size()); 107 } 108 109 unsigned long WebSocketChannel::bufferedAmount() const 110 { 111 LOG(Network, "WebSocketChannel %p bufferedAmount", this); 112 ASSERT(m_handle); 113 ASSERT(!m_suspended); 114 return m_handle->bufferedAmount(); 115 } 116 117 void WebSocketChannel::close() 118 { 119 LOG(Network, "WebSocketChannel %p close", this); 120 ASSERT(!m_suspended); 121 if (m_handle) 122 m_handle->close(); // will call didClose() 123 } 124 125 void WebSocketChannel::disconnect() 126 { 127 LOG(Network, "WebSocketChannel %p disconnect", this); 128 if (m_identifier && m_context) 129 InspectorInstrumentation::didCloseWebSocket(m_context, m_identifier); 130 m_handshake.clearScriptExecutionContext(); 131 m_client = 0; 132 m_context = 0; 133 if (m_handle) 134 m_handle->close(); 135 } 136 137 void WebSocketChannel::suspend() 138 { 139 m_suspended = true; 140 } 141 142 void WebSocketChannel::resume() 143 { 144 m_suspended = false; 145 if ((m_buffer || m_closed) && m_client && !m_resumeTimer.isActive()) 146 m_resumeTimer.startOneShot(0); 147 } 148 149 void WebSocketChannel::didOpen(SocketStreamHandle* handle) 150 { 151 LOG(Network, "WebSocketChannel %p didOpen", this); 152 ASSERT(handle == m_handle); 153 if (!m_context) 154 return; 155 if (m_identifier) 156 InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_context, m_identifier, m_handshake.clientHandshakeRequest()); 157 CString handshakeMessage = m_handshake.clientHandshakeMessage(); 158 if (!handle->send(handshakeMessage.data(), handshakeMessage.length())) { 159 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, "Error sending handshake message.", 0, m_handshake.clientOrigin(), 0); 160 handle->close(); 161 } 162 } 163 164 void WebSocketChannel::didClose(SocketStreamHandle* handle) 165 { 166 LOG(Network, "WebSocketChannel %p didClose", this); 167 if (m_identifier && m_context) 168 InspectorInstrumentation::didCloseWebSocket(m_context, m_identifier); 169 ASSERT_UNUSED(handle, handle == m_handle || !m_handle); 170 m_closed = true; 171 if (m_handle) { 172 m_unhandledBufferedAmount = m_handle->bufferedAmount(); 173 if (m_suspended) 174 return; 175 WebSocketChannelClient* client = m_client; 176 m_client = 0; 177 m_context = 0; 178 m_handle = 0; 179 if (client) 180 client->didClose(m_unhandledBufferedAmount); 181 } 182 deref(); 183 } 184 185 void WebSocketChannel::didReceiveData(SocketStreamHandle* handle, const char* data, int len) 186 { 187 LOG(Network, "WebSocketChannel %p didReceiveData %d", this, len); 188 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference. 189 ASSERT(handle == m_handle); 190 if (!m_context) { 191 return; 192 } 193 if (!m_client) { 194 m_shouldDiscardReceivedData = true; 195 handle->close(); 196 return; 197 } 198 if (m_shouldDiscardReceivedData) 199 return; 200 if (!appendToBuffer(data, len)) { 201 m_shouldDiscardReceivedData = true; 202 handle->close(); 203 return; 204 } 205 while (!m_suspended && m_client && m_buffer) 206 if (!processBuffer()) 207 break; 208 } 209 210 void WebSocketChannel::didFail(SocketStreamHandle* handle, const SocketStreamError& error) 211 { 212 LOG(Network, "WebSocketChannel %p didFail", this); 213 ASSERT(handle == m_handle || !m_handle); 214 if (m_context) { 215 String message; 216 if (error.isNull()) 217 message = "WebSocket network error"; 218 else if (error.localizedDescription().isNull()) 219 message = makeString("WebSocket network error: error code ", String::number(error.errorCode())); 220 else 221 message = makeString("WebSocket network error: ", error.localizedDescription()); 222 m_context->addMessage(OtherMessageSource, NetworkErrorMessageType, ErrorMessageLevel, message, 0, error.failingURL(), 0); 223 } 224 m_shouldDiscardReceivedData = true; 225 handle->close(); 226 } 227 228 void WebSocketChannel::didReceiveAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) 229 { 230 } 231 232 void WebSocketChannel::didCancelAuthenticationChallenge(SocketStreamHandle*, const AuthenticationChallenge&) 233 { 234 } 235 236 bool WebSocketChannel::appendToBuffer(const char* data, size_t len) 237 { 238 size_t newBufferSize = m_bufferSize + len; 239 if (newBufferSize < m_bufferSize) { 240 LOG(Network, "WebSocket buffer overflow (%lu+%lu)", static_cast<unsigned long>(m_bufferSize), static_cast<unsigned long>(len)); 241 return false; 242 } 243 char* newBuffer = 0; 244 if (tryFastMalloc(newBufferSize).getValue(newBuffer)) { 245 if (m_buffer) 246 memcpy(newBuffer, m_buffer, m_bufferSize); 247 memcpy(newBuffer + m_bufferSize, data, len); 248 fastFree(m_buffer); 249 m_buffer = newBuffer; 250 m_bufferSize = newBufferSize; 251 return true; 252 } 253 m_context->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, makeString("WebSocket frame (at ", String::number(static_cast<unsigned long>(newBufferSize)), " bytes) is too long."), 0, m_handshake.clientOrigin(), 0); 254 return false; 255 } 256 257 void WebSocketChannel::skipBuffer(size_t len) 258 { 259 ASSERT(len <= m_bufferSize); 260 m_bufferSize -= len; 261 if (!m_bufferSize) { 262 fastFree(m_buffer); 263 m_buffer = 0; 264 return; 265 } 266 memmove(m_buffer, m_buffer + len, m_bufferSize); 267 } 268 269 bool WebSocketChannel::processBuffer() 270 { 271 ASSERT(!m_suspended); 272 ASSERT(m_client); 273 ASSERT(m_buffer); 274 if (m_shouldDiscardReceivedData) 275 return false; 276 277 if (m_handshake.mode() == WebSocketHandshake::Incomplete) { 278 int headerLength = m_handshake.readServerHandshake(m_buffer, m_bufferSize); 279 if (headerLength <= 0) 280 return false; 281 if (m_handshake.mode() == WebSocketHandshake::Connected) { 282 if (m_identifier) 283 InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(m_context, m_identifier, m_handshake.serverHandshakeResponse()); 284 if (!m_handshake.serverSetCookie().isEmpty()) { 285 if (m_context->isDocument()) { 286 Document* document = static_cast<Document*>(m_context); 287 if (cookiesEnabled(document)) { 288 ExceptionCode ec; // Exception (for sandboxed documents) ignored. 289 document->setCookie(m_handshake.serverSetCookie(), ec); 290 } 291 } 292 } 293 // FIXME: handle set-cookie2. 294 LOG(Network, "WebSocketChannel %p connected", this); 295 skipBuffer(headerLength); 296 m_client->didConnect(); 297 LOG(Network, "remaining in read buf %lu", static_cast<unsigned long>(m_bufferSize)); 298 return m_buffer; 299 } 300 LOG(Network, "WebSocketChannel %p connection failed", this); 301 skipBuffer(headerLength); 302 m_shouldDiscardReceivedData = true; 303 if (!m_closed) 304 m_handle->close(); 305 return false; 306 } 307 if (m_handshake.mode() != WebSocketHandshake::Connected) 308 return false; 309 310 const char* nextFrame = m_buffer; 311 const char* p = m_buffer; 312 const char* end = p + m_bufferSize; 313 314 unsigned char frameByte = static_cast<unsigned char>(*p++); 315 if ((frameByte & 0x80) == 0x80) { 316 size_t length = 0; 317 bool errorFrame = false; 318 while (p < end) { 319 if (length > std::numeric_limits<size_t>::max() / 128) { 320 LOG(Network, "frame length overflow %lu", static_cast<unsigned long>(length)); 321 errorFrame = true; 322 break; 323 } 324 size_t newLength = length * 128; 325 unsigned char msgByte = static_cast<unsigned char>(*p); 326 unsigned int lengthMsgByte = msgByte & 0x7f; 327 if (newLength > std::numeric_limits<size_t>::max() - lengthMsgByte) { 328 LOG(Network, "frame length overflow %lu+%u", static_cast<unsigned long>(newLength), lengthMsgByte); 329 errorFrame = true; 330 break; 331 } 332 newLength += lengthMsgByte; 333 if (newLength < length) { // sanity check 334 LOG(Network, "frame length integer wrap %lu->%lu", static_cast<unsigned long>(length), static_cast<unsigned long>(newLength)); 335 errorFrame = true; 336 break; 337 } 338 length = newLength; 339 ++p; 340 if (!(msgByte & 0x80)) 341 break; 342 } 343 if (p + length < p) { 344 LOG(Network, "frame buffer pointer wrap %p+%lu->%p", p, static_cast<unsigned long>(length), p + length); 345 errorFrame = true; 346 } 347 if (errorFrame) { 348 skipBuffer(m_bufferSize); // Save memory. 349 m_shouldDiscardReceivedData = true; 350 m_client->didReceiveMessageError(); 351 if (!m_client) 352 return false; 353 if (!m_closed) 354 m_handle->close(); 355 return false; 356 } 357 ASSERT(p + length >= p); 358 if (p + length < end) { 359 p += length; 360 nextFrame = p; 361 ASSERT(nextFrame > m_buffer); 362 skipBuffer(nextFrame - m_buffer); 363 m_client->didReceiveMessageError(); 364 return m_buffer; 365 } 366 return false; 367 } 368 369 const char* msgStart = p; 370 while (p < end && *p != '\xff') 371 ++p; 372 if (p < end && *p == '\xff') { 373 int msgLength = p - msgStart; 374 ++p; 375 nextFrame = p; 376 if (frameByte == 0x00) { 377 String msg = String::fromUTF8(msgStart, msgLength); 378 skipBuffer(nextFrame - m_buffer); 379 m_client->didReceiveMessage(msg); 380 } else { 381 skipBuffer(nextFrame - m_buffer); 382 m_client->didReceiveMessageError(); 383 } 384 return m_buffer; 385 } 386 return false; 387 } 388 389 void WebSocketChannel::resumeTimerFired(Timer<WebSocketChannel>* timer) 390 { 391 ASSERT_UNUSED(timer, timer == &m_resumeTimer); 392 393 RefPtr<WebSocketChannel> protect(this); // The client can close the channel, potentially removing the last reference. 394 while (!m_suspended && m_client && m_buffer) 395 if (!processBuffer()) 396 break; 397 if (!m_suspended && m_client && m_closed && m_handle) 398 didClose(m_handle.get()); 399 } 400 401 } // namespace WebCore 402 403 #endif // ENABLE(WEB_SOCKETS) 404