Title: [294932] trunk
Revision
294932
Author
don.olmst...@sony.com
Date
2022-05-26 23:23:44 -0700 (Thu, 26 May 2022)

Log Message

[Curl] Implement WebSocketTask
https://bugs.webkit.org/show_bug.cgi?id=237579

Reviewed by Alex Christensen.

Implement WebSocketTask for curl platforms. Use the SocketProvider to create the
WebSocketChannel. Update web socket expectations based on the WebSocketTask
implementation.

* LayoutTests/platform/wincairo/TestExpectations:
* Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp:
* Source/WebKit/NetworkProcess/WebSocketTask.h:
* Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.cpp:
* Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.h:
* Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.cpp: Added.
* Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.h: Added.
* Source/WebKit/PlatformPlayStation.cmake:
* Source/WebKit/PlatformWin.cmake:

Canonical link: https://commits.webkit.org/251043@main

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/platform/wincairo/TestExpectations (294931 => 294932)


--- trunk/LayoutTests/platform/wincairo/TestExpectations	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/LayoutTests/platform/wincairo/TestExpectations	2022-05-27 06:23:44 UTC (rev 294932)
@@ -886,14 +886,14 @@
 http/tests/websocket/tests/hybi/contentextensions [ Skip ]
 
 http/tests/websocket/tests/hybi/frame-lengths.html [ Pass Timeout ]
-http/tests/websocket/tests/hybi/httponly-cookie.pl [ Pass Failure ]
-http/tests/websocket/tests/hybi/inspector [ Skip ]
-http/tests/websocket/tests/hybi/secure-cookie-insecure-connection.pl [ Pass Failure ]
 http/tests/websocket/tests/hybi/secure-cookie-secure-connection.pl [ Pass Failure ]
 http/tests/websocket/tests/hybi/upgrade-simple-ws.html [ Pass Failure ]
+
+# [Win] Seeing Internal Server Error when running some http websocket tests
+# https://bugs.webkit.org/show_bug.cgi?id=237722
+http/tests/websocket/tests/hybi/websocket-allowed-setting-cookie-as-third-party.html [ Pass Failure ]
 http/tests/websocket/tests/hybi/websocket-blocked-from-setting-cookie-as-third-party.html [ Pass Failure ]
 http/tests/websocket/tests/hybi/websocket-cookie-overwrite-behavior.html [ Pass Failure ]
-http/tests/websocket/tests/hybi/workers/worker-reload.html [ Timeout Pass ]
 
 # Needs curl download support
 http/tests/workers/service/service-worker-download-async-delegates.https.html [ Failure Timeout ]
@@ -2009,7 +2009,6 @@
 http/tests/misc/webtiming-one-redirect.py [ Failure ]
 http/tests/misc/webtiming-two-redirects.py [ Failure ]
 http/tests/performance/paint-timing/performance-paint-timing-fcp-after-visually-non-empty-for-style.html [ Timeout ]
-http/tests/websocket/web-socket-loads-captured-in-per-page-domains.html [ Skip ] # Crash by assertion failure
 http/tests/xmlhttprequest/access-control-basic-allow-list-request-headers.html [ Failure ]
 http/tests/xmlhttprequest/chunked-progress-event-expectedLength.html [ Failure Timeout ]
 http/tests/xmlhttprequest/origin-header-same-origin-get-async.html [ Failure ]

Modified: trunk/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp (294931 => 294932)


