1 | /* |
2 | * Copyright (C) 2011, 2012 Google Inc. All rights reserved. |
3 | * Copyright (C) 2018 Apple Inc. All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are |
7 | * met: |
8 | * |
9 | * * Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * * Redistributions in binary form must reproduce the above |
12 | * copyright notice, this list of conditions and the following disclaimer |
13 | * in the documentation and/or other materials provided with the |
14 | * distribution. |
15 | * * Neither the name of Google Inc. nor the names of its |
16 | * contributors may be used to endorse or promote products derived from |
17 | * this software without specific prior written permission. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include "config.h" |
33 | #include "WebSocketChannel.h" |
34 | |
35 | #include "Blob.h" |
36 | #include "ContentRuleListResults.h" |
37 | #include "CookieJar.h" |
38 | #include "Document.h" |
39 | #include "FileError.h" |
40 | #include "FileReaderLoader.h" |
41 | #include "Frame.h" |
42 | #include "FrameLoader.h" |
43 | #include "InspectorInstrumentation.h" |
44 | #include "Logging.h" |
45 | #include "NetworkingContext.h" |
46 | #include "Page.h" |
47 | #include "ProgressTracker.h" |
48 | #include "ResourceRequest.h" |
49 | #include "ScriptExecutionContext.h" |
50 | #include "SocketProvider.h" |
51 | #include "SocketStreamError.h" |
52 | #include "SocketStreamHandle.h" |
53 | #include "UserContentProvider.h" |
54 | #include "WebSocketChannelClient.h" |
55 | #include "WebSocketHandshake.h" |
56 | #include <JavaScriptCore/ArrayBuffer.h> |
57 | #include <wtf/FastMalloc.h> |
58 | #include <wtf/HashMap.h> |
59 | #include <wtf/text/CString.h> |
60 | #include <wtf/text/StringHash.h> |
61 | |
62 | namespace WebCore { |
63 | |
64 | const Seconds TCPMaximumSegmentLifetime { 2_min }; |
65 | |
66 | WebSocketChannel::WebSocketChannel(Document& document, WebSocketChannelClient& client, SocketProvider& provider) |
67 | : m_document(makeWeakPtr(document)) |
68 | , m_client(makeWeakPtr(client)) |
69 | , m_resumeTimer(*this, &WebSocketChannel::resumeTimerFired) |
70 | , m_closingTimer(*this, &WebSocketChannel::closingTimerFired) |
71 | , m_socketProvider(provider) |
72 | { |
73 | if (Page* page = document.page()) |
74 | m_identifier = page->progress().createUniqueIdentifier(); |
75 | |
76 | LOG(Network, "WebSocketChannel %p ctor, identifier %u" , this, m_identifier); |
77 | } |
78 | |
79 | WebSocketChannel::~WebSocketChannel() |
80 | { |
81 | LOG(Network, "WebSocketChannel %p dtor" , this); |
82 | } |
83 | |
84 | void WebSocketChannel::connect(const URL& requestedURL, const String& protocol) |
85 | { |
86 | LOG(Network, "WebSocketChannel %p connect()" , this); |
87 | |
88 | URL url = requestedURL; |
89 | #if ENABLE(CONTENT_EXTENSIONS) |
90 | if (auto* page = m_document->page()) { |
91 | if (auto* documentLoader = m_document->loader()) { |
92 | auto results = page->userContentProvider().processContentRuleListsForLoad(url, ContentExtensions::ResourceType::Raw, *documentLoader); |
93 | if (results.summary.blockedLoad) { |
94 | Ref<WebSocketChannel> protectedThis(*this); |
95 | callOnMainThread([protectedThis = WTFMove(protectedThis)] { |
96 | if (protectedThis->m_client) |
97 | protectedThis->m_client->didReceiveMessageError(); |
98 | }); |
99 | return; |
100 | } |
101 | if (results.summary.madeHTTPS) { |
102 | ASSERT(url.protocolIs("ws" )); |
103 | url.setProtocol("wss" ); |
104 | if (m_client) |
105 | m_client->didUpgradeURL(); |
106 | } |
107 | if (results.summary.blockedCookies) |
108 | m_allowCookies = false; |
109 | } |
110 | } |
111 | #endif |
112 | |
113 | ASSERT(!m_handle); |
114 | ASSERT(!m_suspended); |
115 | |
116 | String userAgent = m_document->userAgent(m_document->url()); |
117 | String clientOrigin = m_document->securityOrigin().toString(); |
118 | m_handshake = std::make_unique<WebSocketHandshake>(url, protocol, userAgent, clientOrigin, m_allowCookies); |
119 | m_handshake->reset(); |
120 | if (m_deflateFramer.canDeflate()) |
121 | m_handshake->addExtensionProcessor(m_deflateFramer.createExtensionProcessor()); |
122 | if (m_identifier) |
123 | InspectorInstrumentation::didCreateWebSocket(m_document.get(), m_identifier, url); |
124 | |
125 | if (Frame* frame = m_document->frame()) { |
126 | ref(); |
127 | Page* page = frame->page(); |
128 | PAL::SessionID sessionID = page ? page->sessionID() : PAL::SessionID::defaultSessionID(); |
129 | String partition = m_document->domainForCachePartition(); |
130 | m_handle = m_socketProvider->createSocketStreamHandle(m_handshake->url(), *this, sessionID, partition, frame->loader().networkingContext()); |
131 | } |
132 | } |
133 | |
134 | String WebSocketChannel::subprotocol() |
135 | { |
136 | LOG(Network, "WebSocketChannel %p subprotocol()" , this); |
137 | if (!m_handshake || m_handshake->mode() != WebSocketHandshake::Connected) |
138 | return emptyString(); |
139 | String serverProtocol = m_handshake->serverWebSocketProtocol(); |
140 | if (serverProtocol.isNull()) |
141 | return emptyString(); |
142 | return serverProtocol; |
143 | } |
144 | |
145 | String WebSocketChannel::extensions() |
146 | { |
147 | LOG(Network, "WebSocketChannel %p extensions()" , this); |
148 | if (!m_handshake || m_handshake->mode() != WebSocketHandshake::Connected) |
149 | return emptyString(); |
150 | String extensions = m_handshake->acceptedExtensions(); |
151 | if (extensions.isNull()) |
152 | return emptyString(); |
153 | return extensions; |
154 | } |
155 | |
156 | ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const String& message) |
157 | { |
158 | LOG(Network, "WebSocketChannel %p send() Sending String '%s'" , this, message.utf8().data()); |
159 | CString utf8 = message.utf8(StrictConversionReplacingUnpairedSurrogatesWithFFFD); |
160 | enqueueTextFrame(utf8); |
161 | processOutgoingFrameQueue(); |
162 | // According to WebSocket API specification, WebSocket.send() should return void instead |
163 | // of boolean. However, our implementation still returns boolean due to compatibility |
164 | // concern (see bug 65850). |
165 | // m_channel->send() may happen later, thus it's not always possible to know whether |
166 | // the message has been sent to the socket successfully. In this case, we have no choice |
167 | // but to return true. |
168 | return ThreadableWebSocketChannel::SendSuccess; |
169 | } |
170 | |
171 | ThreadableWebSocketChannel::SendResult WebSocketChannel::send(const ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength) |
172 | { |
173 | LOG(Network, "WebSocketChannel %p send() Sending ArrayBuffer %p byteOffset=%u byteLength=%u" , this, &binaryData, byteOffset, byteLength); |
174 | enqueueRawFrame(WebSocketFrame::OpCodeBinary, static_cast<const char*>(binaryData.data()) + byteOffset, byteLength); |
175 | processOutgoingFrameQueue(); |
176 | return ThreadableWebSocketChannel::SendSuccess; |
177 | } |
178 | |
179 | ThreadableWebSocketChannel::SendResult WebSocketChannel::send(Blob& binaryData) |
180 | { |
181 | LOG(Network, "WebSocketChannel %p send() Sending Blob '%s'" , this, binaryData.url().string().utf8().data()); |
182 | enqueueBlobFrame(WebSocketFrame::OpCodeBinary, binaryData); |
183 | processOutgoingFrameQueue(); |
184 | return ThreadableWebSocketChannel::SendSuccess; |
185 | } |
186 | |
187 | bool WebSocketChannel::send(const char* data, int length) |
188 | { |
189 | LOG(Network, "WebSocketChannel %p send() Sending char* data=%p length=%d" , this, data, length); |
190 | enqueueRawFrame(WebSocketFrame::OpCodeBinary, data, length); |
191 | processOutgoingFrameQueue(); |
192 | return true; |
193 | } |
194 | |
195 | unsigned WebSocketChannel::bufferedAmount() const |
196 | { |
197 | LOG(Network, "WebSocketChannel %p bufferedAmount()" , this); |
198 | ASSERT(m_handle); |
199 | ASSERT(!m_suspended); |
200 | return m_handle->bufferedAmount(); |
201 | } |
202 | |
203 | void WebSocketChannel::close(int code, const String& reason) |
204 | { |
205 | LOG(Network, "WebSocketChannel %p close() code=%d reason='%s'" , this, code, reason.utf8().data()); |
206 | ASSERT(!m_suspended); |
207 | if (!m_handle) |
208 | return; |
209 | Ref<WebSocketChannel> protectedThis(*this); // An attempt to send closing handshake may fail, which will get the channel closed and dereferenced. |
210 | startClosingHandshake(code, reason); |
211 | if (m_closing && !m_closingTimer.isActive()) |
212 | m_closingTimer.startOneShot(TCPMaximumSegmentLifetime * 2); |
213 | } |
214 | |
215 | void WebSocketChannel::fail(const String& reason) |
216 | { |
217 | LOG(Network, "WebSocketChannel %p fail() reason='%s'" , this, reason.utf8().data()); |
218 | ASSERT(!m_suspended); |
219 | if (m_document) { |
220 | InspectorInstrumentation::didReceiveWebSocketFrameError(m_document.get(), m_identifier, reason); |
221 | |
222 | String consoleMessage; |
223 | if (m_handshake) |
224 | consoleMessage = makeString("WebSocket connection to '" , m_handshake->url().stringCenterEllipsizedToLength(), "' failed: " , reason); |
225 | else |
226 | consoleMessage = makeString("WebSocket connection failed: " , reason); |
227 | |
228 | m_document->addConsoleMessage(MessageSource::Network, MessageLevel::Error, consoleMessage); |
229 | } |
230 | |
231 | // Hybi-10 specification explicitly states we must not continue to handle incoming data |
232 | // once the WebSocket connection is failed (section 7.1.7). |
233 | Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference. |
234 | m_shouldDiscardReceivedData = true; |
235 | if (!m_buffer.isEmpty()) |
236 | skipBuffer(m_buffer.size()); // Save memory. |
237 | m_deflateFramer.didFail(); |
238 | m_hasContinuousFrame = false; |
239 | m_continuousFrameData.clear(); |
240 | if (m_client) |
241 | m_client->didReceiveMessageError(); |
242 | |
243 | if (m_handle && !m_closed) |
244 | m_handle->disconnect(); // Will call didCloseSocketStream() but maybe not synchronously. |
245 | } |
246 | |
247 | void WebSocketChannel::disconnect() |
248 | { |
249 | LOG(Network, "WebSocketChannel %p disconnect()" , this); |
250 | if (m_identifier && m_document) |
251 | InspectorInstrumentation::didCloseWebSocket(m_document.get(), m_identifier); |
252 | m_client = nullptr; |
253 | m_document = nullptr; |
254 | if (m_handle) |
255 | m_handle->disconnect(); |
256 | } |
257 | |
258 | void WebSocketChannel::suspend() |
259 | { |
260 | m_suspended = true; |
261 | } |
262 | |
263 | void WebSocketChannel::resume() |
264 | { |
265 | m_suspended = false; |
266 | if ((!m_buffer.isEmpty() || m_closed) && m_client && !m_resumeTimer.isActive()) |
267 | m_resumeTimer.startOneShot(0_s); |
268 | } |
269 | |
270 | void WebSocketChannel::didOpenSocketStream(SocketStreamHandle& handle) |
271 | { |
272 | LOG(Network, "WebSocketChannel %p didOpenSocketStream()" , this); |
273 | ASSERT(&handle == m_handle); |
274 | if (!m_document) |
275 | return; |
276 | if (m_identifier && UNLIKELY(InspectorInstrumentation::hasFrontends())) { |
277 | auto = [document = m_document] (const URL& url) -> String { |
278 | if (!document || !document->page()) |
279 | return { }; |
280 | return document->page()->cookieJar().cookieRequestHeaderFieldValue(*document, url); |
281 | }; |
282 | InspectorInstrumentation::willSendWebSocketHandshakeRequest(m_document.get(), m_identifier, m_handshake->clientHandshakeRequest(WTFMove(cookieRequestHeaderFieldValue))); |
283 | } |
284 | auto handshakeMessage = m_handshake->clientHandshakeMessage(); |
285 | Optional<CookieRequestHeaderFieldProxy> ; |
286 | if (m_allowCookies) |
287 | cookieRequestHeaderFieldProxy = CookieJar::cookieRequestHeaderFieldProxy(*m_document, m_handshake->httpURLForAuthenticationAndCookies()); |
288 | handle.sendHandshake(WTFMove(handshakeMessage), WTFMove(cookieRequestHeaderFieldProxy), [this, protectedThis = makeRef(*this)] (bool success, bool didAccessSecureCookies) { |
289 | if (!success) |
290 | fail("Failed to send WebSocket handshake." ); |
291 | |
292 | if (didAccessSecureCookies && m_document) |
293 | m_document->setSecureCookiesAccessed(); |
294 | }); |
295 | } |
296 | |
297 | void WebSocketChannel::didCloseSocketStream(SocketStreamHandle& handle) |
298 | { |
299 | LOG(Network, "WebSocketChannel %p didCloseSocketStream()" , this); |
300 | if (m_identifier && m_document) |
301 | InspectorInstrumentation::didCloseWebSocket(m_document.get(), m_identifier); |
302 | ASSERT_UNUSED(handle, &handle == m_handle || !m_handle); |
303 | m_closed = true; |
304 | if (m_closingTimer.isActive()) |
305 | m_closingTimer.stop(); |
306 | if (m_outgoingFrameQueueStatus != OutgoingFrameQueueClosed) |
307 | abortOutgoingFrameQueue(); |
308 | if (m_handle) { |
309 | m_unhandledBufferedAmount = m_handle->bufferedAmount(); |
310 | if (m_suspended) |
311 | return; |
312 | WebSocketChannelClient* client = m_client.get(); |
313 | m_client = nullptr; |
314 | m_document = nullptr; |
315 | m_handle = nullptr; |
316 | if (client) |
317 | client->didClose(m_unhandledBufferedAmount, m_receivedClosingHandshake ? WebSocketChannelClient::ClosingHandshakeComplete : WebSocketChannelClient::ClosingHandshakeIncomplete, m_closeEventCode, m_closeEventReason); |
318 | } |
319 | deref(); |
320 | } |
321 | |
322 | void WebSocketChannel::didReceiveSocketStreamData(SocketStreamHandle& handle, const char* data, size_t length) |
323 | { |
324 | LOG(Network, "WebSocketChannel %p didReceiveSocketStreamData() Received %zu bytes" , this, length); |
325 | Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference. |
326 | ASSERT(&handle == m_handle); |
327 | if (!m_document) { |
328 | return; |
329 | } |
330 | if (!length) { |
331 | handle.disconnect(); |
332 | return; |
333 | } |
334 | if (!m_client) { |
335 | m_shouldDiscardReceivedData = true; |
336 | handle.disconnect(); |
337 | return; |
338 | } |
339 | if (m_shouldDiscardReceivedData) |
340 | return; |
341 | if (!appendToBuffer(data, length)) { |
342 | m_shouldDiscardReceivedData = true; |
343 | fail("Ran out of memory while receiving WebSocket data." ); |
344 | return; |
345 | } |
346 | while (!m_suspended && m_client && !m_buffer.isEmpty()) { |
347 | if (!processBuffer()) |
348 | break; |
349 | } |
350 | } |
351 | |
352 | void WebSocketChannel::didFailToReceiveSocketStreamData(SocketStreamHandle& handle) |
353 | { |
354 | handle.disconnect(); |
355 | } |
356 | |
357 | void WebSocketChannel::didUpdateBufferedAmount(SocketStreamHandle&, size_t bufferedAmount) |
358 | { |
359 | if (m_client) |
360 | m_client->didUpdateBufferedAmount(bufferedAmount); |
361 | } |
362 | |
363 | void WebSocketChannel::didFailSocketStream(SocketStreamHandle& handle, const SocketStreamError& error) |
364 | { |
365 | LOG(Network, "WebSocketChannel %p didFailSocketStream()" , this); |
366 | ASSERT(&handle == m_handle || !m_handle); |
367 | if (m_document) { |
368 | String message; |
369 | if (error.isNull()) |
370 | message = "WebSocket network error"_s ; |
371 | else if (error.localizedDescription().isNull()) |
372 | message = makeString("WebSocket network error: error code " , error.errorCode()); |
373 | else |
374 | message = "WebSocket network error: " + error.localizedDescription(); |
375 | InspectorInstrumentation::didReceiveWebSocketFrameError(m_document.get(), m_identifier, message); |
376 | m_document->addConsoleMessage(MessageSource::Network, MessageLevel::Error, message); |
377 | } |
378 | m_shouldDiscardReceivedData = true; |
379 | if (m_client) |
380 | m_client->didReceiveMessageError(); |
381 | handle.disconnect(); |
382 | } |
383 | |
384 | void WebSocketChannel::didStartLoading() |
385 | { |
386 | LOG(Network, "WebSocketChannel %p didStartLoading()" , this); |
387 | ASSERT(m_blobLoader); |
388 | ASSERT(m_blobLoaderStatus == BlobLoaderStarted); |
389 | } |
390 | |
391 | void WebSocketChannel::didReceiveData() |
392 | { |
393 | LOG(Network, "WebSocketChannel %p didReceiveData()" , this); |
394 | ASSERT(m_blobLoader); |
395 | ASSERT(m_blobLoaderStatus == BlobLoaderStarted); |
396 | } |
397 | |
398 | void WebSocketChannel::didFinishLoading() |
399 | { |
400 | LOG(Network, "WebSocketChannel %p didFinishLoading()" , this); |
401 | ASSERT(m_blobLoader); |
402 | ASSERT(m_blobLoaderStatus == BlobLoaderStarted); |
403 | m_blobLoaderStatus = BlobLoaderFinished; |
404 | processOutgoingFrameQueue(); |
405 | deref(); |
406 | } |
407 | |
408 | void WebSocketChannel::didFail(int errorCode) |
409 | { |
410 | LOG(Network, "WebSocketChannel %p didFail() errorCode=%d" , this, errorCode); |
411 | ASSERT(m_blobLoader); |
412 | ASSERT(m_blobLoaderStatus == BlobLoaderStarted); |
413 | m_blobLoader = nullptr; |
414 | m_blobLoaderStatus = BlobLoaderFailed; |
415 | fail(makeString("Failed to load Blob: error code = " , errorCode)); // FIXME: Generate human-friendly reason message. |
416 | deref(); |
417 | } |
418 | |
419 | bool WebSocketChannel::appendToBuffer(const char* data, size_t len) |
420 | { |
421 | size_t newBufferSize = m_buffer.size() + len; |
422 | if (newBufferSize < m_buffer.size()) { |
423 | LOG(Network, "WebSocketChannel %p appendToBuffer() Buffer overflow (%u bytes already in receive buffer and appending %u bytes)" , this, static_cast<unsigned>(m_buffer.size()), static_cast<unsigned>(len)); |
424 | return false; |
425 | } |
426 | m_buffer.append(data, len); |
427 | return true; |
428 | } |
429 | |
430 | void WebSocketChannel::skipBuffer(size_t len) |
431 | { |
432 | ASSERT_WITH_SECURITY_IMPLICATION(len <= m_buffer.size()); |
433 | memmove(m_buffer.data(), m_buffer.data() + len, m_buffer.size() - len); |
434 | m_buffer.shrink(m_buffer.size() - len); |
435 | } |
436 | |
437 | bool WebSocketChannel::processBuffer() |
438 | { |
439 | ASSERT(!m_suspended); |
440 | ASSERT(m_client); |
441 | ASSERT(!m_buffer.isEmpty()); |
442 | LOG(Network, "WebSocketChannel %p processBuffer() Receive buffer has %u bytes" , this, static_cast<unsigned>(m_buffer.size())); |
443 | |
444 | if (m_shouldDiscardReceivedData) |
445 | return false; |
446 | |
447 | if (m_receivedClosingHandshake) { |
448 | skipBuffer(m_buffer.size()); |
449 | return false; |
450 | } |
451 | |
452 | Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference. |
453 | |
454 | if (m_handshake->mode() == WebSocketHandshake::Incomplete) { |
455 | int = m_handshake->readServerHandshake(m_buffer.data(), m_buffer.size()); |
456 | if (headerLength <= 0) |
457 | return false; |
458 | if (m_handshake->mode() == WebSocketHandshake::Connected) { |
459 | if (m_identifier) |
460 | InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(m_document.get(), m_identifier, m_handshake->serverHandshakeResponse()); |
461 | String serverSetCookie = m_handshake->serverSetCookie(); |
462 | if (!serverSetCookie.isEmpty()) { |
463 | if (m_document && m_document->page() && m_document->page()->cookieJar().cookiesEnabled(*m_document)) |
464 | m_document->page()->cookieJar().setCookies(*m_document, m_handshake->httpURLForAuthenticationAndCookies(), serverSetCookie); |
465 | } |
466 | LOG(Network, "WebSocketChannel %p Connected" , this); |
467 | skipBuffer(headerLength); |
468 | m_client->didConnect(); |
469 | LOG(Network, "WebSocketChannel %p %u bytes remaining in m_buffer" , this, static_cast<unsigned>(m_buffer.size())); |
470 | return !m_buffer.isEmpty(); |
471 | } |
472 | ASSERT(m_handshake->mode() == WebSocketHandshake::Failed); |
473 | LOG(Network, "WebSocketChannel %p Connection failed" , this); |
474 | skipBuffer(headerLength); |
475 | m_shouldDiscardReceivedData = true; |
476 | fail(m_handshake->failureReason()); |
477 | return false; |
478 | } |
479 | if (m_handshake->mode() != WebSocketHandshake::Connected) |
480 | return false; |
481 | |
482 | return processFrame(); |
483 | } |
484 | |
485 | void WebSocketChannel::resumeTimerFired() |
486 | { |
487 | Ref<WebSocketChannel> protectedThis(*this); // The client can close the channel, potentially removing the last reference. |
488 | while (!m_suspended && m_client && !m_buffer.isEmpty()) |
489 | if (!processBuffer()) |
490 | break; |
491 | if (!m_suspended && m_client && m_closed && m_handle) |
492 | didCloseSocketStream(*m_handle); |
493 | } |
494 | |
495 | void WebSocketChannel::startClosingHandshake(int code, const String& reason) |
496 | { |
497 | LOG(Network, "WebSocketChannel %p startClosingHandshake() code=%d m_receivedClosingHandshake=%d" , this, m_closing, m_receivedClosingHandshake); |
498 | ASSERT(!m_closed); |
499 | if (m_closing) |
500 | return; |
501 | ASSERT(m_handle); |
502 | |
503 | Vector<char> buf; |
504 | if (!m_receivedClosingHandshake && code != CloseEventCodeNotSpecified) { |
505 | unsigned char highByte = code >> 8; |
506 | unsigned char lowByte = code; |
507 | buf.append(static_cast<char>(highByte)); |
508 | buf.append(static_cast<char>(lowByte)); |
509 | auto reasonUTF8 = reason.utf8(); |
510 | buf.append(reasonUTF8.data(), reasonUTF8.length()); |
511 | } |
512 | enqueueRawFrame(WebSocketFrame::OpCodeClose, buf.data(), buf.size()); |
513 | Ref<WebSocketChannel> protectedThis(*this); // An attempt to send closing handshake may fail, which will get the channel closed and dereferenced. |
514 | processOutgoingFrameQueue(); |
515 | |
516 | if (m_closed) { |
517 | // The channel got closed because processOutgoingFrameQueue() failed. |
518 | return; |
519 | } |
520 | |
521 | m_closing = true; |
522 | if (m_client) |
523 | m_client->didStartClosingHandshake(); |
524 | } |
525 | |
526 | void WebSocketChannel::closingTimerFired() |
527 | { |
528 | LOG(Network, "WebSocketChannel %p closingTimerFired()" , this); |
529 | if (m_handle) |
530 | m_handle->disconnect(); |
531 | } |
532 | |
533 | |
534 | bool WebSocketChannel::processFrame() |
535 | { |
536 | ASSERT(!m_buffer.isEmpty()); |
537 | |
538 | WebSocketFrame frame; |
539 | const char* frameEnd; |
540 | String errorString; |
541 | WebSocketFrame::ParseFrameResult result = WebSocketFrame::parseFrame(m_buffer.data(), m_buffer.size(), frame, frameEnd, errorString); |
542 | if (result == WebSocketFrame::FrameIncomplete) |
543 | return false; |
544 | if (result == WebSocketFrame::FrameError) { |
545 | fail(errorString); |
546 | return false; |
547 | } |
548 | |
549 | ASSERT(m_buffer.data() < frameEnd); |
550 | ASSERT(frameEnd <= m_buffer.data() + m_buffer.size()); |
551 | |
552 | auto inflateResult = m_deflateFramer.inflate(frame); |
553 | if (!inflateResult->succeeded()) { |
554 | fail(inflateResult->failureReason()); |
555 | return false; |
556 | } |
557 | |
558 | // Validate the frame data. |
559 | if (WebSocketFrame::isReservedOpCode(frame.opCode)) { |
560 | fail(makeString("Unrecognized frame opcode: " , static_cast<unsigned>(frame.opCode))); |
561 | return false; |
562 | } |
563 | |
564 | if (frame.reserved2 || frame.reserved3) { |
565 | fail(makeString("One or more reserved bits are on: reserved2 = " , static_cast<unsigned>(frame.reserved2), ", reserved3 = " , static_cast<unsigned>(frame.reserved3))); |
566 | return false; |
567 | } |
568 | |
569 | if (frame.masked) { |
570 | fail("A server must not mask any frames that it sends to the client." ); |
571 | return false; |
572 | } |
573 | |
574 | // All control frames must not be fragmented. |
575 | if (WebSocketFrame::isControlOpCode(frame.opCode) && !frame.final) { |
576 | fail(makeString("Received fragmented control frame: opcode = " , static_cast<unsigned>(frame.opCode))); |
577 | return false; |
578 | } |
579 | |
580 | // All control frames must have a payload of 125 bytes or less, which means the frame must not contain |
581 | // the "extended payload length" field. |
582 | if (WebSocketFrame::isControlOpCode(frame.opCode) && WebSocketFrame::needsExtendedLengthField(frame.payloadLength)) { |
583 | fail(makeString("Received control frame having too long payload: " , frame.payloadLength, " bytes" )); |
584 | return false; |
585 | } |
586 | |
587 | // A new data frame is received before the previous continuous frame finishes. |
588 | // Note that control frames are allowed to come in the middle of continuous frames. |
589 | if (m_hasContinuousFrame && frame.opCode != WebSocketFrame::OpCodeContinuation && !WebSocketFrame::isControlOpCode(frame.opCode)) { |
590 | fail("Received new data frame but previous continuous frame is unfinished." ); |
591 | return false; |
592 | } |
593 | |
594 | InspectorInstrumentation::didReceiveWebSocketFrame(m_document.get(), m_identifier, frame); |
595 | |
596 | switch (frame.opCode) { |
597 | case WebSocketFrame::OpCodeContinuation: |
598 | // An unexpected continuation frame is received without any leading frame. |
599 | if (!m_hasContinuousFrame) { |
600 | fail("Received unexpected continuation frame." ); |
601 | return false; |
602 | } |
603 | m_continuousFrameData.append(frame.payload, frame.payloadLength); |
604 | skipBuffer(frameEnd - m_buffer.data()); |
605 | if (frame.final) { |
606 | // onmessage handler may eventually call the other methods of this channel, |
607 | // so we should pretend that we have finished to read this frame and |
608 | // make sure that the member variables are in a consistent state before |
609 | // the handler is invoked. |
610 | Vector<uint8_t> continuousFrameData = WTFMove(m_continuousFrameData); |
611 | m_hasContinuousFrame = false; |
612 | if (m_continuousFrameOpCode == WebSocketFrame::OpCodeText) { |
613 | String message; |
614 | if (continuousFrameData.size()) |
615 | message = String::fromUTF8(continuousFrameData.data(), continuousFrameData.size()); |
616 | else |
617 | message = emptyString(); |
618 | if (message.isNull()) |
619 | fail("Could not decode a text frame as UTF-8." ); |
620 | else |
621 | m_client->didReceiveMessage(message); |
622 | } else if (m_continuousFrameOpCode == WebSocketFrame::OpCodeBinary) |
623 | m_client->didReceiveBinaryData(WTFMove(continuousFrameData)); |
624 | } |
625 | break; |
626 | |
627 | case WebSocketFrame::OpCodeText: |
628 | if (frame.final) { |
629 | String message; |
630 | if (frame.payloadLength) |
631 | message = String::fromUTF8(frame.payload, frame.payloadLength); |
632 | else |
633 | message = emptyString(); |
634 | skipBuffer(frameEnd - m_buffer.data()); |
635 | if (message.isNull()) |
636 | fail("Could not decode a text frame as UTF-8." ); |
637 | else |
638 | m_client->didReceiveMessage(message); |
639 | } else { |
640 | m_hasContinuousFrame = true; |
641 | m_continuousFrameOpCode = WebSocketFrame::OpCodeText; |
642 | ASSERT(m_continuousFrameData.isEmpty()); |
643 | m_continuousFrameData.append(frame.payload, frame.payloadLength); |
644 | skipBuffer(frameEnd - m_buffer.data()); |
645 | } |
646 | break; |
647 | |
648 | case WebSocketFrame::OpCodeBinary: |
649 | if (frame.final) { |
650 | Vector<uint8_t> binaryData(frame.payloadLength); |
651 | memcpy(binaryData.data(), frame.payload, frame.payloadLength); |
652 | skipBuffer(frameEnd - m_buffer.data()); |
653 | m_client->didReceiveBinaryData(WTFMove(binaryData)); |
654 | } else { |
655 | m_hasContinuousFrame = true; |
656 | m_continuousFrameOpCode = WebSocketFrame::OpCodeBinary; |
657 | ASSERT(m_continuousFrameData.isEmpty()); |
658 | m_continuousFrameData.append(frame.payload, frame.payloadLength); |
659 | skipBuffer(frameEnd - m_buffer.data()); |
660 | } |
661 | break; |
662 | |
663 | case WebSocketFrame::OpCodeClose: |
664 | if (!frame.payloadLength) |
665 | m_closeEventCode = CloseEventCodeNoStatusRcvd; |
666 | else if (frame.payloadLength == 1) { |
667 | m_closeEventCode = CloseEventCodeAbnormalClosure; |
668 | fail("Received a broken close frame containing an invalid size body." ); |
669 | return false; |
670 | } else { |
671 | unsigned char highByte = static_cast<unsigned char>(frame.payload[0]); |
672 | unsigned char lowByte = static_cast<unsigned char>(frame.payload[1]); |
673 | m_closeEventCode = highByte << 8 | lowByte; |
674 | if (m_closeEventCode == CloseEventCodeNoStatusRcvd || m_closeEventCode == CloseEventCodeAbnormalClosure || m_closeEventCode == CloseEventCodeTLSHandshake) { |
675 | m_closeEventCode = CloseEventCodeAbnormalClosure; |
676 | fail("Received a broken close frame containing a reserved status code." ); |
677 | return false; |
678 | } |
679 | } |
680 | if (frame.payloadLength >= 3) |
681 | m_closeEventReason = String::fromUTF8(&frame.payload[2], frame.payloadLength - 2); |
682 | else |
683 | m_closeEventReason = emptyString(); |
684 | skipBuffer(frameEnd - m_buffer.data()); |
685 | m_receivedClosingHandshake = true; |
686 | startClosingHandshake(m_closeEventCode, m_closeEventReason); |
687 | if (m_closing) { |
688 | if (m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen) |
689 | m_outgoingFrameQueueStatus = OutgoingFrameQueueClosing; |
690 | processOutgoingFrameQueue(); |
691 | } |
692 | break; |
693 | |
694 | case WebSocketFrame::OpCodePing: |
695 | enqueueRawFrame(WebSocketFrame::OpCodePong, frame.payload, frame.payloadLength); |
696 | skipBuffer(frameEnd - m_buffer.data()); |
697 | processOutgoingFrameQueue(); |
698 | break; |
699 | |
700 | case WebSocketFrame::OpCodePong: |
701 | // A server may send a pong in response to our ping, or an unsolicited pong which is not associated with |
702 | // any specific ping. Either way, there's nothing to do on receipt of pong. |
703 | skipBuffer(frameEnd - m_buffer.data()); |
704 | break; |
705 | |
706 | default: |
707 | ASSERT_NOT_REACHED(); |
708 | skipBuffer(frameEnd - m_buffer.data()); |
709 | break; |
710 | } |
711 | |
712 | return !m_buffer.isEmpty(); |
713 | } |
714 | |
715 | void WebSocketChannel::enqueueTextFrame(const CString& string) |
716 | { |
717 | ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); |
718 | auto frame = std::make_unique<QueuedFrame>(); |
719 | frame->opCode = WebSocketFrame::OpCodeText; |
720 | frame->frameType = QueuedFrameTypeString; |
721 | frame->stringData = string; |
722 | m_outgoingFrameQueue.append(WTFMove(frame)); |
723 | } |
724 | |
725 | void WebSocketChannel::enqueueRawFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength) |
726 | { |
727 | ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); |
728 | auto frame = std::make_unique<QueuedFrame>(); |
729 | frame->opCode = opCode; |
730 | frame->frameType = QueuedFrameTypeVector; |
731 | frame->vectorData.resize(dataLength); |
732 | if (dataLength) |
733 | memcpy(frame->vectorData.data(), data, dataLength); |
734 | m_outgoingFrameQueue.append(WTFMove(frame)); |
735 | } |
736 | |
737 | void WebSocketChannel::enqueueBlobFrame(WebSocketFrame::OpCode opCode, Blob& blob) |
738 | { |
739 | ASSERT(m_outgoingFrameQueueStatus == OutgoingFrameQueueOpen); |
740 | auto frame = std::make_unique<QueuedFrame>(); |
741 | frame->opCode = opCode; |
742 | frame->frameType = QueuedFrameTypeBlob; |
743 | frame->blobData = &blob; |
744 | m_outgoingFrameQueue.append(WTFMove(frame)); |
745 | } |
746 | |
747 | void WebSocketChannel::processOutgoingFrameQueue() |
748 | { |
749 | if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosed) |
750 | return; |
751 | |
752 | Ref<WebSocketChannel> protectedThis(*this); // Any call to fail() will get the channel closed and dereferenced. |
753 | |
754 | while (!m_outgoingFrameQueue.isEmpty()) { |
755 | auto frame = m_outgoingFrameQueue.takeFirst(); |
756 | switch (frame->frameType) { |
757 | case QueuedFrameTypeString: { |
758 | sendFrame(frame->opCode, frame->stringData.data(), frame->stringData.length(), [this, protectedThis = makeRef(*this)] (bool success) { |
759 | if (!success) |
760 | fail("Failed to send WebSocket frame." ); |
761 | }); |
762 | break; |
763 | } |
764 | |
765 | case QueuedFrameTypeVector: |
766 | sendFrame(frame->opCode, frame->vectorData.data(), frame->vectorData.size(), [this, protectedThis = makeRef(*this)] (bool success) { |
767 | if (!success) |
768 | fail("Failed to send WebSocket frame." ); |
769 | }); |
770 | break; |
771 | |
772 | case QueuedFrameTypeBlob: { |
773 | switch (m_blobLoaderStatus) { |
774 | case BlobLoaderNotStarted: |
775 | ref(); // Will be derefed after didFinishLoading() or didFail(). |
776 | ASSERT(!m_blobLoader); |
777 | ASSERT(frame->blobData); |
778 | m_blobLoader = std::make_unique<FileReaderLoader>(FileReaderLoader::ReadAsArrayBuffer, this); |
779 | m_blobLoaderStatus = BlobLoaderStarted; |
780 | m_blobLoader->start(m_document.get(), *frame->blobData); |
781 | m_outgoingFrameQueue.prepend(WTFMove(frame)); |
782 | return; |
783 | |
784 | case BlobLoaderStarted: |
785 | case BlobLoaderFailed: |
786 | m_outgoingFrameQueue.prepend(WTFMove(frame)); |
787 | return; |
788 | |
789 | case BlobLoaderFinished: { |
790 | RefPtr<ArrayBuffer> result = m_blobLoader->arrayBufferResult(); |
791 | m_blobLoader = nullptr; |
792 | m_blobLoaderStatus = BlobLoaderNotStarted; |
793 | sendFrame(frame->opCode, static_cast<const char*>(result->data()), result->byteLength(), [this, protectedThis = makeRef(*this)] (bool success) { |
794 | if (!success) |
795 | fail("Failed to send WebSocket frame." ); |
796 | }); |
797 | break; |
798 | } |
799 | } |
800 | break; |
801 | } |
802 | |
803 | default: |
804 | ASSERT_NOT_REACHED(); |
805 | break; |
806 | } |
807 | } |
808 | |
809 | ASSERT(m_outgoingFrameQueue.isEmpty()); |
810 | if (m_outgoingFrameQueueStatus == OutgoingFrameQueueClosing) { |
811 | m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed; |
812 | m_handle->close(); |
813 | } |
814 | } |
815 | |
816 | void WebSocketChannel::abortOutgoingFrameQueue() |
817 | { |
818 | m_outgoingFrameQueue.clear(); |
819 | m_outgoingFrameQueueStatus = OutgoingFrameQueueClosed; |
820 | if (m_blobLoaderStatus == BlobLoaderStarted) { |
821 | m_blobLoader->cancel(); |
822 | didFail(FileError::ABORT_ERR); |
823 | } |
824 | } |
825 | |
826 | void WebSocketChannel::sendFrame(WebSocketFrame::OpCode opCode, const char* data, size_t dataLength, WTF::Function<void(bool)> completionHandler) |
827 | { |
828 | ASSERT(m_handle); |
829 | ASSERT(!m_suspended); |
830 | |
831 | WebSocketFrame frame(opCode, true, false, true, data, dataLength); |
832 | InspectorInstrumentation::didSendWebSocketFrame(m_document.get(), m_identifier, frame); |
833 | |
834 | auto deflateResult = m_deflateFramer.deflate(frame); |
835 | if (!deflateResult->succeeded()) { |
836 | fail(deflateResult->failureReason()); |
837 | return completionHandler(false); |
838 | } |
839 | |
840 | Vector<char> frameData; |
841 | frame.makeFrameData(frameData); |
842 | |
843 | m_handle->sendData(frameData.data(), frameData.size(), WTFMove(completionHandler)); |
844 | } |
845 | |
846 | ResourceRequest WebSocketChannel::clientHandshakeRequest(Function<String(const URL&)>&& ) |
847 | { |
848 | return m_handshake->clientHandshakeRequest(WTFMove(cookieRequestHeaderFieldValue)); |
849 | } |
850 | |
851 | const ResourceResponse& WebSocketChannel::serverHandshakeResponse() const |
852 | { |
853 | return m_handshake->serverHandshakeResponse(); |
854 | } |
855 | |
856 | WebSocketHandshake::Mode WebSocketChannel::handshakeMode() const |
857 | { |
858 | return m_handshake->mode(); |
859 | } |
860 | |
861 | } // namespace WebCore |
862 | |