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