--- trunk/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/Source/WebCore/Modules/websockets/ThreadableWebSocketChannel.cpp	2022-05-27 06:23:44 UTC (rev 294932)
@@ -53,7 +53,7 @@
 
 Ref<ThreadableWebSocketChannel> ThreadableWebSocketChannel::create(Document& document, WebSocketChannelClient& client, SocketProvider& provider)
 {
-#if USE(SOUP)
+#if USE(SOUP) || USE(CURL)
     auto channel = provider.createWebSocketChannel(document, client);
     ASSERT(channel);
     return channel.releaseNonNull();

Modified: trunk/Source/WebKit/NetworkProcess/WebSocketTask.h (294931 => 294932)


--- trunk/Source/WebKit/NetworkProcess/WebSocketTask.h	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/Source/WebKit/NetworkProcess/WebSocketTask.h	2022-05-27 06:23:44 UTC (rev 294932)
@@ -29,6 +29,8 @@
 #include "WebSocketTaskCocoa.h"
 #elif USE(SOUP)
 #include "WebSocketTaskSoup.h"
+#elif USE(CURL)
+#include "WebSocketTaskCurl.h"
 #else
 
 #include "DataReference.h"

Modified: trunk/Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.cpp (294931 => 294932)


--- trunk/Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.cpp	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.cpp	2022-05-27 06:23:44 UTC (rev 294932)
@@ -29,6 +29,7 @@
 #include "NetworkProcess.h"
 #include "NetworkSessionCreationParameters.h"
 #include "WebCookieManager.h"
+#include "WebSocketTaskCurl.h"
 #include <WebCore/CookieJarDB.h>
 #include <WebCore/CurlContext.h>
 #include <WebCore/NetworkStorageSession.h>
@@ -58,4 +59,9 @@
 
 }
 
+std::unique_ptr<WebSocketTask> NetworkSessionCurl::createWebSocketTask(WebPageProxyIdentifier, NetworkSocketChannel& channel, const WebCore::ResourceRequest& request, const String& protocol, const WebCore::ClientOrigin&, bool)
+{
+    return makeUnique<WebSocketTask>(channel, request, protocol);
+}
+
 } // namespace WebKit

Modified: trunk/Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.h (294931 => 294932)


--- trunk/Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.h	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/Source/WebKit/NetworkProcess/curl/NetworkSessionCurl.h	2022-05-27 06:23:44 UTC (rev 294932)
@@ -39,6 +39,9 @@
     }
     NetworkSessionCurl(NetworkProcess&, const NetworkSessionCreationParameters&);
     ~NetworkSessionCurl();
+
+private:
+    std::unique_ptr<WebSocketTask> createWebSocketTask(WebPageProxyIdentifier, NetworkSocketChannel&, const WebCore::ResourceRequest&, const String& protocol, const WebCore::ClientOrigin&, bool) final;
 };
 
 } // namespace WebKit

Added: trunk/Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.cpp (0 => 294932)


