1 | /* |
2 | * Copyright (C) 2009, 2011 Google Inc. All rights reserved. |
3 | * Copyright (C) 2012 Samsung Electronics Ltd. 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 "SocketStreamHandleImpl.h" |
34 | |
35 | #if USE(SOUP) |
36 | |
37 | #include "DeprecatedGlobalSettings.h" |
38 | #include "Logging.h" |
39 | #include "NetworkStorageSession.h" |
40 | #include "ResourceError.h" |
41 | #include "SocketStreamError.h" |
42 | #include "SocketStreamHandleClient.h" |
43 | #include "SoupNetworkSession.h" |
44 | #include "StorageSessionProvider.h" |
45 | #include "URLSoup.h" |
46 | #include <gio/gio.h> |
47 | #include <glib.h> |
48 | #include <wtf/URL.h> |
49 | #include <wtf/Vector.h> |
50 | #include <wtf/glib/GUniquePtr.h> |
51 | #include <wtf/glib/RunLoopSourcePriority.h> |
52 | #include <wtf/text/CString.h> |
53 | |
54 | #define READ_BUFFER_SIZE 1024 |
55 | |
56 | namespace WebCore { |
57 | |
58 | static gboolean acceptCertificateCallback(GTlsConnection*, GTlsCertificate* certificate, GTlsCertificateFlags errors, SocketStreamHandleImpl* handle) |
59 | { |
60 | // FIXME: Using DeprecatedGlobalSettings from here is a layering violation. |
61 | if (DeprecatedGlobalSettings::allowsAnySSLCertificate()) |
62 | return TRUE; |
63 | |
64 | return !SoupNetworkSession::checkTLSErrors(handle->url(), certificate, errors); |
65 | } |
66 | |
67 | #if SOUP_CHECK_VERSION(2, 61, 90) |
68 | static void connectProgressCallback(SoupSession*, GSocketClientEvent event, GIOStream* connection, SocketStreamHandleImpl* handle) |
69 | { |
70 | if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING) |
71 | return; |
72 | |
73 | g_signal_connect(connection, "accept-certificate" , G_CALLBACK(acceptCertificateCallback), handle); |
74 | } |
75 | #else |
76 | static void socketClientEventCallback(GSocketClient*, GSocketClientEvent event, GSocketConnectable*, GIOStream* connection, SocketStreamHandleImpl* handle) |
77 | { |
78 | if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING) |
79 | return; |
80 | |
81 | g_signal_connect(connection, "accept-certificate" , G_CALLBACK(acceptCertificateCallback), handle); |
82 | } |
83 | #endif |
84 | |
85 | Ref<SocketStreamHandleImpl> SocketStreamHandleImpl::create(const URL& url, SocketStreamHandleClient& client, PAL::SessionID, const String&, SourceApplicationAuditToken&&, const StorageSessionProvider* storageSessionProvider) |
86 | { |
87 | Ref<SocketStreamHandleImpl> socket = adoptRef(*new SocketStreamHandleImpl(url, client, storageSessionProvider)); |
88 | |
89 | #if SOUP_CHECK_VERSION(2, 61, 90) |
90 | auto* networkStorageSession = storageSessionProvider ? storageSessionProvider->storageSession() : nullptr; |
91 | if (!networkStorageSession) |
92 | return socket; |
93 | |
94 | auto uri = urlToSoupURI(url); |
95 | Ref<SocketStreamHandle> protectedSocketStreamHandle = socket.copyRef(); |
96 | soup_session_connect_async(networkStorageSession->soupNetworkSession().soupSession(), uri.get(), socket->m_cancellable.get(), |
97 | url.protocolIs("wss" ) ? reinterpret_cast<SoupSessionConnectProgressCallback>(connectProgressCallback) : nullptr, |
98 | reinterpret_cast<GAsyncReadyCallback>(connectedCallback), &protectedSocketStreamHandle.leakRef()); |
99 | #else |
100 | unsigned port = url.port() ? url.port().value() : (url.protocolIs("wss" ) ? 443 : 80); |
101 | GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); |
102 | if (url.protocolIs("wss" )) { |
103 | g_socket_client_set_tls(socketClient.get(), TRUE); |
104 | g_signal_connect(socketClient.get(), "event" , G_CALLBACK(socketClientEventCallback), socket.ptr()); |
105 | } |
106 | Ref<SocketStreamHandle> protectedSocketStreamHandle = socket.copyRef(); |
107 | g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, socket->m_cancellable.get(), |
108 | reinterpret_cast<GAsyncReadyCallback>(connectedCallback), &protectedSocketStreamHandle.leakRef()); |
109 | #endif |
110 | |
111 | return socket; |
112 | } |
113 | |
114 | SocketStreamHandleImpl::SocketStreamHandleImpl(const URL& url, SocketStreamHandleClient& client, const StorageSessionProvider* provider) |
115 | : SocketStreamHandle(url, client) |
116 | , m_storageSessionProvider(provider) |
117 | , m_cancellable(adoptGRef(g_cancellable_new())) |
118 | { |
119 | LOG(Network, "SocketStreamHandle %p new client %p" , this, &m_client); |
120 | } |
121 | |
122 | SocketStreamHandleImpl::~SocketStreamHandleImpl() |
123 | { |
124 | LOG(Network, "SocketStreamHandle %p delete" , this); |
125 | } |
126 | |
127 | void SocketStreamHandleImpl::connected(GRefPtr<GIOStream>&& stream) |
128 | { |
129 | m_stream = WTFMove(stream); |
130 | m_outputStream = G_POLLABLE_OUTPUT_STREAM(g_io_stream_get_output_stream(m_stream.get())); |
131 | m_inputStream = g_io_stream_get_input_stream(m_stream.get()); |
132 | m_readBuffer = makeUniqueArray<char>(READ_BUFFER_SIZE); |
133 | |
134 | RefPtr<SocketStreamHandleImpl> protectedThis(this); |
135 | g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(), |
136 | reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), protectedThis.leakRef()); |
137 | |
138 | m_state = Open; |
139 | m_client.didOpenSocketStream(*this); |
140 | } |
141 | |
142 | void SocketStreamHandleImpl::connectedCallback(GObject* object, GAsyncResult* result, SocketStreamHandleImpl* handle) |
143 | { |
144 | RefPtr<SocketStreamHandle> protectedThis = adoptRef(handle); |
145 | |
146 | // Always finish the connection, even if this SocketStreamHandle was cancelled earlier. |
147 | GUniqueOutPtr<GError> error; |
148 | #if SOUP_CHECK_VERSION(2, 61, 90) |
149 | GRefPtr<GIOStream> stream = adoptGRef(soup_session_connect_finish(SOUP_SESSION(object), result, &error.outPtr())); |
150 | #else |
151 | GRefPtr<GIOStream> stream = adoptGRef(G_IO_STREAM(g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(object), result, &error.outPtr()))); |
152 | #endif |
153 | |
154 | // The SocketStreamHandle has been cancelled, so just close the connection, ignoring errors. |
155 | if (g_cancellable_is_cancelled(handle->m_cancellable.get())) { |
156 | if (stream) |
157 | g_io_stream_close(stream.get(), nullptr, nullptr); |
158 | return; |
159 | } |
160 | |
161 | if (error) |
162 | handle->didFail(SocketStreamError(error->code, { }, error->message)); |
163 | else |
164 | handle->connected(WTFMove(stream)); |
165 | } |
166 | |
167 | void SocketStreamHandleImpl::readBytes(gssize bytesRead) |
168 | { |
169 | if (!bytesRead) { |
170 | close(); |
171 | return; |
172 | } |
173 | |
174 | // The client can close the handle, potentially removing the last reference. |
175 | RefPtr<SocketStreamHandle> protectedThis(this); |
176 | if (bytesRead == -1) |
177 | m_client.didFailToReceiveSocketStreamData(*this); |
178 | else |
179 | m_client.didReceiveSocketStreamData(*this, m_readBuffer.get(), static_cast<size_t>(bytesRead)); |
180 | |
181 | if (m_inputStream) { |
182 | g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, RunLoopSourcePriority::AsyncIONetwork, m_cancellable.get(), |
183 | reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), protectedThis.leakRef()); |
184 | } |
185 | } |
186 | |
187 | void SocketStreamHandleImpl::readReadyCallback(GInputStream* stream, GAsyncResult* result, SocketStreamHandleImpl* handle) |
188 | { |
189 | RefPtr<SocketStreamHandle> protectedThis = adoptRef(handle); |
190 | |
191 | // Always finish the read, even if this SocketStreamHandle was cancelled earlier. |
192 | GUniqueOutPtr<GError> error; |
193 | gssize bytesRead = g_input_stream_read_finish(stream, result, &error.outPtr()); |
194 | |
195 | if (g_cancellable_is_cancelled(handle->m_cancellable.get())) |
196 | return; |
197 | |
198 | if (error) |
199 | handle->didFail(SocketStreamError(error->code, String(), error->message)); |
200 | else |
201 | handle->readBytes(bytesRead); |
202 | } |
203 | |
204 | void SocketStreamHandleImpl::didFail(SocketStreamError&& error) |
205 | { |
206 | m_client.didFailSocketStream(*this, WTFMove(error)); |
207 | } |
208 | |
209 | void SocketStreamHandleImpl::writeReady() |
210 | { |
211 | // We no longer have buffered data, so stop waiting for the socket to be writable. |
212 | if (!bufferedAmount()) { |
213 | stopWaitingForSocketWritability(); |
214 | return; |
215 | } |
216 | |
217 | sendPendingData(); |
218 | } |
219 | |
220 | Optional<size_t> SocketStreamHandleImpl::platformSendInternal(const uint8_t* data, size_t length) |
221 | { |
222 | LOG(Network, "SocketStreamHandle %p platformSend" , this); |
223 | if (!m_outputStream || !data) |
224 | return 0; |
225 | |
226 | GUniqueOutPtr<GError> error; |
227 | gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), reinterpret_cast<const char*>(data), length, m_cancellable.get(), &error.outPtr()); |
228 | if (error) { |
229 | if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) |
230 | beginWaitingForSocketWritability(); |
231 | else |
232 | didFail(SocketStreamError(error->code, String(), error->message)); |
233 | return WTF::nullopt; |
234 | } |
235 | |
236 | // If we did not send all the bytes we were given, we know that |
237 | // SocketStreamHandle will need to send more in the future. |
238 | if (written == -1 || static_cast<size_t>(written) < length) |
239 | beginWaitingForSocketWritability(); |
240 | |
241 | if (written == -1) |
242 | return WTF::nullopt; |
243 | |
244 | return static_cast<size_t>(written); |
245 | } |
246 | |
247 | void SocketStreamHandleImpl::platformClose() |
248 | { |
249 | LOG(Network, "SocketStreamHandle %p platformClose" , this); |
250 | // We cancel this handle first to disable all callbacks. |
251 | g_cancellable_cancel(m_cancellable.get()); |
252 | stopWaitingForSocketWritability(); |
253 | |
254 | if (m_stream) { |
255 | GUniqueOutPtr<GError> error; |
256 | g_io_stream_close(m_stream.get(), nullptr, &error.outPtr()); |
257 | if (error) |
258 | didFail(SocketStreamError(error->code, { }, error->message)); |
259 | m_stream = nullptr; |
260 | } |
261 | |
262 | m_outputStream = nullptr; |
263 | m_inputStream = nullptr; |
264 | m_readBuffer = nullptr; |
265 | |
266 | m_client.didCloseSocketStream(*this); |
267 | } |
268 | |
269 | void SocketStreamHandleImpl::beginWaitingForSocketWritability() |
270 | { |
271 | if (m_writeReadySource) // Already waiting. |
272 | return; |
273 | |
274 | m_writeReadySource = adoptGRef(g_pollable_output_stream_create_source(m_outputStream.get(), m_cancellable.get())); |
275 | ref(); |
276 | g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(reinterpret_cast<GCallback>(writeReadyCallback)), this, [](gpointer handle) { |
277 | static_cast<SocketStreamHandleImpl*>(handle)->deref(); |
278 | }); |
279 | g_source_attach(m_writeReadySource.get(), g_main_context_get_thread_default()); |
280 | } |
281 | |
282 | void SocketStreamHandleImpl::stopWaitingForSocketWritability() |
283 | { |
284 | if (!m_writeReadySource) // Not waiting. |
285 | return; |
286 | |
287 | g_source_destroy(m_writeReadySource.get()); |
288 | m_writeReadySource = nullptr; |
289 | } |
290 | |
291 | gboolean SocketStreamHandleImpl::writeReadyCallback(GPollableOutputStream*, SocketStreamHandleImpl* handle) |
292 | { |
293 | if (g_cancellable_is_cancelled(handle->m_cancellable.get())) |
294 | return G_SOURCE_REMOVE; |
295 | |
296 | handle->writeReady(); |
297 | return G_SOURCE_CONTINUE; |
298 | } |
299 | |
300 | } // namespace WebCore |
301 | |
302 | #endif |
303 | |