Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / platform / network / curl / SocketStreamHandleImplCurl.cpp
1 /*
2  * Copyright (C) 2009 Brent Fulgham.  All rights reserved.
3  * Copyright (C) 2009 Google Inc.  All rights reserved.
4  * Copyright (C) 2018 Sony Interactive Entertainment Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "SocketStreamHandleImpl.h"
35
36 #if USE(CURL)
37
38 #include "DeprecatedGlobalSettings.h"
39 #include "Logging.h"
40 #include "SocketStreamError.h"
41 #include "SocketStreamHandleClient.h"
42 #include <wtf/Lock.h>
43 #include <wtf/MainThread.h>
44 #include <wtf/URL.h>
45 #include <wtf/text/CString.h>
46
47 namespace WebCore {
48
49 SocketStreamHandleImpl::SocketStreamHandleImpl(const URL& url, SocketStreamHandleClient& client)
50     : SocketStreamHandle(url, client)
51 {
52     LOG(Network, "SocketStreamHandle %p new client %p", this, &m_client);
53     ASSERT(isMainThread());
54
55     // FIXME: Using DeprecatedGlobalSettings from here is a layering violation.
56     if (m_url.protocolIs("wss") && DeprecatedGlobalSettings::allowsAnySSLCertificate())
57         CurlContext::singleton().sslHandle().setIgnoreSSLErrors(true);
58
59     m_workerThread = Thread::create("WebSocket thread", [this, protectedThis = makeRef(*this)] {
60         threadEntryPoint();
61     });
62 }
63
64 SocketStreamHandleImpl::~SocketStreamHandleImpl()
65 {
66     LOG(Network, "SocketStreamHandle %p delete", this);
67     stopThread();
68 }
69
70 std::optional<size_t> SocketStreamHandleImpl::platformSendInternal(const uint8_t* data, size_t length)
71 {
72     LOG(Network, "SocketStreamHandle %p platformSend", this);
73     ASSERT(isMainThread());
74
75     // If there's data waiting, return zero to indicate whole data should be put in a m_buffer.
76     // This is thread-safe because state is read in atomic. Also even if the state is cleared by
77     // worker thread between load() and evaluation of size, it is okay because invocation of
78     // sendPendingData() is serialized in the main thread, so that another call will be happen
79     // immediately.
80     if (m_writeBufferSize.load())
81         return 0;
82
83     if (length > kWriteBufferSize)
84         length = kWriteBufferSize;
85
86     // We copy the buffer and then set the state atomically to say there are bytes available.
87     // The worker thread will skip reading the buffer if no bytes are available, so it won't
88     // access the buffer prematurely
89     m_writeBuffer = makeUniqueArray<uint8_t>(length);
90     memcpy(m_writeBuffer.get(), data, length);
91     m_writeBufferOffset = 0;
92     m_writeBufferSize.store(length);
93
94     return length;
95 }
96
97 void SocketStreamHandleImpl::platformClose()
98 {
99     LOG(Network, "SocketStreamHandle %p platformClose", this);
100     ASSERT(isMainThread());
101
102     if (m_state == Closed)
103         return;
104
105     stopThread();
106     m_client.didCloseSocketStream(*this);
107 }
108
109 void SocketStreamHandleImpl::threadEntryPoint()
110 {
111     ASSERT(!isMainThread());
112
113     CurlSocketHandle socket { m_url, [this](CURLcode errorCode) {
114         handleError(errorCode);
115     }};
116
117     // Connect to host
118     if (!socket.connect())
119         return;
120
121     callOnMainThread([this, protectedThis = makeRef(*this)] {
122         if (m_state == Connecting) {
123             m_state = Open;
124             m_client.didOpenSocketStream(*this);
125         }
126     });
127
128     while (m_running) {
129         auto writeBufferSize = m_writeBufferSize.load();
130         auto result = socket.wait(20_ms, writeBufferSize > 0);
131         if (!result)
132             continue;
133
134         // These logic only run when there's data waiting. In that case, m_writeBufferSize won't
135         // updated by `platformSendInternal()` running in main thread.
136         if (result->writable && m_running) {
137             auto offset = m_writeBufferOffset;
138             auto bytesSent = socket.send(m_writeBuffer.get() + offset, writeBufferSize - offset);
139             offset += bytesSent;
140
141             if (writeBufferSize > offset)
142                 m_writeBufferOffset = offset;
143             else {
144                 m_writeBuffer = nullptr;
145                 m_writeBufferOffset = 0;
146                 m_writeBufferSize.store(0);
147                 callOnMainThread([this, protectedThis = makeRef(*this)] {
148                     sendPendingData();
149                 });
150             }
151         }
152
153         if (result->readable && m_running) {
154             auto readBuffer = makeUniqueArray<uint8_t>(kReadBufferSize);
155             auto bytesRead = socket.receive(readBuffer.get(), kReadBufferSize);
156             // `nullopt` result means nothing to handle at this moment.
157             if (!bytesRead)
158                 continue;
159
160             // 0 bytes indicates a closed connection.
161             if (!*bytesRead) {
162                 m_running = false;
163                 callOnMainThread([this, protectedThis = makeRef(*this)] {
164                     close();
165                 });
166                 break;
167             }
168
169             callOnMainThread([this, protectedThis = makeRef(*this), buffer = WTFMove(readBuffer), size = *bytesRead ] {
170                 if (m_state == Open)
171                     m_client.didReceiveSocketStreamData(*this, reinterpret_cast<const char*>(buffer.get()), size);
172             });
173         }
174     }
175 }
176
177 void SocketStreamHandleImpl::handleError(CURLcode errorCode)
178 {
179     m_running = false;
180     callOnMainThread([this, protectedThis = makeRef(*this), errorCode, localizedDescription = CurlHandle::errorDescription(errorCode)] {
181         if (errorCode == CURLE_RECV_ERROR)
182             m_client.didFailToReceiveSocketStreamData(*this);
183         else
184             m_client.didFailSocketStream(*this, SocketStreamError(static_cast<int>(errorCode), { }, localizedDescription));
185     });
186 }
187
188 void SocketStreamHandleImpl::stopThread()
189 {
190     ASSERT(isMainThread());
191
192     m_running = false;
193
194     if (m_workerThread) {
195         m_workerThread->waitForCompletion();
196         m_workerThread = nullptr;
197     }
198 }
199
200 } // namespace WebCore
201
202 #endif