--- trunk/Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.cpp	                        (rev 0)
+++ trunk/Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.cpp	2022-05-27 06:23:44 UTC (rev 294932)
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2022 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#include "config.h"
+#include "WebSocketTaskCurl.h"
+
+#include "NetworkSession.h"
+#include "NetworkSocketChannel.h"
+#include <WebCore/CurlStreamScheduler.h>
+#include <WebCore/DeprecatedGlobalSettings.h>
+
+namespace WebKit {
+
+WebSocketTask::WebSocketTask(NetworkSocketChannel& channel, const WebCore::ResourceRequest& request, const String& protocol)
+    : m_channel(channel)
+    , m_request(request.isolatedCopy())
+    , m_protocol(protocol)
+    , m_scheduler(WebCore::CurlContext::singleton().streamScheduler())
+{
+    if (request.url().protocolIs("wss"_s) && WebCore::DeprecatedGlobalSettings::allowsAnySSLCertificate())
+        WebCore::CurlContext::singleton().sslHandle().setIgnoreSSLErrors(true);
+
+    m_streamID = m_scheduler.createStream(request.url(), *this);
+    m_channel.didSendHandshakeRequest(WebCore::ResourceRequest(m_request));
+}
+
+WebSocketTask::~WebSocketTask()
+{
+    destructStream();
+}
+
+void WebSocketTask::sendString(const IPC::DataReference& utf8, CompletionHandler<void()>&& callback)
+{
+    if (m_state == State::Opened) {
+        if (!sendFrame(WebCore::WebSocketFrame::OpCodeText, utf8.data(), utf8.size()))
+            didFail("Failed to send WebSocket frame."_s);
+    }
+    callback();
+}
+
+void WebSocketTask::sendData(const IPC::DataReference& data, CompletionHandler<void()>&& callback)
+{
+    if (m_state == State::Opened) {
+        if (!sendFrame(WebCore::WebSocketFrame::OpCodeBinary, data.data(), data.size()))
+            didFail("Failed to send WebSocket frame."_s);
+    }
+    callback();
+}
+
+void WebSocketTask::close(int32_t code, const String& reason)
+{
+    if (m_state == State::Closed)
+        return;
+
+    if (m_state == State::Connecting || m_state == State::Handshaking) {
+        didClose(WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeAbnormalClosure, { });
+        return;
+    }
+
+    sendClosingHandshakeIfNeeded(code, reason);
+}
+
+void WebSocketTask::cancel()
+{
+
+}
+
+void WebSocketTask::resume()
+{
+
+}
+
+void WebSocketTask::didOpen(WebCore::CurlStreamID)
+{
+    if (m_state != State::Connecting)
+        return;
+
+    m_state = State::Handshaking;
+
+    m_handshake = makeUnique<WebCore::WebSocketHandshake>(m_request.url(), m_protocol, m_request.httpUserAgent(), m_request.httpHeaderField(WebCore::HTTPHeaderName::Origin), m_request.allowCookies(), false);
+    m_handshake->reset();
+    m_handshake->addExtensionProcessor(m_deflateFramer.createExtensionProcessor());
+
+    CString cookieHeader;
+
+    if (m_request.allowCookies()) {
+        auto includeSecureCookies = m_request.url().protocolIs("wss"_s) ? WebCore::IncludeSecureCookies::Yes : WebCore::IncludeSecureCookies::No;
+        auto cookieHeaderField = m_channel.session()->networkStorageSession()->cookieRequestHeaderFieldValue(m_request.firstPartyForCookies(), WebCore::SameSiteInfo::create(m_request), m_request.url(), std::nullopt, std::nullopt, includeSecureCookies, WebCore::ShouldAskITP::Yes, WebCore::ShouldRelaxThirdPartyCookieBlocking::No).first;
+        if (!cookieHeaderField.isEmpty())
+            cookieHeader = makeString("Cookie: "_s, cookieHeaderField, "\r\n"_s).utf8();
+    }
+
+    auto originalMessage = m_handshake->clientHandshakeMessage();
+    auto handshakeMessageLength = originalMessage.length() + cookieHeader.length();
+    auto handshakeMessage = makeUniqueArray<uint8_t>(handshakeMessageLength);
+
+    memcpy(handshakeMessage.get(), originalMessage.data(), originalMessage.length());
+    if (!cookieHeader.isNull() && cookieHeader.length()) {
+        memcpy(handshakeMessage.get() + originalMessage.length() - 2, cookieHeader.data(), cookieHeader.length());
+        memcpy(handshakeMessage.get() + handshakeMessageLength - 2, "\r\n", 2);
+    }
+
+    m_scheduler.send(m_streamID, WTFMove(handshakeMessage), handshakeMessageLength);
+}
+
+void WebSocketTask::didReceiveData(WebCore::CurlStreamID, const WebCore::SharedBuffer& buffer)
+{
+    if (m_state == State::Connecting || m_state == State::Closed)
+        return;
+
+    if (!buffer.size()) {
+        didClose(0, { });
+        return;
+    }
+
+    if (m_shouldDiscardReceivedData || m_receivedClosingHandshake)
+        return;
+
+    if (!appendReceivedBuffer(buffer)) {
+        didFail("Ran out of memory while receiving WebSocket data."_s);
+        return;
+    }
+
+    auto validateResult = validateOpeningHandshake();
+    if (!validateResult.has_value()) {
+        didFail(String(validateResult.error()));
+        return;
+    }
+    if (!validateResult.value())
+        return;
+
+    auto frameResult = receiveFrames([this, weakThis = WeakPtr { *this }](WebCore::WebSocketFrame::OpCode opCode, const uint8_t* data, size_t length) {
+        if (!weakThis)
+            return;
+
+        switch (opCode) {
+        case WebCore::WebSocketFrame::OpCodeText:
+            {
+                String message = length ? String::fromUTF8(data, length) : emptyString();
+                if (!message.isNull())
+                    m_channel.didReceiveText(message);
+                else
+                    didFail("Could not decode a text frame as UTF-8."_s);
+            }
+            break;
+
+        case WebCore::WebSocketFrame::OpCodeBinary:
+            m_channel.didReceiveBinaryData(reinterpret_cast<const uint8_t*>(data), length);
+            break;
+
+        case WebCore::WebSocketFrame::OpCodeClose:
+            if (!length)
+                m_closeEventCode = WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeNoStatusRcvd;
+            else if (length == 1) {
+                m_closeEventCode = WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeAbnormalClosure;
+                didFail("Received a broken close frame containing an invalid size body."_s);
+                return;
+            } else {
+                auto highByte = static_cast<unsigned char>(data[0]);
+                auto lowByte = static_cast<unsigned char>(data[1]);
+                m_closeEventCode = highByte << 8 | lowByte;
+                if (m_closeEventCode == WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeNoStatusRcvd
+                    || m_closeEventCode == WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeAbnormalClosure
+                    || m_closeEventCode == WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeTLSHandshake) {
+                    m_closeEventCode = WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeAbnormalClosure;
+                    didFail("Received a broken close frame containing a reserved status code."_s);
+                    return;
+                }
+            }
+            if (length >= 3)
+                m_closeEventReason = String::fromUTF8(&data[2], length - 2);
+            else
+                m_closeEventReason = emptyString();
+
+            m_receivedClosingHandshake = true;
+            sendClosingHandshakeIfNeeded(m_closeEventCode, m_closeEventReason);
+            didClose(m_closeEventCode, m_closeEventReason);
+            break;
+
+        case WebCore::WebSocketFrame::OpCodePing:
+            if (!sendFrame(WebCore::WebSocketFrame::OpCodePong, data, length))
+                didFail("Failed to send WebSocket frame."_s);
+            break;
+        }
+    });
+
+    if (frameResult) {
+        didFail(String(*frameResult));
+        return;
+    }
+}
+
+void WebSocketTask::didFail(WebCore::CurlStreamID, CURLcode errorCode)
+{
+    didFail(makeString("WebSocket network error: error code "_s, static_cast<uint32_t>(errorCode)));
+}
+
+bool WebSocketTask::appendReceivedBuffer(const WebCore::SharedBuffer& buffer)
+{
+    size_t newBufferSize = m_receiveBuffer.size() + buffer.size();
+    if (newBufferSize < m_receiveBuffer.size())
+        return false;
+
+    m_receiveBuffer.append(buffer.data(), buffer.size());
+    return true;
+}
+
+void WebSocketTask::skipReceivedBuffer(size_t length)
+{
+    memmove(m_receiveBuffer.data(), m_receiveBuffer.data() + length, m_receiveBuffer.size() - length);
+    m_receiveBuffer.shrink(m_receiveBuffer.size() - length);
+}
+
+Expected<bool, String> WebSocketTask::validateOpeningHandshake()
+{
+    if (m_didCompleteOpeningHandshake)
+        return true;
+
+    if (m_state != State::Handshaking || !m_handshake || m_handshake->mode() != WebCore::WebSocketHandshake::Incomplete) {
+        m_handshake = nullptr;
+        return makeUnexpected("Unexpected handshakeing condition"_s);
+    }
+
+    auto headerLength = m_handshake->readServerHandshake(m_receiveBuffer.data(), m_receiveBuffer.size());
+    if (headerLength <= 0)
+        return false;
+
+    skipReceivedBuffer(headerLength);
+
+    if (m_handshake->mode() != WebCore::WebSocketHandshake::Connected) {
+        auto reason = m_handshake->failureReason();
+        m_handshake = nullptr;
+        return makeUnexpected(reason);
+    }
+
+    auto serverSetCookie = m_handshake->serverSetCookie();
+    if (!serverSetCookie.isEmpty())
+        m_channel.session()->networkStorageSession()->setCookiesFromHTTPResponse(m_request.firstPartyForCookies(), m_request.url(), serverSetCookie);
+
+    m_state = State::Opened;
+    m_didCompleteOpeningHandshake = true;
+
+    m_channel.didConnect(m_handshake->serverWebSocketProtocol(), m_handshake->acceptedExtensions());
+    m_channel.didReceiveHandshakeResponse(WebCore::ResourceResponse(m_handshake->serverHandshakeResponse()));
+
+    m_handshake = nullptr;
+    return true;
+}
+
+std::optional<String> WebSocketTask::receiveFrames(Function<void(WebCore::WebSocketFrame::OpCode, const uint8_t*, size_t)>&& callback)
+{
+    if (m_state != State::Opened && m_state != State::Closing)
+        return std::nullopt;
+
+    while (m_receiveBuffer.size() && !m_shouldDiscardReceivedData && !m_receivedClosingHandshake) {
+        WebCore::WebSocketFrame frame;
+        const uint8_t* frameEnd;
+        String errorString;
+        auto parseResult = WebCore::WebSocketFrame::parseFrame(reinterpret_cast<uint8_t*>(m_receiveBuffer.data()), m_receiveBuffer.size(), frame, frameEnd, errorString);
+        if (parseResult == WebCore::WebSocketFrame::FrameIncomplete)
+            return std::nullopt;
+        if (parseResult == WebCore::WebSocketFrame::FrameError)
+            return errorString;
+
+        auto inflateResult = m_deflateFramer.inflate(frame);
+        if (!inflateResult->succeeded())
+            return inflateResult->failureReason();
+
+        if (auto validateResult = validateFrame(frame))
+            return *validateResult;
+
+        if (!frame.final || frame.opCode == WebCore::WebSocketFrame::OpCodeContinuation) {
+            if (frame.opCode != WebCore::WebSocketFrame::OpCodeContinuation) {
+                m_hasContinuousFrame = true;
+                m_continuousFrameOpCode = frame.opCode;
+            }
+
+            m_continuousFrameData.append(frame.payload, frame.payloadLength);
+
+            if (frame.final) {
+                callback(m_continuousFrameOpCode, m_continuousFrameData.data(), m_continuousFrameData.size());
+                m_hasContinuousFrame = false;
+                m_continuousFrameData.clear();
+            }
+        } else
+            callback(frame.opCode, frame.payload, frame.payloadLength);
+
+        if (!m_receiveBuffer.isEmpty())
+            skipReceivedBuffer(frameEnd - m_receiveBuffer.data());
+    }
+
+    return std::nullopt;
+}
+
+std::optional<String> WebSocketTask::validateFrame(const WebCore::WebSocketFrame& frame)
+{
+    if (WebCore::WebSocketFrame::isReservedOpCode(frame.opCode))
+        return makeString("Unrecognized frame opcode: "_s, static_cast<unsigned>(frame.opCode));
+
+    if (frame.reserved2 || frame.reserved3)
+        return makeString("One or more reserved bits are on: reserved2 = "_s, static_cast<unsigned>(frame.reserved2), ", reserved3 = "_s, static_cast<unsigned>(frame.reserved3));
+
+    if (frame.masked)
+        return  "A server must not mask any frames that it sends to the client."_s;
+
+    // All control frames must not be fragmented.
+    if (WebCore::WebSocketFrame::isControlOpCode(frame.opCode) && !frame.final)
+        return makeString("Received fragmented control frame: opcode = "_s, static_cast<unsigned>(frame.opCode));
+
+    // All control frames must have a payload of 125 bytes or less, which means the frame must not contain
+    // the "extended payload length" field.
+    if (WebCore::WebSocketFrame::isControlOpCode(frame.opCode) && WebCore::WebSocketFrame::needsExtendedLengthField(frame.payloadLength))
+        return makeString("Received control frame having too long payload: "_s, frame.payloadLength, " bytes"_s);
+
+    // A new data frame is received before the previous continuous frame finishes.
+    // Note that control frames are allowed to come in the middle of continuous frames.
+    if (m_hasContinuousFrame && frame.opCode != WebCore::WebSocketFrame::OpCodeContinuation && !WebCore::WebSocketFrame::isControlOpCode(frame.opCode))
+        return "Received new data frame but previous continuous frame is unfinished."_s;
+
+    // An unexpected continuation frame is received without any leading frame.
+    if (!m_hasContinuousFrame && frame.opCode == WebCore::WebSocketFrame::OpCodeContinuation)
+        return "Received unexpected continuation frame."_s;
+
+    return std::nullopt;
+}
+
+void WebSocketTask::sendClosingHandshakeIfNeeded(int32_t code, const String& reason)
+{
+    if (m_didSendClosingHandshake)
+        return;
+
+    Vector<uint8_t> buf;
+    if (!m_receivedClosingHandshake && code != WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeNotSpecified) {
+        unsigned char highByte = static_cast<unsigned short>(code) >> 8;
+        unsigned char lowByte = static_cast<unsigned short>(code);
+        buf.append(static_cast<char>(highByte));
+        buf.append(static_cast<char>(lowByte));
+        auto reasonUTF8 = reason.utf8();
+        buf.append(reasonUTF8.data(), reasonUTF8.length());
+    }
+
+    if (!sendFrame(WebCore::WebSocketFrame::OpCodeClose, buf.data(), buf.size()))
+        didFail("Failed to send WebSocket frame."_s);
+
+    m_state = State::Closing;
+    m_didSendClosingHandshake = true;
+}
+
+bool WebSocketTask::sendFrame(WebCore::WebSocketFrame::OpCode opCode, const uint8_t* data, size_t dataLength)
+{
+    if (m_didSendClosingHandshake)
+        return true;
+
+    WebCore::WebSocketFrame frame(opCode, true, false, true, data, dataLength);
+
+    auto deflateResult = m_deflateFramer.deflate(frame);
+    if (!deflateResult->succeeded()) {
+        didFail(deflateResult->failureReason());
+        return false;
+    }
+
+    Vector<uint8_t> frameData;
+    frame.makeFrameData(frameData);
+
+    auto buffer = makeUniqueArray<uint8_t>(frameData.size());
+    memcpy(buffer.get(), frameData.data(), frameData.size());
+
+    m_scheduler.send(m_streamID, WTFMove(buffer), frameData.size());
+    return true;
+}
+
+void WebSocketTask::didFail(String&& reason)
+{
+    if (m_receivedDidFail)
+        return;
+
+    m_receivedDidFail = true;
+
+    // Hybi-10 specification explicitly states we must not continue to handle incoming data
+    // once the WebSocket connection is failed (section 7.1.7).
+    m_shouldDiscardReceivedData = true;
+    if (!m_receiveBuffer.isEmpty())
+        skipReceivedBuffer(m_receiveBuffer.size()); // Save memory.
+    m_deflateFramer.didFail();
+    m_hasContinuousFrame = false;
+    m_continuousFrameData.clear();
+
+    m_channel.didReceiveMessageError(WTFMove(reason));
+    didClose(WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeAbnormalClosure, { });
+}
+
+void WebSocketTask::didClose(int32_t code, const String& reason)
+{
+    destructStream();
+
+    if (m_state == State::Closed)
+        return;
+
+    m_state = State::Closed;
+
+    callOnMainRunLoop([this, weakThis = WeakPtr { *this }, code, reason] {
+        if (!weakThis)
+            return;
+
+        m_channel.didClose(code, reason);
+    });
+}
+
+void WebSocketTask::destructStream()
+{
+    if (isStreamInvalidated())
+        return;
+
+    m_scheduler.destroyStream(m_streamID);
+    m_streamID = WebCore::invalidCurlStreamID;
+}
+
+} // namespace WebKit

