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
56namespace WebCore {
57
58static 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)
68static 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
76static 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
85Ref<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
114SocketStreamHandleImpl::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
122SocketStreamHandleImpl::~SocketStreamHandleImpl()
123{
124 LOG(Network, "SocketStreamHandle %p delete", this);
125}
126
127void 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
142void 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
167void 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
187void 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
204void SocketStreamHandleImpl::didFail(SocketStreamError&& error)
205{
206 m_client.didFailSocketStream(*this, WTFMove(error));
207}
208
209void 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
220Optional<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
247void 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
269void 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
282void 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
291gboolean 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