c9b0c3b2157ad8aebe746f4f39fb5efcd75e715a
[WebKit-https.git] / Source / WebKit / NetworkProcess / CustomProtocols / soup / LegacyCustomProtocolManagerSoup.cpp
1 /*
2  * Copyright (C) 2013 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "LegacyCustomProtocolManager.h"
22
23 #include "DataReference.h"
24 #include "LegacyCustomProtocolManagerMessages.h"
25 #include "NetworkProcess.h"
26 #include "NetworkSessionSoup.h"
27 #include "WebKitSoupRequestInputStream.h"
28 #include <WebCore/NotImplemented.h>
29 #include <WebCore/ResourceError.h>
30 #include <WebCore/ResourceRequest.h>
31 #include <WebCore/ResourceResponse.h>
32 #include <WebCore/SoupNetworkSession.h>
33 #include <WebCore/WebKitSoupRequestGeneric.h>
34 #include <wtf/NeverDestroyed.h>
35
36 namespace WebKit {
37 using namespace WebCore;
38
39 RefPtr<NetworkProcess>& lastCreatedNetworkProcess()
40 {
41     static NeverDestroyed<RefPtr<NetworkProcess>> networkProcess;
42     return networkProcess.get();
43 }
44
45 void LegacyCustomProtocolManager::networkProcessCreated(NetworkProcess& networkProcess)
46 {
47     lastCreatedNetworkProcess() = &networkProcess;
48 }
49
50 LegacyCustomProtocolManager::WebSoupRequestAsyncData::WebSoupRequestAsyncData(GRefPtr<GTask>&& requestTask, WebKitSoupRequestGeneric* requestGeneric)
51     : task(WTFMove(requestTask))
52     , request(requestGeneric)
53     , cancellable(g_task_get_cancellable(task.get()))
54 {
55     // If the struct contains a null request, it is because the request failed.
56     g_object_add_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request));
57 }
58
59 LegacyCustomProtocolManager::WebSoupRequestAsyncData::~WebSoupRequestAsyncData()
60 {
61     if (request)
62         g_object_remove_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request));
63 }
64
65 class CustomProtocolRequestClient final : public WebKitSoupRequestGenericClient {
66 public:
67     static CustomProtocolRequestClient& singleton()
68     {
69         static NeverDestroyed<CustomProtocolRequestClient> client;
70         return client;
71     }
72
73 private:
74     void startRequest(GRefPtr<GTask>&& task) override
75     {
76         WebKitSoupRequestGeneric* request = WEBKIT_SOUP_REQUEST_GENERIC(g_task_get_source_object(task.get()));
77         auto* customProtocolManager = lastCreatedNetworkProcess()->supplement<LegacyCustomProtocolManager>();
78         if (!customProtocolManager)
79             return;
80
81         auto customProtocolID = customProtocolManager->addCustomProtocol(makeUnique<LegacyCustomProtocolManager::WebSoupRequestAsyncData>(WTFMove(task), request));
82         customProtocolManager->startLoading(customProtocolID, webkitSoupRequestGenericGetRequest(request));
83     }
84 };
85
86 void LegacyCustomProtocolManager::registerProtocolClass()
87 {
88     static_cast<WebKitSoupRequestGenericClass*>(g_type_class_ref(WEBKIT_TYPE_SOUP_REQUEST_GENERIC))->client = &CustomProtocolRequestClient::singleton();
89     SoupNetworkSession::setCustomProtocolRequestType(WEBKIT_TYPE_SOUP_REQUEST_GENERIC);
90 }
91
92 void LegacyCustomProtocolManager::registerScheme(const String& scheme)
93 {
94     if (!m_registeredSchemes)
95         m_registeredSchemes = adoptGRef(g_ptr_array_new_with_free_func(g_free));
96
97     if (m_registeredSchemes->len)
98         g_ptr_array_remove_index_fast(m_registeredSchemes.get(), m_registeredSchemes->len - 1);
99     g_ptr_array_add(m_registeredSchemes.get(), g_strdup(scheme.utf8().data()));
100     g_ptr_array_add(m_registeredSchemes.get(), nullptr);
101
102     auto* genericRequestClass = static_cast<SoupRequestClass*>(g_type_class_peek(WEBKIT_TYPE_SOUP_REQUEST_GENERIC));
103     ASSERT(genericRequestClass);
104     genericRequestClass->schemes = const_cast<const char**>(reinterpret_cast<char**>(m_registeredSchemes->pdata));
105     lastCreatedNetworkProcess()->forEachNetworkSession([](const auto& session) {
106         static_cast<const NetworkSessionSoup&>(session).soupNetworkSession().setupCustomProtocols();
107     });
108 }
109
110 void LegacyCustomProtocolManager::unregisterScheme(const String&)
111 {
112     notImplemented();
113 }
114
115 bool LegacyCustomProtocolManager::supportsScheme(const String& scheme)
116 {
117     if (scheme.isNull())
118         return false;
119
120     CString cScheme = scheme.utf8();
121     for (unsigned i = 0; i < m_registeredSchemes->len; ++i) {
122         if (cScheme == static_cast<char*>(g_ptr_array_index(m_registeredSchemes.get(), i)))
123             return true;
124     }
125
126     return false;
127 }
128
129 void LegacyCustomProtocolManager::didFailWithError(uint64_t customProtocolID, const ResourceError& error)
130 {
131     auto* data = m_customProtocolMap.get(customProtocolID);
132     ASSERT(data);
133
134     // Either we haven't started reading the stream yet, in which case we need to complete the
135     // task first, or we failed reading it and the task was already completed by didLoadData().
136     ASSERT(!data->stream || !data->task);
137
138     if (!data->stream) {
139         GRefPtr<GTask> task = std::exchange(data->task, nullptr);
140         ASSERT(task.get());
141         g_task_return_new_error(task.get(), g_quark_from_string(error.domain().utf8().data()),
142             error.errorCode(), "%s", error.localizedDescription().utf8().data());
143     } else
144         webkitSoupRequestInputStreamDidFailWithError(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), error);
145
146     removeCustomProtocol(customProtocolID);
147 }
148
149 void LegacyCustomProtocolManager::didLoadData(uint64_t customProtocolID, const IPC::DataReference& dataReference)
150 {
151     auto* data = m_customProtocolMap.get(customProtocolID);
152     // The data might have been removed from the request map if a previous chunk failed
153     // and a new message was sent by the UI process before being notified about the failure.
154     if (!data)
155         return;
156
157     if (!data->stream) {
158         GRefPtr<GTask> task = std::exchange(data->task, nullptr);
159         ASSERT(task.get());
160
161         goffset soupContentLength = soup_request_get_content_length(SOUP_REQUEST(g_task_get_source_object(task.get())));
162         uint64_t contentLength = soupContentLength == -1 ? 0 : static_cast<uint64_t>(soupContentLength);
163         if (!dataReference.size()) {
164             // Empty reply, just create and empty GMemoryInputStream.
165             data->stream = g_memory_input_stream_new();
166         } else if (dataReference.size() == contentLength) {
167             // We don't expect more data, so we can just create a GMemoryInputStream with all the data.
168             data->stream = g_memory_input_stream_new_from_data(g_memdup(dataReference.data(), dataReference.size()), contentLength, g_free);
169         } else {
170             // We expect more data chunks from the UI process.
171             data->stream = webkitSoupRequestInputStreamNew(contentLength);
172             webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), dataReference.data(), dataReference.size());
173         }
174         g_task_return_pointer(task.get(), data->stream.get(), g_object_unref);
175         return;
176     }
177
178     if (g_cancellable_is_cancelled(data->cancellable.get()) || !data->request) {
179         // ResourceRequest failed or it was cancelled. It doesn't matter here the error or if it was cancelled,
180         // because that's already handled by the resource handle client, we just want to notify the UI process
181         // to stop reading data from the user input stream. If UI process already sent all the data we simply
182         // finish silently.
183         if (!webkitSoupRequestInputStreamFinished(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get())))
184             stopLoading(customProtocolID);
185
186         return;
187     }
188
189     webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), dataReference.data(), dataReference.size());
190 }
191
192 void LegacyCustomProtocolManager::didReceiveResponse(uint64_t customProtocolID, const ResourceResponse& response, uint32_t)
193 {
194     auto* data = m_customProtocolMap.get(customProtocolID);
195     // The data might have been removed from the request map if an error happened even before this point.
196     if (!data)
197         return;
198
199     ASSERT(data->task);
200
201     WebKitSoupRequestGeneric* request = WEBKIT_SOUP_REQUEST_GENERIC(g_task_get_source_object(data->task.get()));
202     webkitSoupRequestGenericSetContentLength(request, response.expectedContentLength() ? response.expectedContentLength() : -1);
203     webkitSoupRequestGenericSetContentType(request, !response.mimeType().isEmpty() ? response.mimeType().utf8().data() : 0);
204 }
205
206 void LegacyCustomProtocolManager::didFinishLoading(uint64_t customProtocolID)
207 {
208     ASSERT(m_customProtocolMap.contains(customProtocolID));
209     removeCustomProtocol(customProtocolID);
210 }
211
212 void LegacyCustomProtocolManager::wasRedirectedToRequest(uint64_t, const ResourceRequest&, const ResourceResponse&)
213 {
214     notImplemented();
215 }
216
217 } // namespace WebKit