Added: trunk/Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.h (0 => 294932)


--- trunk/Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.h	                        (rev 0)
+++ trunk/Source/WebKit/NetworkProcess/curl/WebSocketTaskCurl.h	2022-05-27 06:23:44 UTC (rev 294932)
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 Sony Interactive Entertainment Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#pragma once
+
+#include "DataReference.h"
+#include "WebSocketTask.h"
+#include <WebCore/CurlStream.h>
+#include <WebCore/ResourceRequest.h>
+#include <WebCore/WebSocketChannel.h>
+#include <WebCore/WebSocketDeflateFramer.h>
+
+namespace WebCore {
+class CurlStreamScheduler;
+class SharedBuffer;
+}
+
+namespace WebKit {
+
+class NetworkSocketChannel;
+struct SessionSet;
+
+class WebSocketTask : public CanMakeWeakPtr<WebSocketTask>, public WebCore::CurlStream::Client {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    WebSocketTask(NetworkSocketChannel&, const WebCore::ResourceRequest&, const String& protocol);
+    ~WebSocketTask();
+
+    void sendString(const IPC::DataReference&, CompletionHandler<void()>&&);
+    void sendData(const IPC::DataReference&, CompletionHandler<void()>&&);
+    void close(int32_t code, const String& reason);
+
+    void cancel();
+    void resume();
+
+    SessionSet* sessionSet() { return nullptr; }
+
+private:
+    enum class State : uint8_t {
+        Connecting,
+        Handshaking,
+        Opened,
+        Closing,
+        Closed
+    };
+
+    void didOpen(WebCore::CurlStreamID) final;
+    void didSendData(WebCore::CurlStreamID, size_t) final { };
+    void didReceiveData(WebCore::CurlStreamID, const WebCore::SharedBuffer&) final;
+    void didFail(WebCore::CurlStreamID, CURLcode) final;
+
+    bool appendReceivedBuffer(const WebCore::SharedBuffer&);
+    void skipReceivedBuffer(size_t len);
+
+    Expected<bool, String> validateOpeningHandshake();
+    std::optional<String> receiveFrames(Function<void(WebCore::WebSocketFrame::OpCode, const uint8_t*, size_t)>&&);
+    std::optional<String> validateFrame(const WebCore::WebSocketFrame&);
+
+    bool sendFrame(WebCore::WebSocketFrame::OpCode, const uint8_t* data, size_t dataLength);
+    void sendClosingHandshakeIfNeeded(int32_t, const String& reason);
+
+    void didFail(String&& reason);
+    void didClose(int32_t code, const String& reason);
+
+    bool isStreamInvalidated() { return m_streamID == WebCore::invalidCurlStreamID; }
+    void destructStream();
+
+    NetworkSocketChannel& m_channel;
+    WebCore::ResourceRequest m_request;
+    String m_protocol;
+
+    WebCore::CurlStreamScheduler& m_scheduler;
+    WebCore::CurlStreamID m_streamID { WebCore::invalidCurlStreamID };
+
+    State m_state { State::Connecting };
+
+    std::unique_ptr<WebCore::WebSocketHandshake> m_handshake;
+    WebCore::WebSocketDeflateFramer m_deflateFramer;
+
+    bool m_didCompleteOpeningHandshake { false };
+
+    bool m_shouldDiscardReceivedData { false };
+    Vector<uint8_t> m_receiveBuffer;
+
+    bool m_hasContinuousFrame { false };
+    WebCore::WebSocketFrame::OpCode m_continuousFrameOpCode { WebCore::WebSocketFrame::OpCode::OpCodeInvalid };
+    Vector<uint8_t> m_continuousFrameData;
+
+    bool m_receivedClosingHandshake { false };
+    int32_t m_closeEventCode { WebCore::WebSocketChannel::CloseEventCode::CloseEventCodeNotSpecified };
+    String m_closeEventReason;
+
+    bool m_didSendClosingHandshake { false };
+    bool m_receivedDidFail { false };
+};
+
+} // namespace WebKit

