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 | |
41 | namespace WebKit { |
42 | |
43 | Ref<WebSocketChannel> WebSocketChannel::create(WebCore::Document& document, WebCore::WebSocketChannelClient& client) |
44 | { |
45 | return adoptRef(*new WebSocketChannel(document, client)); |
46 | } |
47 | |
48 | WebSocketChannel::WebSocketChannel(WebCore::Document& document, WebCore::WebSocketChannelClient& client) |
49 | : m_document(makeWeakPtr(document)) |
50 | , m_client(makeWeakPtr(client)) |
51 | { |
52 | } |
53 | |
54 | WebSocketChannel::~WebSocketChannel() |
55 | { |
56 | } |
57 | |
58 | IPC::Connection* WebSocketChannel::messageSenderConnection() const |
59 | { |
60 | return &WebProcess::singleton().ensureNetworkProcessConnection().connection(); |
61 | } |
62 | |
63 | uint64_t WebSocketChannel::messageSenderDestinationID() const |
64 | { |
65 | return identifier(); |
66 | } |
67 | |
68 | String WebSocketChannel::subprotocol() |
69 | { |
70 | // FIXME: support subprotocol. |
71 | return emptyString(); |
72 | } |
73 | |
74 | String WebSocketChannel::extensions() |
75 | { |
76 | // FIXME: support extensions. |
77 | return emptyString(); |
78 | } |
79 | |
80 | WebSocketChannel::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 | |
96 | WebSocketChannel::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 | |
113 | WebSocketChannel::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 | |
129 | WebSocketChannel::SendResult WebSocketChannel::send(WebCore::Blob&) |
130 | { |
131 | notImplemented(); |
132 | return SendFail; |
133 | } |
134 | |
135 | unsigned WebSocketChannel::bufferedAmount() const |
136 | { |
137 | return m_bufferedAmount; |
138 | } |
139 | |
140 | void 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 | |
151 | void WebSocketChannel::fail(const String& reason) |
152 | { |
153 | MessageSender::send(Messages::NetworkSocketChannel::Close { 0, reason }); |
154 | } |
155 | |
156 | void 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 | |
165 | void 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 | |
183 | void 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 | |
201 | void 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 | |
219 | void 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 | |
237 | void 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 | |
252 | void WebSocketChannel::networkProcessCrashed() |
253 | { |
254 | didFail(); |
255 | } |
256 | |
257 | void WebSocketChannel::suspend() |
258 | { |
259 | m_isSuspended = true; |
260 | } |
261 | |
262 | void WebSocketChannel::resume() |
263 | { |
264 | m_isSuspended = false; |
265 | while (!m_isSuspended && !m_pendingTasks.isEmpty()) |
266 | m_pendingTasks.takeFirst()(); |
267 | } |
268 | |
269 | void WebSocketChannel::enqueueTask(Function<void()>&& task) |
270 | { |
271 | m_pendingTasks.append(WTFMove(task)); |
272 | } |
273 | |
274 | } // namespace WebKit |
275 | |