Home | History | Annotate | Download | only in websockets
      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