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