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