1 | /* |
2 | * Copyright (C) 2011, 2012 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 | #pragma once |
32 | |
33 | #include "FileReaderLoaderClient.h" |
34 | #include "SocketStreamHandleClient.h" |
35 | #include "ThreadableWebSocketChannel.h" |
36 | #include "Timer.h" |
37 | #include "WebSocketDeflateFramer.h" |
38 | #include "WebSocketFrame.h" |
39 | #include "WebSocketHandshake.h" |
40 | #include <wtf/Deque.h> |
41 | #include <wtf/Forward.h> |
42 | #include <wtf/RefCounted.h> |
43 | #include <wtf/TypeCasts.h> |
44 | #include <wtf/Vector.h> |
45 | #include <wtf/text/CString.h> |
46 | |
47 | namespace WebCore { |
48 | |
49 | class Blob; |
50 | class Document; |
51 | class FileReaderLoader; |
52 | class ResourceRequest; |
53 | class ResourceResponse; |
54 | class SocketProvider; |
55 | class SocketStreamHandle; |
56 | class SocketStreamError; |
57 | class WebSocketChannelClient; |
58 | |
59 | class WebSocketChannel : public RefCounted<WebSocketChannel>, public SocketStreamHandleClient, public ThreadableWebSocketChannel, public FileReaderLoaderClient |
60 | { |
61 | WTF_MAKE_FAST_ALLOCATED; |
62 | public: |
63 | static Ref<WebSocketChannel> create(Document& document, WebSocketChannelClient& client, SocketProvider& provider) { return adoptRef(*new WebSocketChannel(document, client, provider)); } |
64 | virtual ~WebSocketChannel(); |
65 | |
66 | bool isWebSocketChannel() const final { return true; } |
67 | |
68 | bool send(const char* data, int length); |
69 | |
70 | // ThreadableWebSocketChannel functions. |
71 | void connect(const URL&, const String& protocol) override; |
72 | String subprotocol() override; |
73 | String extensions() override; |
74 | ThreadableWebSocketChannel::SendResult send(const String& message) override; |
75 | ThreadableWebSocketChannel::SendResult send(const JSC::ArrayBuffer&, unsigned byteOffset, unsigned byteLength) override; |
76 | ThreadableWebSocketChannel::SendResult send(Blob&) override; |
77 | unsigned bufferedAmount() const override; |
78 | void close(int code, const String& reason) override; // Start closing handshake. |
79 | void fail(const String& reason) override; |
80 | void disconnect() override; |
81 | |
82 | void suspend() override; |
83 | void resume() override; |
84 | |
85 | // SocketStreamHandleClient functions. |
86 | void didOpenSocketStream(SocketStreamHandle&) final; |
87 | void didCloseSocketStream(SocketStreamHandle&) final; |
88 | void didReceiveSocketStreamData(SocketStreamHandle&, const char*, size_t) final; |
89 | void didFailToReceiveSocketStreamData(SocketStreamHandle&) final; |
90 | void didUpdateBufferedAmount(SocketStreamHandle&, size_t bufferedAmount) final; |
91 | void didFailSocketStream(SocketStreamHandle&, const SocketStreamError&) final; |
92 | |
93 | enum CloseEventCode { |
94 | CloseEventCodeNotSpecified = -1, |
95 | CloseEventCodeNormalClosure = 1000, |
96 | CloseEventCodeGoingAway = 1001, |
97 | CloseEventCodeProtocolError = 1002, |
98 | CloseEventCodeUnsupportedData = 1003, |
99 | CloseEventCodeFrameTooLarge = 1004, |
100 | CloseEventCodeNoStatusRcvd = 1005, |
101 | CloseEventCodeAbnormalClosure = 1006, |
102 | CloseEventCodeInvalidFramePayloadData = 1007, |
103 | CloseEventCodePolicyViolation = 1008, |
104 | CloseEventCodeMessageTooBig = 1009, |
105 | CloseEventCodeMandatoryExt = 1010, |
106 | CloseEventCodeInternalError = 1011, |
107 | CloseEventCodeTLSHandshake = 1015, |
108 | CloseEventCodeMinimumUserDefined = 3000, |
109 | CloseEventCodeMaximumUserDefined = 4999 |
110 | }; |
111 | |
112 | // FileReaderLoaderClient functions. |
113 | void didStartLoading() override; |
114 | void didReceiveData() override; |
115 | void didFinishLoading() override; |
116 | void didFail(int errorCode) override; |
117 | |
118 | unsigned identifier() const { return m_identifier; } |
119 | bool hasCreatedHandshake() { return !!m_handshake; } |
120 | ResourceRequest clientHandshakeRequest(Function<String(const URL&)>&& ); |
121 | const ResourceResponse& serverHandshakeResponse() const; |
122 | WebSocketHandshake::Mode handshakeMode() const; |
123 | |
124 | using RefCounted<WebSocketChannel>::ref; |
125 | using RefCounted<WebSocketChannel>::deref; |
126 | |
127 | Document* document() { return m_document.get(); } |
128 | |
129 | protected: |
130 | void refThreadableWebSocketChannel() override { ref(); } |
131 | void derefThreadableWebSocketChannel() override { deref(); } |
132 | |
133 | private: |
134 | WEBCORE_EXPORT WebSocketChannel(Document&, WebSocketChannelClient&, SocketProvider&); |
135 | |
136 | bool appendToBuffer(const char* data, size_t len); |
137 | void skipBuffer(size_t len); |
138 | bool processBuffer(); |
139 | void resumeTimerFired(); |
140 | void startClosingHandshake(int code, const String& reason); |
141 | void closingTimerFired(); |
142 | |
143 | bool processFrame(); |
144 | |
145 | // It is allowed to send a Blob as a binary frame if hybi-10 protocol is in use. Sending a Blob |
146 | // can be delayed because it must be read asynchronously. Other types of data (String or |
147 | // ArrayBuffer) may also be blocked by preceding sending request of a Blob. |
148 | // |
149 | // To address this situation, messages to be sent need to be stored in a queue. Whenever a new |
150 | // data frame is going to be sent, it first must go to the queue. Items in the queue are processed |
151 | // in the order they were put into the queue. Sending request of a Blob blocks further processing |
152 | // until the Blob is completely read and sent to the socket stream. |
153 | enum QueuedFrameType { |
154 | QueuedFrameTypeString, |
155 | QueuedFrameTypeVector, |
156 | QueuedFrameTypeBlob |
157 | }; |
158 | struct QueuedFrame { |
159 | WTF_MAKE_STRUCT_FAST_ALLOCATED; |
160 | |
161 | WebSocketFrame::OpCode opCode; |
162 | QueuedFrameType frameType; |
163 | // Only one of the following items is used, according to the value of frameType. |
164 | CString stringData; |
165 | Vector<char> vectorData; |
166 | RefPtr<Blob> blobData; |
167 | }; |
168 | void enqueueTextFrame(const CString&); |
169 | void enqueueRawFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength); |
170 | void enqueueBlobFrame(WebSocketFrame::OpCode, Blob&); |
171 | |
172 | void processOutgoingFrameQueue(); |
173 | void abortOutgoingFrameQueue(); |
174 | |
175 | enum OutgoingFrameQueueStatus { |
176 | // It is allowed to put a new item into the queue. |
177 | OutgoingFrameQueueOpen, |
178 | // Close frame has already been put into the queue but may not have been sent yet; |
179 | // m_handle->close() will be called as soon as the queue is cleared. It is not |
180 | // allowed to put a new item into the queue. |
181 | OutgoingFrameQueueClosing, |
182 | // Close frame has been sent or the queue was aborted. It is not allowed to put |
183 | // a new item to the queue. |
184 | OutgoingFrameQueueClosed |
185 | }; |
186 | |
187 | // If you are going to send a hybi-10 frame, you need to use the outgoing frame queue |
188 | // instead of call sendFrame() directly. |
189 | void sendFrame(WebSocketFrame::OpCode, const char* data, size_t dataLength, WTF::Function<void(bool)> completionHandler); |
190 | |
191 | enum BlobLoaderStatus { |
192 | BlobLoaderNotStarted, |
193 | BlobLoaderStarted, |
194 | BlobLoaderFinished, |
195 | BlobLoaderFailed |
196 | }; |
197 | |
198 | WeakPtr<Document> m_document; |
199 | WeakPtr<WebSocketChannelClient> m_client; |
200 | std::unique_ptr<WebSocketHandshake> m_handshake; |
201 | RefPtr<SocketStreamHandle> m_handle; |
202 | Vector<char> m_buffer; |
203 | |
204 | Timer m_resumeTimer; |
205 | bool m_suspended { false }; |
206 | bool m_closing { false }; |
207 | bool m_receivedClosingHandshake { false }; |
208 | bool m_allowCookies { true }; |
209 | Timer m_closingTimer; |
210 | bool m_closed { false }; |
211 | bool m_shouldDiscardReceivedData { false }; |
212 | unsigned m_unhandledBufferedAmount { 0 }; |
213 | |
214 | unsigned m_identifier { 0 }; // m_identifier == 0 means that we could not obtain a valid identifier. |
215 | |
216 | // Private members only for hybi-10 protocol. |
217 | bool m_hasContinuousFrame { false }; |
218 | WebSocketFrame::OpCode m_continuousFrameOpCode; |
219 | Vector<uint8_t> m_continuousFrameData; |
220 | unsigned short m_closeEventCode { CloseEventCodeAbnormalClosure }; |
221 | String m_closeEventReason; |
222 | |
223 | Deque<std::unique_ptr<QueuedFrame>> m_outgoingFrameQueue; |
224 | OutgoingFrameQueueStatus m_outgoingFrameQueueStatus { OutgoingFrameQueueOpen }; |
225 | |
226 | // FIXME: Load two or more Blobs simultaneously for better performance. |
227 | std::unique_ptr<FileReaderLoader> m_blobLoader; |
228 | BlobLoaderStatus m_blobLoaderStatus { BlobLoaderNotStarted }; |
229 | |
230 | WebSocketDeflateFramer m_deflateFramer; |
231 | Ref<SocketProvider> m_socketProvider; |
232 | }; |
233 | |
234 | } // namespace WebCore |
235 | |
236 | SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::WebSocketChannel) |
237 | static bool isType(const WebCore::ThreadableWebSocketChannel& threadableWebSocketChannel) { return threadableWebSocketChannel.isWebSocketChannel(); } |
238 | SPECIALIZE_TYPE_TRAITS_END() |
239 | |