744082aad8b29dbfd38ebfb480c887304a25aca8
[WebKit-https.git] / Source / WebKit2 / WebProcess / soup / WebSoupRequestManager.cpp
1 /*
2  * Copyright (C) 2012 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 "WebSoupRequestManager.h"
22
23 #include "DataReference.h"
24 #include "WebErrors.h"
25 #include "WebKitSoupRequestGeneric.h"
26 #include "WebKitSoupRequestInputStream.h"
27 #include "WebPageProxyMessages.h"
28 #include "WebProcess.h"
29 #include "WebSoupRequestManagerMessages.h"
30 #include "WebSoupRequestManagerProxyMessages.h"
31 #include <WebCore/ResourceHandle.h>
32 #include <WebCore/ResourceRequest.h>
33 #include <wtf/gobject/GOwnPtr.h>
34 #include <wtf/text/CString.h>
35
36 namespace WebKit {
37
38 static uint64_t generateSoupRequestID()
39 {
40     static uint64_t uniqueSoupRequestID = 1;
41     return uniqueSoupRequestID++;
42 }
43
44 struct WebSoupRequestAsyncData {
45     WebSoupRequestAsyncData(GSimpleAsyncResult* result, WebKitSoupRequestGeneric* requestGeneric, GCancellable* cancellable)
46         : result(result)
47         , request(requestGeneric)
48         , cancellable(cancellable)
49     {
50         // If the struct contains a null request, it is because the request failed.
51         g_object_add_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request));
52     }
53
54     ~WebSoupRequestAsyncData()
55     {
56         if (request)
57             g_object_remove_weak_pointer(G_OBJECT(request), reinterpret_cast<void**>(&request));
58     }
59
60     bool requestFailed()
61     {
62         return g_cancellable_is_cancelled(cancellable.get()) || !request;
63     }
64
65     GRefPtr<GSimpleAsyncResult> releaseResult()
66     {
67         GSimpleAsyncResult* returnValue = result;
68         result = 0;
69         return adoptGRef(returnValue);
70     }
71
72     GSimpleAsyncResult* result;
73     WebKitSoupRequestGeneric* request;
74     GRefPtr<GCancellable> cancellable;
75     GRefPtr<GInputStream> stream;
76 };
77
78 WebSoupRequestManager::WebSoupRequestManager(WebProcess* process)
79     : m_process(process)
80     , m_schemes(adoptGRef(g_ptr_array_new_with_free_func(g_free)))
81 {
82     m_process->addMessageReceiver(Messages::WebSoupRequestManager::messageReceiverName(), this);
83 }
84
85 WebSoupRequestManager::~WebSoupRequestManager()
86 {
87 }
88
89 void WebSoupRequestManager::registerURIScheme(const String& scheme)
90 {
91     if (m_schemes->len)
92         g_ptr_array_remove_index_fast(m_schemes.get(), m_schemes->len - 1);
93     g_ptr_array_add(m_schemes.get(), g_strdup(scheme.utf8().data()));
94     g_ptr_array_add(m_schemes.get(), 0);
95
96     SoupSession* session = WebCore::ResourceHandle::defaultSession();
97     SoupRequestClass* genericRequestClass = static_cast<SoupRequestClass*>(g_type_class_ref(WEBKIT_TYPE_SOUP_REQUEST_GENERIC));
98     genericRequestClass->schemes = const_cast<const char**>(reinterpret_cast<char**>(m_schemes->pdata));
99     soup_session_add_feature_by_type(session, WEBKIT_TYPE_SOUP_REQUEST_GENERIC);
100 }
101
102 void WebSoupRequestManager::didHandleURIRequest(const CoreIPC::DataReference& requestData, uint64_t contentLength, const String& mimeType, uint64_t requestID)
103 {
104     WebSoupRequestAsyncData* data = m_requestMap.get(requestID);
105     ASSERT(data);
106     GRefPtr<GSimpleAsyncResult> result = data->releaseResult();
107     ASSERT(result.get());
108
109     GRefPtr<WebKitSoupRequestGeneric> request = adoptGRef(WEBKIT_SOUP_REQUEST_GENERIC(g_async_result_get_source_object(G_ASYNC_RESULT(result.get()))));
110     webkitSoupRequestGenericSetContentLength(request.get(), contentLength ? contentLength : -1);
111     webkitSoupRequestGenericSetContentType(request.get(), !mimeType.isEmpty() ? mimeType.utf8().data() : 0);
112
113     GInputStream* dataStream;
114     if (!requestData.size()) {
115         // Empty reply, just create and empty GMemoryInputStream.
116         dataStream = g_memory_input_stream_new();
117         m_requestMap.remove(requestID);
118     } else if (requestData.size() == contentLength) {
119         // We don't expect more data, so we can just create a GMemoryInputStream with all the data.
120         dataStream = g_memory_input_stream_new_from_data(g_memdup(requestData.data(), requestData.size()), contentLength, g_free);
121         m_requestMap.remove(requestID);
122     } else {
123         // We expect more data chunks from the UI process.
124         dataStream = webkitSoupRequestInputStreamNew(contentLength);
125         data->stream = dataStream;
126         webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(dataStream), requestData.data(), requestData.size());
127     }
128     g_simple_async_result_set_op_res_gpointer(result.get(), dataStream, g_object_unref);
129     g_simple_async_result_complete(result.get());
130 }
131
132 void WebSoupRequestManager::didReceiveURIRequestData(const CoreIPC::DataReference& requestData, uint64_t requestID)
133 {
134     WebSoupRequestAsyncData* data = m_requestMap.get(requestID);
135     // The data might have been removed from the request map if a previous chunk failed
136     // and a new message was sent by the UI process before being notified about the failure.
137     if (!data)
138         return;
139     ASSERT(data->stream.get());
140
141     if (data->requestFailed()) {
142         // ResourceRequest failed or it was cancelled. It doesn't matter here the error or if it was cancelled,
143         // because that's already handled by the resource handle client, we just want to notify the UI process
144         // to stop reading data from the user input stream. If UI process already sent all the data we simply
145         // finish silently.
146         if (!webkitSoupRequestInputStreamFinished(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get())))
147             m_process->connection()->send(Messages::WebSoupRequestManagerProxy::DidFailToLoadURIRequest(requestID), 0);
148         m_requestMap.remove(requestID);
149
150         return;
151     }
152
153     webkitSoupRequestInputStreamAddData(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get()), requestData.data(), requestData.size());
154     if (webkitSoupRequestInputStreamFinished(WEBKIT_SOUP_REQUEST_INPUT_STREAM(data->stream.get())))
155         m_requestMap.remove(requestID);
156 }
157
158 void WebSoupRequestManager::didFailURIRequest(const WebCore::ResourceError& error, uint64_t requestID)
159 {
160     WebSoupRequestAsyncData* data = m_requestMap.get(requestID);
161     ASSERT(data);
162     GRefPtr<GSimpleAsyncResult> result = data->releaseResult();
163     ASSERT(result.get());
164
165     g_simple_async_result_take_error(result.get(),
166         g_error_new_literal(g_quark_from_string(error.domain().utf8().data()),
167             error.errorCode(),
168             error.localizedDescription().utf8().data()));
169     g_simple_async_result_complete(result.get());
170
171     m_requestMap.remove(requestID);
172 }
173
174 void WebSoupRequestManager::send(GSimpleAsyncResult* result, GCancellable* cancellable)
175 {
176     GRefPtr<WebKitSoupRequestGeneric> request = adoptGRef(WEBKIT_SOUP_REQUEST_GENERIC(g_async_result_get_source_object(G_ASYNC_RESULT(result))));
177     SoupRequest* soupRequest = SOUP_REQUEST(request.get());
178     GOwnPtr<char> uriString(soup_uri_to_string(soup_request_get_uri(soupRequest), FALSE));
179
180     uint64_t requestID = generateSoupRequestID();
181     m_requestMap.set(requestID, adoptPtr(new WebSoupRequestAsyncData(result, request.get(), cancellable)));
182
183     uint64_t initiatingPageID = WebCore::ResourceHandle::getSoupRequestInitiatingPageID(soupRequest);
184     m_process->connection()->send(Messages::WebPageProxy::DidReceiveURIRequest(String::fromUTF8(uriString.get()), requestID), initiatingPageID);
185 }
186
187 GInputStream* WebSoupRequestManager::finish(GSimpleAsyncResult* result)
188 {
189     return G_INPUT_STREAM(g_object_ref(g_simple_async_result_get_op_res_gpointer(result)));
190 }
191
192 } // namespace WebKit