Modified: trunk/Source/WebKit/PlatformPlayStation.cmake (294931 => 294932)


--- trunk/Source/WebKit/PlatformPlayStation.cmake	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/Source/WebKit/PlatformPlayStation.cmake	2022-05-27 06:23:44 UTC (rev 294932)
@@ -46,6 +46,7 @@
     NetworkProcess/curl/NetworkProcessCurl.cpp
     NetworkProcess/curl/NetworkProcessMainCurl.cpp
     NetworkProcess/curl/NetworkSessionCurl.cpp
+    NetworkProcess/curl/WebSocketTaskCurl.cpp
 
     Platform/IPC/unix/AttachmentUnix.cpp
     Platform/IPC/unix/ConnectionUnix.cpp

Modified: trunk/Source/WebKit/PlatformWin.cmake (294931 => 294932)


--- trunk/Source/WebKit/PlatformWin.cmake	2022-05-27 05:47:49 UTC (rev 294931)
+++ trunk/Source/WebKit/PlatformWin.cmake	2022-05-27 06:23:44 UTC (rev 294932)
@@ -162,6 +162,7 @@
         NetworkProcess/curl/NetworkProcessCurl.cpp
         NetworkProcess/curl/NetworkProcessMainCurl.cpp
         NetworkProcess/curl/NetworkSessionCurl.cpp
+        NetworkProcess/curl/WebSocketTaskCurl.cpp
 
         Shared/API/c/cairo/WKImageCairo.cpp
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to