/* * Copyright (C) 2011, 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MainThreadWebSocketChannel_h #define MainThreadWebSocketChannel_h #include "core/fileapi/FileError.h" #include "core/fileapi/FileReaderLoaderClient.h" #include "core/frame/ConsoleTypes.h" #include "modules/websockets/WebSocketChannel.h" #include "modules/websockets/WebSocketDeflateFramer.h" #include "modules/websockets/WebSocketFrame.h" #include "modules/websockets/WebSocketHandshake.h" #include "modules/websockets/WebSocketPerMessageDeflate.h" #include "platform/Timer.h" #include "platform/network/SocketStreamHandleClient.h" #include "wtf/Deque.h" #include "wtf/Forward.h" #include "wtf/PassOwnPtr.h" #include "wtf/RefCounted.h" #include "wtf/Vector.h" #include "wtf/text/CString.h" namespace WebCore { class BlobDataHandle; class Document; class FileReaderLoader; class SocketStreamHandle; class SocketStreamError; class WebSocketChannelClient; class MainThreadWebSocketChannel : public RefCounted, public SocketStreamHandleClient, public WebSocketChannel, public FileReaderLoaderClient { WTF_MAKE_FAST_ALLOCATED; public: // You can specify the source file and the line number information // explicitly by passing the last parameter. // In the usual case, they are set automatically and you don't have to // pass it. static PassRefPtr create(Document* document, WebSocketChannelClient* client, const String& sourceURL = String(), unsigned lineNumber = 0) { return adoptRef(new MainThreadWebSocketChannel(document, client, sourceURL, lineNumber)); } virtual ~MainThreadWebSocketChannel(); bool send(const char* data, int length); // WebSocketChannel functions. virtual void connect(const KURL&, const String& protocol) OVERRIDE; virtual String subprotocol() OVERRIDE; virtual String extensions() OVERRIDE; virtual WebSocketChannel::SendResult send(const String& message) OVERRIDE; virtual WebSocketChannel::SendResult send(const ArrayBuffer&, unsigned byteOffset, unsigned byteLength) OVERRIDE; virtual WebSocketChannel::SendResult send(PassRefPtr) OVERRIDE; virtual unsigned long bufferedAmount() const OVERRIDE; // Start closing handshake. Use the CloseEventCodeNotSpecified for the code // argument to omit payload. virtual void close(int code, const String& reason) OVERRIDE; virtual void fail(const String& reason, MessageLevel, const String&, unsigned lineNumber) OVERRIDE; using WebSocketChannel::fail; virtual void disconnect() OVERRIDE; virtual void suspend() OVERRIDE; virtual void resume() OVERRIDE; // SocketStreamHandleClient functions. virtual void willOpenSocketStream(SocketStreamHandle*) OVERRIDE; virtual void didOpenSocketStream(SocketStreamHandle*) OVERRIDE; virtual void didCloseSocketStream(SocketStreamHandle*) OVERRIDE; virtual void didReceiveSocketStreamData(SocketStreamHandle*, const char*, int) OVERRIDE; virtual void didUpdateBufferedAmount(SocketStreamHandle*, size_t bufferedAmount) OVERRIDE; virtual void didFailSocketStream(SocketStreamHandle*, const SocketStreamError&) OVERRIDE; // FileReaderLoaderClient functions. virtual void didStartLoading(); virtual void didReceiveData(); virtual void didFinishLoading(); virtual void didFail(FileError::ErrorCode); using RefCounted::ref; using RefCounted::deref; protected: // WebSocketChannel functions. virtual void refWebSocketChannel() OVERRIDE { ref(); } virtual void derefWebSocketChannel() OVERRIDE { deref(); } private: MainThreadWebSocketChannel(Document*, WebSocketChannelClient*, const String&, unsigned); void disconnectHandle(); bool appendToBuffer(const char* data, size_t len); void skipBuffer(size_t len); // Repeats parsing data from m_buffer until instructed to stop. void processBuffer(); // Parses a handshake response or one frame from m_buffer and processes it. bool processOneItemFromBuffer(); void resumeTimerFired(Timer*); void startClosingHandshake(int code, const String& reason); void closingTimerFired(Timer*); // Parses one frame from m_buffer and processes it. bool processFrame(); // It is allowed to send a Blob as a binary frame if hybi-10 protocol is in use. Sending a Blob // can be delayed because it must be read asynchronously. Other types of data (String or // ArrayBuffer) may also be blocked by preceding sending request of a Blob. // // To address this situation, messages to be sent need to be stored in a queue. Whenever a new // data frame is going to be sent, it first must go to the queue. Items in the queue are processed // in the order they were put into the queue. Sending request of a Blob blocks further processing // until the Blob is completely read and sent to the socket stream. enum QueuedFrameType { QueuedFrameTypeString, QueuedFrameTypeVector, QueuedFrameTypeBlob }; struct QueuedFrame { WebSocketFrame::OpCode opCode; QueuedFrameType frameType; // Only one of the following items is used, according to the value of frameType. CString stringData; Vector vectorData; RefPtr blobData; }; void enqueueTextFrame(const CString&); void enqueueRawFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength); void enqueueBlobFrame(WebSocketFrame::OpCode, PassRefPtr); void failAsError(const String& reason) { fail(reason, ErrorMessageLevel, m_sourceURLAtConstruction, m_lineNumberAtConstruction); } void processOutgoingFrameQueue(); void abortOutgoingFrameQueue(); enum OutgoingFrameQueueStatus { // It is allowed to put a new item into the queue. OutgoingFrameQueueOpen, // Close frame has already been put into the queue but may not have been sent yet; // m_handle->close() will be called as soon as the queue is cleared. It is not // allowed to put a new item into the queue. OutgoingFrameQueueClosing, // Close frame has been sent or the queue was aborted. It is not allowed to put // a new item to the queue. OutgoingFrameQueueClosed }; // In principle, this method is called only by processOutgoingFrameQueue(). // It does work necessary to build frames including Blob loading for queued // data in order. Calling this method directly jumps in the process. bool sendFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength); enum BlobLoaderStatus { BlobLoaderNotStarted, BlobLoaderStarted, BlobLoaderFinished, BlobLoaderFailed }; enum ChannelState { ChannelIdle, ChannelClosing, ChannelClosed }; Document* m_document; WebSocketChannelClient* m_client; OwnPtr m_handshake; RefPtr m_handle; Vector m_buffer; Timer m_resumeTimer; bool m_suspended; bool m_didFailOfClientAlreadyRun; // Set to true iff this instance called disconnect() on m_handle. bool m_hasCalledDisconnectOnHandle; bool m_receivedClosingHandshake; Timer m_closingTimer; ChannelState m_state; bool m_shouldDiscardReceivedData; unsigned long m_unhandledBufferedAmount; unsigned long m_identifier; // m_identifier == 0 means that we could not obtain a valid identifier. // Private members only for hybi-10 protocol. bool m_hasContinuousFrame; WebSocketFrame::OpCode m_continuousFrameOpCode; Vector m_continuousFrameData; unsigned short m_closeEventCode; String m_closeEventReason; Deque > m_outgoingFrameQueue; OutgoingFrameQueueStatus m_outgoingFrameQueueStatus; // FIXME: Load two or more Blobs simultaneously for better performance. OwnPtr m_blobLoader; BlobLoaderStatus m_blobLoaderStatus; // Source code position where construction happened. To be used to show a // console message where no JS callstack info available. String m_sourceURLAtConstruction; unsigned m_lineNumberAtConstruction; WebSocketPerMessageDeflate m_perMessageDeflate; WebSocketDeflateFramer m_deflateFramer; }; } // namespace WebCore #endif // MainThreadWebSocketChannel_h