1/*
2 * Copyright (C) 2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or othe r materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebSocketChannel.h"
28
29#include "DataReference.h"
30#include "NetworkConnectionToWebProcessMessages.h"
31#include "NetworkProcessConnection.h"
32#include "NetworkSocketChannelMessages.h"
33#include "WebCoreArgumentCoders.h"
34#include "WebProcess.h"
35#include <WebCore/Document.h>
36#include <WebCore/NotImplemented.h>
37#include <WebCore/WebSocketChannel.h>
38#include <WebCore/WebSocketChannelClient.h>
39#include <pal/SessionID.h>
40
41namespace WebKit {
42
43Ref<WebSocketChannel> WebSocketChannel::create(WebCore::Document& document, WebCore::WebSocketChannelClient& client)
44{
45 return adoptRef(*new WebSocketChannel(document, client));
46}
47
48WebSocketChannel::WebSocketChannel(WebCore::Document& document, WebCore::WebSocketChannelClient& client)
49 : m_document(makeWeakPtr(document))
50 , m_client(makeWeakPtr(client))
51{
52}
53
54WebSocketChannel::~WebSocketChannel()
55{
56}
57
58IPC::Connection* WebSocketChannel::messageSenderConnection() const
59{
60 return &WebProcess::singleton().ensureNetworkProcessConnection().connection();
61}
62
63uint64_t WebSocketChannel::messageSenderDestinationID() const
64{
65 return identifier();
66}
67
68String WebSocketChannel::subprotocol()
69{
70 // FIXME: support subprotocol.
71 return emptyString();
72}
73
74String WebSocketChannel::extensions()
75{
76 // FIXME: support extensions.
77 return emptyString();
78}
79
80WebSocketChannel::ConnectStatus WebSocketChannel::connect(const URL& url, const String& protocol)
81{
82 if (!m_document)
83 return ConnectStatus::KO;
84
85 auto request = webSocketConnectRequest(*m_document, url);
86 if (!request)
87 return ConnectStatus::KO;
88
89 if (request->url() != url && m_client)
90 m_client->didUpgradeURL();
91
92 MessageSender::send(Messages::NetworkConnectionToWebProcess::CreateSocketChannel { m_document->sessionID(), *request, protocol, identifier() });
93 return ConnectStatus::OK;
94}
95
96WebSocketChannel::SendResult WebSocketChannel::send(const String& message)
97{
98 auto byteLength = message.sizeInBytes();
99 m_bufferedAmount += byteLength;
100 if (m_client)
101 m_client->didUpdateBufferedAmount(m_bufferedAmount);
102
103 CompletionHandler<void()> completionHandler = [this, protectedThis = makeRef(*this), byteLength] {
104 ASSERT(m_bufferedAmount >= byteLength);
105 m_bufferedAmount -= byteLength;
106 if (m_client)
107 m_client->didUpdateBufferedAmount(m_bufferedAmount);
108 };
109 sendWithAsyncReply(Messages::NetworkSocketChannel::SendString { message }, WTFMove(completionHandler));
110 return SendSuccess;
111}
112
113WebSocketChannel::SendResult WebSocketChannel::send(const JSC::ArrayBuffer& binaryData, unsigned byteOffset, unsigned byteLength)
114{
115 m_bufferedAmount += byteLength;
116 if (m_client)
117 m_client->didUpdateBufferedAmount(m_bufferedAmount);
118
119 CompletionHandler<void()> completionHandler = [this, protectedThis = makeRef(*this), byteLength] {
120 ASSERT(m_bufferedAmount >= byteLength);
121 m_bufferedAmount -= byteLength;
122 if (m_client)
123 m_client->didUpdateBufferedAmount(m_bufferedAmount);
124 };
125 sendWithAsyncReply(Messages::NetworkSocketChannel::SendData { IPC::DataReference { static_cast<const uint8_t*>(binaryData.data()) + byteOffset, byteLength } }, WTFMove(completionHandler));
126 return SendSuccess;
127}
128
129WebSocketChannel::SendResult WebSocketChannel::send(WebCore::Blob&)
130{
131 notImplemented();
132 return SendFail;
133}
134
135unsigned WebSocketChannel::bufferedAmount() const
136{
137 return m_bufferedAmount;
138}
139
140void WebSocketChannel::close(int code, const String& reason)
141{
142 m_isClosing = true;
143 if (m_client)
144 m_client->didStartClosingHandshake();
145
146 ASSERT(code >= 0 || code == WebCore::WebSocketChannel::CloseEventCodeNotSpecified);
147
148 MessageSender::send(Messages::NetworkSocketChannel::Close { code, reason });
149}
150
151void WebSocketChannel::fail(const String& reason)
152{
153 MessageSender::send(Messages::NetworkSocketChannel::Close { 0, reason });
154}
155
156void WebSocketChannel::disconnect()
157{
158 m_client = nullptr;
159 m_document = nullptr;
160 m_pendingTasks.clear();
161
162 MessageSender::send(Messages::NetworkSocketChannel::Close { 0, { } });
163}
164
165void WebSocketChannel::didConnect()
166{
167 if (m_isClosing)
168 return;
169
170 if (!m_client)
171 return;
172
173 if (m_isSuspended) {
174 enqueueTask([this] {
175 didConnect();
176 });
177 return;
178 }
179
180 m_client->didConnect();
181}
182
183void WebSocketChannel::didReceiveText(const String& message)
184{
185 if (m_isClosing)
186 return;
187
188 if (!m_client)
189 return;
190
191 if (m_isSuspended) {
192 enqueueTask([this, message] {
193 didReceiveText(message);
194 });
195 return;
196 }
197
198 m_client->didReceiveMessage(message);
199}
200
201void WebSocketChannel::didReceiveBinaryData(const IPC::DataReference& data)
202{
203 if (m_isClosing)
204 return;
205
206 if (!m_client)
207 return;
208
209 if (m_isSuspended) {
210 enqueueTask([this, data = data.vector()]() mutable {
211 if (!m_isClosing && m_client)
212 m_client->didReceiveBinaryData(WTFMove(data));
213 });
214 return;
215 }
216 m_client->didReceiveBinaryData(data.vector());
217}
218
219void WebSocketChannel::didClose(unsigned short code, const String& reason)
220{
221 if (!m_client)
222 return;
223
224 if (m_isSuspended) {
225 enqueueTask([this, code, reason] {
226 didClose(code, reason);
227 });
228 return;
229 }
230
231 if (code == WebCore::WebSocketChannel::CloseEventCodeNormalClosure)
232 m_client->didStartClosingHandshake();
233
234 m_client->didClose(m_bufferedAmount, (m_isClosing || code == WebCore::WebSocketChannel::CloseEventCodeNormalClosure) ? WebCore::WebSocketChannelClient::ClosingHandshakeComplete : WebCore::WebSocketChannelClient::ClosingHandshakeIncomplete, code, reason);
235}
236
237void WebSocketChannel::didFail()
238{
239 if (!m_client)
240 return;
241
242 if (m_isSuspended) {
243 enqueueTask([this] {
244 didFail();
245 });
246 return;
247 }
248
249 m_client->didReceiveMessageError();
250}
251
252void WebSocketChannel::networkProcessCrashed()
253{
254 didFail();
255}
256
257void WebSocketChannel::suspend()
258{
259 m_isSuspended = true;
260}
261
262void WebSocketChannel::resume()
263{
264 m_isSuspended = false;
265 while (!m_isSuspended && !m_pendingTasks.isEmpty())
266 m_pendingTasks.takeFirst()();
267}
268
269void WebSocketChannel::enqueueTask(Function<void()>&& task)
270{
271 m_pendingTasks.append(WTFMove(task));
272}
273
274} // namespace WebKit
275