1 /* 2 * Copyright (C) 2013 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 #include "modules/websockets/NewWebSocketChannelImpl.h" 33 34 #include "core/dom/Document.h" 35 #include "core/dom/ExecutionContext.h" 36 #include "core/fileapi/FileReaderLoader.h" 37 #include "core/fileapi/FileReaderLoaderClient.h" 38 #include "core/inspector/InspectorInstrumentation.h" 39 #include "core/loader/UniqueIdentifier.h" 40 #include "modules/websockets/WebSocketChannelClient.h" 41 #include "platform/Logging.h" 42 #include "platform/weborigin/SecurityOrigin.h" 43 #include "public/platform/Platform.h" 44 #include "public/platform/WebSocketHandshakeRequestInfo.h" 45 #include "public/platform/WebSocketHandshakeResponseInfo.h" 46 #include "public/platform/WebString.h" 47 #include "public/platform/WebURL.h" 48 #include "public/platform/WebVector.h" 49 50 using blink::WebSocketHandle; 51 52 namespace WebCore { 53 54 class NewWebSocketChannelImpl::BlobLoader : public FileReaderLoaderClient { 55 public: 56 BlobLoader(PassRefPtr<BlobDataHandle>, NewWebSocketChannelImpl*); 57 virtual ~BlobLoader() { } 58 59 void cancel(); 60 61 // FileReaderLoaderClient functions. 62 virtual void didStartLoading() OVERRIDE { } 63 virtual void didReceiveData() OVERRIDE { } 64 virtual void didFinishLoading() OVERRIDE; 65 virtual void didFail(FileError::ErrorCode) OVERRIDE; 66 67 private: 68 NewWebSocketChannelImpl* m_channel; 69 FileReaderLoader m_loader; 70 }; 71 72 NewWebSocketChannelImpl::BlobLoader::BlobLoader(PassRefPtr<BlobDataHandle> blobDataHandle, NewWebSocketChannelImpl* channel) 73 : m_channel(channel) 74 , m_loader(FileReaderLoader::ReadAsArrayBuffer, this) 75 { 76 m_loader.start(channel->executionContext(), blobDataHandle); 77 } 78 79 void NewWebSocketChannelImpl::BlobLoader::cancel() 80 { 81 m_loader.cancel(); 82 // didFail will be called immediately. 83 // |this| is deleted here. 84 } 85 86 void NewWebSocketChannelImpl::BlobLoader::didFinishLoading() 87 { 88 m_channel->didFinishLoadingBlob(m_loader.arrayBufferResult()); 89 // |this| is deleted here. 90 } 91 92 void NewWebSocketChannelImpl::BlobLoader::didFail(FileError::ErrorCode errorCode) 93 { 94 m_channel->didFailLoadingBlob(errorCode); 95 // |this| is deleted here. 96 } 97 98 NewWebSocketChannelImpl::NewWebSocketChannelImpl(ExecutionContext* context, WebSocketChannelClient* client, const String& sourceURL, unsigned lineNumber) 99 : ContextLifecycleObserver(context) 100 , m_handle(adoptPtr(blink::Platform::current()->createWebSocketHandle())) 101 , m_client(client) 102 , m_identifier(0) 103 , m_sendingQuota(0) 104 , m_receivedDataSizeForFlowControl(receivedDataSizeForFlowControlHighWaterMark * 2) // initial quota 105 , m_bufferedAmount(0) 106 , m_sentSizeOfTopMessage(0) 107 , m_sourceURLAtConstruction(sourceURL) 108 , m_lineNumberAtConstruction(lineNumber) 109 { 110 if (context->isDocument() && toDocument(context)->page()) 111 m_identifier = createUniqueIdentifier(); 112 } 113 114 NewWebSocketChannelImpl::~NewWebSocketChannelImpl() 115 { 116 abortAsyncOperations(); 117 } 118 119 void NewWebSocketChannelImpl::connect(const KURL& url, const String& protocol) 120 { 121 WTF_LOG(Network, "NewWebSocketChannelImpl %p connect()", this); 122 if (!m_handle) 123 return; 124 m_url = url; 125 Vector<String> protocols; 126 // Avoid placing an empty token in the Vector when the protocol string is 127 // empty. 128 if (!protocol.isEmpty()) { 129 // Since protocol is already verified and escaped, we can simply split 130 // it. 131 protocol.split(", ", true, protocols); 132 } 133 blink::WebVector<blink::WebString> webProtocols(protocols.size()); 134 for (size_t i = 0; i < protocols.size(); ++i) { 135 webProtocols[i] = protocols[i]; 136 } 137 String origin = executionContext()->securityOrigin()->toString(); 138 m_handle->connect(url, webProtocols, origin, this); 139 flowControlIfNecessary(); 140 if (m_identifier) 141 InspectorInstrumentation::didCreateWebSocket(document(), m_identifier, url, protocol); 142 } 143 144 String NewWebSocketChannelImpl::subprotocol() 145 { 146 WTF_LOG(Network, "NewWebSocketChannelImpl %p subprotocol()", this); 147 return m_subprotocol; 148 } 149 150 String NewWebSocketChannelImpl::extensions() 151 { 152 WTF_LOG(Network, "NewWebSocketChannelImpl %p extensions()", this); 153 return m_extensions; 154 } 155 156 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const String& message) 157 { 158 WTF_LOG(Network, "NewWebSocketChannelImpl %p sendText(%s)", this, message.utf8().data()); 159 if (m_identifier) { 160 // FIXME: Change the inspector API to show the entire message instead 161 // of individual frames. 162 CString data = message.utf8(); 163 WebSocketFrame frame(WebSocketFrame::OpCodeText, data.data(), data.length(), WebSocketFrame::Final | WebSocketFrame::Masked); 164 InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, frame); 165 } 166 m_messages.append(Message(message)); 167 sendInternal(); 168 return SendSuccess; 169 } 170 171 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(PassRefPtr<BlobDataHandle> blobDataHandle) 172 { 173 WTF_LOG(Network, "NewWebSocketChannelImpl %p sendBlob(%s, %s, %llu)", this, blobDataHandle->uuid().utf8().data(), blobDataHandle->type().utf8().data(), blobDataHandle->size()); 174 if (m_identifier) { 175 // FIXME: Change the inspector API to show the entire message instead 176 // of individual frames. 177 // FIXME: We can't access the data here. 178 // Since Binary data are not displayed in Inspector, this does not 179 // affect actual behavior. 180 WebSocketFrame frame(WebSocketFrame::OpCodeBinary, "", 0, WebSocketFrame::Final | WebSocketFrame::Masked); 181 InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, frame); 182 } 183 m_messages.append(Message(blobDataHandle)); 184 sendInternal(); 185 return SendSuccess; 186 } 187 188 WebSocketChannel::SendResult NewWebSocketChannelImpl::send(const ArrayBuffer& buffer, unsigned byteOffset, unsigned byteLength) 189 { 190 WTF_LOG(Network, "NewWebSocketChannelImpl %p sendArrayBuffer(%p, %u, %u)", this, buffer.data(), byteOffset, byteLength); 191 if (m_identifier) { 192 // FIXME: Change the inspector API to show the entire message instead 193 // of individual frames. 194 WebSocketFrame frame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(buffer.data()) + byteOffset, byteLength, WebSocketFrame::Final | WebSocketFrame::Masked); 195 InspectorInstrumentation::didSendWebSocketFrame(document(), m_identifier, frame); 196 } 197 // buffer.slice copies its contents. 198 m_messages.append(buffer.slice(byteOffset, byteOffset + byteLength)); 199 sendInternal(); 200 return SendSuccess; 201 } 202 203 unsigned long NewWebSocketChannelImpl::bufferedAmount() const 204 { 205 WTF_LOG(Network, "NewWebSocketChannelImpl %p bufferedAmount()", this); 206 return m_bufferedAmount; 207 } 208 209 void NewWebSocketChannelImpl::close(int code, const String& reason) 210 { 211 WTF_LOG(Network, "NewWebSocketChannelImpl %p close(%d, %s)", this, code, reason.utf8().data()); 212 ASSERT(m_handle); 213 m_handle->close(static_cast<unsigned short>(code), reason); 214 } 215 216 void NewWebSocketChannelImpl::fail(const String& reason, MessageLevel level, const String& sourceURL, unsigned lineNumber) 217 { 218 WTF_LOG(Network, "NewWebSocketChannelImpl %p fail(%s)", this, reason.utf8().data()); 219 // m_handle and m_client can be null here. 220 221 if (m_identifier) 222 InspectorInstrumentation::didReceiveWebSocketFrameError(document(), m_identifier, reason); 223 const String message = "WebSocket connection to '" + m_url.elidedString() + "' failed: " + reason; 224 executionContext()->addConsoleMessage(JSMessageSource, level, message, sourceURL, lineNumber); 225 226 if (m_client) 227 m_client->didReceiveMessageError(); 228 // |reason| is only for logging and should not be provided for scripts, 229 // hence close reason must be empty. 230 handleDidClose(false, CloseEventCodeAbnormalClosure, String()); 231 // handleDidClose may delete this object. 232 } 233 234 void NewWebSocketChannelImpl::disconnect() 235 { 236 WTF_LOG(Network, "NewWebSocketChannelImpl %p disconnect()", this); 237 if (m_identifier) 238 InspectorInstrumentation::didCloseWebSocket(document(), m_identifier); 239 abortAsyncOperations(); 240 m_handle.clear(); 241 m_client = 0; 242 m_identifier = 0; 243 } 244 245 void NewWebSocketChannelImpl::suspend() 246 { 247 WTF_LOG(Network, "NewWebSocketChannelImpl %p suspend()", this); 248 } 249 250 void NewWebSocketChannelImpl::resume() 251 { 252 WTF_LOG(Network, "NewWebSocketChannelImpl %p resume()", this); 253 } 254 255 NewWebSocketChannelImpl::Message::Message(const String& text) 256 : type(MessageTypeText) 257 , text(text.utf8(String::StrictConversionReplacingUnpairedSurrogatesWithFFFD)) { } 258 259 NewWebSocketChannelImpl::Message::Message(PassRefPtr<BlobDataHandle> blobDataHandle) 260 : type(MessageTypeBlob) 261 , blobDataHandle(blobDataHandle) { } 262 263 NewWebSocketChannelImpl::Message::Message(PassRefPtr<ArrayBuffer> arrayBuffer) 264 : type(MessageTypeArrayBuffer) 265 , arrayBuffer(arrayBuffer) { } 266 267 void NewWebSocketChannelImpl::sendInternal() 268 { 269 ASSERT(m_handle); 270 unsigned long bufferedAmount = m_bufferedAmount; 271 while (!m_messages.isEmpty() && m_sendingQuota > 0 && !m_blobLoader) { 272 bool final = false; 273 const Message& message = m_messages.first(); 274 switch (message.type) { 275 case MessageTypeText: { 276 WebSocketHandle::MessageType type = 277 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuation : WebSocketHandle::MessageTypeText; 278 size_t size = std::min(static_cast<size_t>(m_sendingQuota), message.text.length() - m_sentSizeOfTopMessage); 279 final = (m_sentSizeOfTopMessage + size == message.text.length()); 280 m_handle->send(final, type, message.text.data() + m_sentSizeOfTopMessage, size); 281 m_sentSizeOfTopMessage += size; 282 m_sendingQuota -= size; 283 break; 284 } 285 case MessageTypeBlob: 286 ASSERT(!m_blobLoader); 287 m_blobLoader = adoptPtr(new BlobLoader(message.blobDataHandle, this)); 288 break; 289 case MessageTypeArrayBuffer: { 290 WebSocketHandle::MessageType type = 291 m_sentSizeOfTopMessage ? WebSocketHandle::MessageTypeContinuation : WebSocketHandle::MessageTypeBinary; 292 size_t size = std::min(static_cast<size_t>(m_sendingQuota), message.arrayBuffer->byteLength() - m_sentSizeOfTopMessage); 293 final = (m_sentSizeOfTopMessage + size == message.arrayBuffer->byteLength()); 294 m_handle->send(final, type, static_cast<const char*>(message.arrayBuffer->data()) + m_sentSizeOfTopMessage, size); 295 m_sentSizeOfTopMessage += size; 296 m_sendingQuota -= size; 297 break; 298 } 299 } 300 if (final) { 301 m_messages.removeFirst(); 302 m_sentSizeOfTopMessage = 0; 303 } 304 } 305 if (m_client && m_bufferedAmount != bufferedAmount) { 306 m_client->didUpdateBufferedAmount(m_bufferedAmount); 307 } 308 } 309 310 void NewWebSocketChannelImpl::flowControlIfNecessary() 311 { 312 if (!m_handle || m_receivedDataSizeForFlowControl < receivedDataSizeForFlowControlHighWaterMark) { 313 return; 314 } 315 m_handle->flowControl(m_receivedDataSizeForFlowControl); 316 m_receivedDataSizeForFlowControl = 0; 317 } 318 319 void NewWebSocketChannelImpl::abortAsyncOperations() 320 { 321 if (m_blobLoader) { 322 m_blobLoader->cancel(); 323 m_blobLoader.clear(); 324 } 325 } 326 327 void NewWebSocketChannelImpl::handleDidClose(bool wasClean, unsigned short code, const String& reason) 328 { 329 m_handle.clear(); 330 abortAsyncOperations(); 331 if (!m_client) { 332 return; 333 } 334 WebSocketChannelClient* client = m_client; 335 m_client = 0; 336 WebSocketChannelClient::ClosingHandshakeCompletionStatus status = 337 wasClean ? WebSocketChannelClient::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete; 338 client->didClose(m_bufferedAmount, status, code, reason); 339 // client->didClose may delete this object. 340 } 341 342 Document* NewWebSocketChannelImpl::document() 343 { 344 ASSERT(m_identifier); 345 ExecutionContext* context = executionContext(); 346 ASSERT(context->isDocument()); 347 return toDocument(context); 348 } 349 350 void NewWebSocketChannelImpl::didConnect(WebSocketHandle* handle, bool fail, const blink::WebString& selectedProtocol, const blink::WebString& extensions) 351 { 352 WTF_LOG(Network, "NewWebSocketChannelImpl %p didConnect(%p, %d, %s, %s)", this, handle, fail, selectedProtocol.utf8().data(), extensions.utf8().data()); 353 ASSERT(m_handle); 354 ASSERT(handle == m_handle); 355 ASSERT(m_client); 356 if (fail) { 357 failAsError("Cannot connect to " + m_url.string() + "."); 358 // failAsError may delete this object. 359 return; 360 } 361 m_subprotocol = selectedProtocol; 362 m_extensions = extensions; 363 m_client->didConnect(); 364 } 365 366 void NewWebSocketChannelImpl::didStartOpeningHandshake(WebSocketHandle* handle, const blink::WebSocketHandshakeRequestInfo& request) 367 { 368 WTF_LOG(Network, "NewWebSocketChannelImpl %p didStartOpeningHandshake(%p)", this, handle); 369 if (m_identifier) 370 InspectorInstrumentation::willSendWebSocketHandshakeRequest(document(), m_identifier, request.toCoreRequest()); 371 } 372 373 void NewWebSocketChannelImpl::didFinishOpeningHandshake(WebSocketHandle* handle, const blink::WebSocketHandshakeResponseInfo& response) 374 { 375 WTF_LOG(Network, "NewWebSocketChannelImpl %p didFinishOpeningHandshake(%p)", this, handle); 376 if (m_identifier) 377 InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(document(), m_identifier, response.toCoreResponse()); 378 } 379 380 void NewWebSocketChannelImpl::didFail(WebSocketHandle* handle, const blink::WebString& message) 381 { 382 WTF_LOG(Network, "NewWebSocketChannelImpl %p didFail(%p, %s)", this, handle, message.utf8().data()); 383 // This function is called when the browser is required to fail the 384 // WebSocketConnection. Hence we fail this channel by calling 385 // |this->failAsError| function. 386 failAsError(message); 387 // |this| may be deleted. 388 } 389 390 void NewWebSocketChannelImpl::didReceiveData(WebSocketHandle* handle, bool fin, WebSocketHandle::MessageType type, const char* data, size_t size) 391 { 392 WTF_LOG(Network, "NewWebSocketChannelImpl %p didReceiveData(%p, %d, %d, (%p, %zu))", this, handle, fin, type, data, size); 393 ASSERT(m_handle); 394 ASSERT(handle == m_handle); 395 ASSERT(m_client); 396 // Non-final frames cannot be empty. 397 ASSERT(fin || size); 398 switch (type) { 399 case WebSocketHandle::MessageTypeText: 400 ASSERT(m_receivingMessageData.isEmpty()); 401 m_receivingMessageTypeIsText = true; 402 break; 403 case WebSocketHandle::MessageTypeBinary: 404 ASSERT(m_receivingMessageData.isEmpty()); 405 m_receivingMessageTypeIsText = false; 406 break; 407 case WebSocketHandle::MessageTypeContinuation: 408 ASSERT(!m_receivingMessageData.isEmpty()); 409 break; 410 } 411 412 m_receivingMessageData.append(data, size); 413 m_receivedDataSizeForFlowControl += size; 414 flowControlIfNecessary(); 415 if (!fin) { 416 return; 417 } 418 if (m_identifier) { 419 // FIXME: Change the inspector API to show the entire message instead 420 // of individual frames. 421 WebSocketFrame::OpCode opcode = m_receivingMessageTypeIsText ? WebSocketFrame::OpCodeText : WebSocketFrame::OpCodeBinary; 422 WebSocketFrame frame(opcode, m_receivingMessageData.data(), m_receivingMessageData.size(), WebSocketFrame::Final); 423 InspectorInstrumentation::didReceiveWebSocketFrame(document(), m_identifier, frame); 424 } 425 if (m_receivingMessageTypeIsText) { 426 String message = String::fromUTF8(m_receivingMessageData.data(), m_receivingMessageData.size()); 427 m_receivingMessageData.clear(); 428 if (message.isNull()) { 429 failAsError("Could not decode a text frame as UTF-8."); 430 // failAsError may delete this object. 431 } else { 432 m_client->didReceiveMessage(message); 433 } 434 } else { 435 OwnPtr<Vector<char> > binaryData = adoptPtr(new Vector<char>); 436 binaryData->swap(m_receivingMessageData); 437 m_client->didReceiveBinaryData(binaryData.release()); 438 } 439 } 440 441 void NewWebSocketChannelImpl::didClose(WebSocketHandle* handle, bool wasClean, unsigned short code, const blink::WebString& reason) 442 { 443 WTF_LOG(Network, "NewWebSocketChannelImpl %p didClose(%p, %d, %u, %s)", this, handle, wasClean, code, String(reason).utf8().data()); 444 ASSERT(m_handle); 445 m_handle.clear(); 446 if (m_identifier) { 447 InspectorInstrumentation::didCloseWebSocket(document(), m_identifier); 448 m_identifier = 0; 449 } 450 451 handleDidClose(wasClean, code, reason); 452 // handleDidClose may delete this object. 453 } 454 455 void NewWebSocketChannelImpl::didReceiveFlowControl(WebSocketHandle* handle, int64_t quota) 456 { 457 WTF_LOG(Network, "NewWebSocketChannelImpl %p didReceiveFlowControl(%p, %ld)", this, handle, static_cast<long>(quota)); 458 ASSERT(m_handle); 459 m_sendingQuota += quota; 460 sendInternal(); 461 } 462 463 void NewWebSocketChannelImpl::didFinishLoadingBlob(PassRefPtr<ArrayBuffer> buffer) 464 { 465 m_blobLoader.clear(); 466 ASSERT(m_handle); 467 // The loaded blob is always placed on m_messages[0]. 468 ASSERT(m_messages.size() > 0 && m_messages.first().type == MessageTypeBlob); 469 // We replace it with the loaded blob. 470 m_messages.first() = Message(buffer); 471 sendInternal(); 472 } 473 474 void NewWebSocketChannelImpl::didFailLoadingBlob(FileError::ErrorCode errorCode) 475 { 476 m_blobLoader.clear(); 477 if (errorCode == FileError::ABORT_ERR) { 478 // The error is caused by cancel(). 479 return; 480 } 481 // FIXME: Generate human-friendly reason message. 482 failAsError("Failed to load Blob: error code = " + String::number(errorCode)); 483 // |this| can be deleted here. 484 } 485 486 } // namespace WebCore 487