diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/platform/network/soup | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/platform/network/soup')
33 files changed, 1676 insertions, 1379 deletions
diff --git a/Source/WebCore/platform/network/soup/AuthenticationChallenge.h b/Source/WebCore/platform/network/soup/AuthenticationChallenge.h index 48cf591be..c6d4bd1dd 100644 --- a/Source/WebCore/platform/network/soup/AuthenticationChallenge.h +++ b/Source/WebCore/platform/network/soup/AuthenticationChallenge.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -44,10 +44,8 @@ public: { } - AuthenticationChallenge(SoupSession*, SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient*); + AuthenticationChallenge(SoupMessage*, SoupAuth*, bool retrying, AuthenticationClient* = nullptr); AuthenticationClient* authenticationClient() const { return m_authenticationClient.get(); } - SoupSession* soupSession() const { return m_soupSession.get(); } - SoupMessage* soupMessage() const { return m_soupMessage.get(); } SoupAuth* soupAuth() const { return m_soupAuth.get(); } void setProposedCredential(const Credential& credential) { m_proposedCredential = credential; } @@ -55,8 +53,6 @@ private: friend class AuthenticationChallengeBase; static bool platformCompare(const AuthenticationChallenge&, const AuthenticationChallenge&); - GRefPtr<SoupSession> m_soupSession; - GRefPtr<SoupMessage> m_soupMessage; GRefPtr<SoupAuth> m_soupAuth; RefPtr<AuthenticationClient> m_authenticationClient; }; diff --git a/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp b/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp index 2e7d02852..97e923c8a 100644 --- a/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp +++ b/Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY IGALIA S.L. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,6 +24,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "AuthenticationChallenge.h" #include "ResourceError.h" @@ -63,14 +66,12 @@ static ProtectionSpace protectionSpaceFromSoupAuthAndMessage(SoupAuth* soupAuth, String::fromUTF8(soup_auth_get_realm(soupAuth)), scheme); } -AuthenticationChallenge::AuthenticationChallenge(SoupSession* soupSession, SoupMessage* soupMessage, SoupAuth* soupAuth, bool retrying, AuthenticationClient* client) +AuthenticationChallenge::AuthenticationChallenge(SoupMessage* soupMessage, SoupAuth* soupAuth, bool retrying, AuthenticationClient* client) : AuthenticationChallengeBase(protectionSpaceFromSoupAuthAndMessage(soupAuth, soupMessage), Credential(), // proposedCredentials retrying ? 1 : 0, // previousFailureCount soupMessage, // failureResponse ResourceError::authenticationError(soupMessage)) - , m_soupSession(soupSession) - , m_soupMessage(soupMessage) , m_soupAuth(soupAuth) , m_authenticationClient(client) { @@ -78,9 +79,9 @@ AuthenticationChallenge::AuthenticationChallenge(SoupSession* soupSession, SoupM bool AuthenticationChallenge::platformCompare(const AuthenticationChallenge& a, const AuthenticationChallenge& b) { - return a.soupSession() == b.soupSession() - && a.soupMessage() == b.soupMessage() - && a.soupAuth() == b.soupAuth(); + return a.soupAuth() == b.soupAuth(); } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/CertificateInfo.cpp b/Source/WebCore/platform/network/soup/CertificateInfo.cpp index d5fae4dbf..1db77a323 100644 --- a/Source/WebCore/platform/network/soup/CertificateInfo.cpp +++ b/Source/WebCore/platform/network/soup/CertificateInfo.cpp @@ -24,6 +24,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "CertificateInfo.h" #include <ResourceError.h> @@ -60,3 +63,5 @@ CertificateInfo::~CertificateInfo() } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/CertificateInfo.h b/Source/WebCore/platform/network/soup/CertificateInfo.h index 94e4ff585..206cdef68 100644 --- a/Source/WebCore/platform/network/soup/CertificateInfo.h +++ b/Source/WebCore/platform/network/soup/CertificateInfo.h @@ -27,8 +27,9 @@ #ifndef CertificateInfo_h #define CertificateInfo_h +#include "NotImplemented.h" #include <libsoup/soup.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> namespace WebCore { @@ -48,6 +49,8 @@ public: GTlsCertificateFlags tlsErrors() const { return m_tlsErrors; } void setTLSErrors(GTlsCertificateFlags tlsErrors) { m_tlsErrors = tlsErrors; } + bool containsNonRootSHA1SignedCertificate() const { notImplemented(); return false; } + private: GRefPtr<GTlsCertificate> m_certificate; GTlsCertificateFlags m_tlsErrors; diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp index fec9dd544..31f009d17 100644 --- a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp +++ b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp @@ -19,55 +19,22 @@ */ #include "config.h" -#include "CookieJarSoup.h" + +#if USE(SOUP) #include "Cookie.h" #include "GUniquePtrSoup.h" -#include "URL.h" +#include "NetworkStorageSession.h" #include "NetworkingContext.h" #include "PlatformCookieJar.h" #include "SoupNetworkSession.h" -#include <wtf/gobject/GRefPtr.h> +#include "URL.h" +#include <wtf/DateMath.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/CString.h> namespace WebCore { -static SoupCookieJar* cookieJarForSession(const NetworkStorageSession& session) -{ - return session.soupNetworkSession().cookieJar(); -} - -static GRefPtr<SoupCookieJar>& defaultCookieJar() -{ - DEFINE_STATIC_LOCAL(GRefPtr<SoupCookieJar>, cookieJar, ()); - return cookieJar; -} - -SoupCookieJar* soupCookieJar() -{ - if (GRefPtr<SoupCookieJar>& jar = defaultCookieJar()) - return jar.get(); - - SoupCookieJar* jar = soup_cookie_jar_new(); - soup_cookie_jar_set_accept_policy(jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); - setSoupCookieJar(jar); - return jar; -} - -SoupCookieJar* createPrivateBrowsingCookieJar() -{ - SoupCookieJar* jar = soup_cookie_jar_new(); - - soup_cookie_jar_set_accept_policy(jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); - - return jar; -} - -void setSoupCookieJar(SoupCookieJar* jar) -{ - defaultCookieJar() = jar; -} - static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name, const gchar* path) { for (const GSList* iter = cookies; iter; iter = g_slist_next(iter)) { @@ -84,9 +51,7 @@ static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const URL& url, const String& value) { - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return; + SoupCookieJar* jar = session.cookieStorage(); GUniquePtr<SoupURI> origin = url.createSoupURI(); GUniquePtr<SoupURI> firstPartyURI = firstParty.createSoupURI(); @@ -118,12 +83,8 @@ void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstPar static String cookiesForSession(const NetworkStorageSession& session, const URL& url, bool forHTTPHeader) { - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return String(); - GUniquePtr<SoupURI> uri = url.createSoupURI(); - GUniquePtr<char> cookies(soup_cookie_jar_get_cookies(jar, uri.get(), forHTTPHeader)); + GUniquePtr<char> cookies(soup_cookie_jar_get_cookies(session.cookieStorage(), uri.get(), forHTTPHeader)); return String::fromUTF8(cookies.get()); } @@ -139,18 +100,15 @@ String cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const bool cookiesEnabled(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& /*url*/) { - return !!cookieJarForSession(session); + auto policy = soup_cookie_jar_get_accept_policy(session.cookieStorage()); + return policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS || policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY; } bool getRawCookies(const NetworkStorageSession& session, const URL& /*firstParty*/, const URL& url, Vector<Cookie>& rawCookies) { rawCookies.clear(); - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return false; - GUniquePtr<SoupURI> uri = url.createSoupURI(); - GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE)); + GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(session.cookieStorage(), uri.get(), TRUE)); if (!cookies) return false; @@ -167,9 +125,7 @@ bool getRawCookies(const NetworkStorageSession& session, const URL& /*firstParty void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& name) { - SoupCookieJar* jar = cookieJarForSession(session); - if (!jar) - return; + SoupCookieJar* jar = session.cookieStorage(); GUniquePtr<SoupURI> uri = url.createSoupURI(); GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE)); @@ -188,10 +144,36 @@ void deleteCookie(const NetworkStorageSession& session, const URL& url, const St } } +static SoupDate* msToSoupDate(double ms) +{ + int year = msToYear(ms); + int dayOfYear = dayInYear(ms, year); + bool leapYear = isLeapYear(year); + return soup_date_new(year, monthFromDayInYear(dayOfYear, leapYear), dayInMonthFromDayInYear(dayOfYear, leapYear), msToHours(ms), msToMinutes(ms), static_cast<int>(ms / 1000) % 60); +} + +static SoupCookie* toSoupCookie(const Cookie& cookie) +{ + SoupCookie* soupCookie = soup_cookie_new(cookie.name.utf8().data(), cookie.value.utf8().data(), + cookie.domain.utf8().data(), cookie.path.utf8().data(), -1); + soup_cookie_set_http_only(soupCookie, cookie.httpOnly); + soup_cookie_set_secure(soupCookie, cookie.secure); + if (!cookie.session) { + SoupDate* date = msToSoupDate(cookie.expires); + soup_cookie_set_expires(soupCookie, date); + soup_date_free(date); + } + return soupCookie; +} + +void addCookie(const NetworkStorageSession& session, const URL&, const Cookie& cookie) +{ + soup_cookie_jar_add_cookie(session.cookieStorage(), toSoupCookie(cookie)); +} + void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames) { - SoupCookieJar* cookieJar = cookieJarForSession(session); - GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); + GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(session.cookieStorage())); for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { SoupCookie* cookie = static_cast<SoupCookie*>(item->data); if (cookie->domain) @@ -200,22 +182,26 @@ void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<Strin } } -void deleteCookiesForHostname(const NetworkStorageSession& session, const String& hostname) +void deleteCookiesForHostnames(const NetworkStorageSession& session, const Vector<String>& hostnames) { - CString hostNameString = hostname.utf8(); - SoupCookieJar* cookieJar = cookieJarForSession(session); - GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); - for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { - SoupCookie* cookie = static_cast<SoupCookie*>(item->data); - if (soup_cookie_domain_matches(cookie, hostNameString.data())) - soup_cookie_jar_delete_cookie(cookieJar, cookie); - soup_cookie_free(cookie); + SoupCookieJar* cookieJar = session.cookieStorage(); + + for (const auto& hostname : hostnames) { + CString hostNameString = hostname.utf8(); + + GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); + for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { + SoupCookie* cookie = static_cast<SoupCookie*>(item->data); + if (soup_cookie_domain_matches(cookie, hostNameString.data())) + soup_cookie_jar_delete_cookie(cookieJar, cookie); + soup_cookie_free(cookie); + } } } void deleteAllCookies(const NetworkStorageSession& session) { - SoupCookieJar* cookieJar = cookieJarForSession(session); + SoupCookieJar* cookieJar = session.cookieStorage(); GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar)); for (GSList* item = cookies.get(); item; item = g_slist_next(item)) { SoupCookie* cookie = static_cast<SoupCookie*>(item->data); @@ -224,4 +210,15 @@ void deleteAllCookies(const NetworkStorageSession& session) } } +void deleteAllCookiesModifiedSince(const NetworkStorageSession& session, std::chrono::system_clock::time_point timestamp) +{ + // FIXME: Add support for deleting cookies modified since the given timestamp. It should probably be added to libsoup. + if (timestamp == std::chrono::system_clock::from_time_t(0)) + deleteAllCookies(session); + else + g_warning("Deleting cookies modified since a given time span is not supported yet"); +} + } + +#endif diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.h b/Source/WebCore/platform/network/soup/CookieJarSoup.h deleted file mode 100644 index b949ef8fa..000000000 --- a/Source/WebCore/platform/network/soup/CookieJarSoup.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2008 Xan Lopez <xan@gnome.org> - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef CookieJarSoup_h -#define CookieJarSoup_h - -#include <libsoup/soup.h> - -namespace WebCore { - -SoupCookieJar* soupCookieJar(); -void setSoupCookieJar(SoupCookieJar*); - -SoupCookieJar* createPrivateBrowsingCookieJar(); - -} - -#endif diff --git a/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp b/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp index 65817a1bd..ea6a6b292 100644 --- a/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp +++ b/Source/WebCore/platform/network/soup/CookieStorageSoup.cpp @@ -19,34 +19,20 @@ #include "config.h" #include "CookieStorage.h" -#include "CookieJarSoup.h" -#include "NotImplemented.h" - -#include <stdio.h> +#if USE(SOUP) namespace WebCore { -static CookieChangeCallbackPtr cookieChangeCallback; - -static void soupCookiesChanged(SoupCookieJar* jar, SoupCookie*, SoupCookie*, gpointer) +void startObservingCookieChanges(const NetworkStorageSession&, std::function<void ()>&&) { - if (jar != soupCookieJar()) - return; - cookieChangeCallback(); + ASSERT_NOT_REACHED(); } -void startObservingCookieChanges(CookieChangeCallbackPtr callback) +void stopObservingCookieChanges(const NetworkStorageSession&) { - ASSERT(!cookieChangeCallback); - cookieChangeCallback = callback; - - g_signal_connect(soupCookieJar(), "changed", G_CALLBACK(soupCookiesChanged), 0); + ASSERT_NOT_REACHED(); } -void stopObservingCookieChanges() -{ - g_signal_handlers_disconnect_by_func(soupCookieJar(), reinterpret_cast<void*>(soupCookiesChanged), 0); - cookieChangeCallback = 0; } -} +#endif diff --git a/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp b/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp index 54ccad056..e5ec16f95 100644 --- a/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp +++ b/Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "CredentialStorage.h" +#if USE(SOUP) + #include "Credential.h" namespace WebCore { @@ -36,3 +38,5 @@ Credential CredentialStorage::getFromPersistentStorage(const ProtectionSpace&) } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/DNSSoup.cpp b/Source/WebCore/platform/network/soup/DNSSoup.cpp index 6f630b5b8..ca11457dd 100644 --- a/Source/WebCore/platform/network/soup/DNSSoup.cpp +++ b/Source/WebCore/platform/network/soup/DNSSoup.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2008 Apple Inc. All rights reserved. * Copyright (C) 2009, 2012 Igalia S.L. * * Redistribution and use in source and binary forms, with or without @@ -14,7 +14,7 @@ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -28,31 +28,74 @@ #include "DNS.h" #include "DNSResolveQueue.h" +#if USE(SOUP) + +#include "NetworkStorageSession.h" #include "SoupNetworkSession.h" #include <libsoup/soup.h> #include <wtf/MainThread.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> namespace WebCore { -// There is no current reliable way to know if we're behind a proxy at -// this level. We'll have to implement it in -// SoupSession/SoupProxyURIResolver/GProxyResolver -bool DNSResolveQueue::platformProxyIsEnabledInSystemPreferences() +// Initially true to ensure prefetch stays disabled until we have proxy settings. +static bool isUsingHttpProxy = true; +static bool isUsingHttpsProxy = true; + +static bool didResolveProxy(char** uris) +{ + // We have a list of possible proxies to use for the URI. If the first item in the list is + // direct:// (the usual case), then the user prefers not to use a proxy. This is similar to + // resolving hostnames: there could be many possibilities returned in order of preference, and + // if we're trying to connect we should attempt each one in order, but here we are not trying + // to connect, merely to decide whether a proxy "should" be used. + return uris && *uris && strcmp(*uris, "direct://"); +} + +static void didResolveProxy(GProxyResolver* resolver, GAsyncResult* result, bool* isUsingProxyType, bool* isUsingProxy) +{ + GUniqueOutPtr<GError> error; + GUniquePtr<char*> uris(g_proxy_resolver_lookup_finish(resolver, result, &error.outPtr())); + if (error) { + WTFLogAlways("Error determining system proxy settings: %s", error->message); + return; + } + + *isUsingProxyType = didResolveProxy(uris.get()); + *isUsingProxy = isUsingHttpProxy || isUsingHttpsProxy; +} + +static void proxyResolvedForHttpUriCallback(GObject* source, GAsyncResult* result, void* userData) { - return false; + didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpProxy, static_cast<bool*>(userData)); +} + +static void proxyResolvedForHttpsUriCallback(GObject* source, GAsyncResult* result, void* userData) +{ + didResolveProxy(G_PROXY_RESOLVER(source), result, &isUsingHttpsProxy, static_cast<bool*>(userData)); +} + +void DNSResolveQueue::updateIsUsingProxy() +{ + GRefPtr<GProxyResolver> resolver; + g_object_get(NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession(), "proxy-resolver", &resolver.outPtr(), nullptr); + ASSERT(resolver); + + g_proxy_resolver_lookup_async(resolver.get(), "http://example.com/", nullptr, proxyResolvedForHttpUriCallback, &m_isUsingProxy); + g_proxy_resolver_lookup_async(resolver.get(), "https://example.com/", nullptr, proxyResolvedForHttpsUriCallback, &m_isUsingProxy); } static void resolvedCallback(SoupAddress*, guint, void*) { - DNSResolveQueue::shared().decrementRequestCount(); + DNSResolveQueue::singleton().decrementRequestCount(); } void DNSResolveQueue::platformResolve(const String& hostname) { ASSERT(isMainThread()); - soup_session_prefetch_dns(SoupNetworkSession::defaultSession().soupSession(), hostname.utf8().data(), nullptr, resolvedCallback, nullptr); + soup_session_prefetch_dns(NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession(), hostname.utf8().data(), nullptr, resolvedCallback, nullptr); } void prefetchDNS(const String& hostname) @@ -61,7 +104,9 @@ void prefetchDNS(const String& hostname) if (hostname.isEmpty()) return; - DNSResolveQueue::shared().add(hostname); + DNSResolveQueue::singleton().add(hostname); } } + +#endif diff --git a/Source/WebCore/platform/network/soup/GRefPtrSoup.cpp b/Source/WebCore/platform/network/soup/GRefPtrSoup.cpp new file mode 100644 index 000000000..009aec168 --- /dev/null +++ b/Source/WebCore/platform/network/soup/GRefPtrSoup.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "GRefPtrSoup.h" + +namespace WTF { + +template <> SoupBuffer* refGPtr(SoupBuffer* ptr) +{ + return ptr ? soup_buffer_copy(ptr) : nullptr; +} + +template <> void derefGPtr(SoupBuffer* ptr) +{ + if (ptr) + soup_buffer_free(ptr); +} + +} // namespace WTF diff --git a/Source/WebCore/platform/network/soup/GRefPtrSoup.h b/Source/WebCore/platform/network/soup/GRefPtrSoup.h new file mode 100644 index 000000000..a5988e2fb --- /dev/null +++ b/Source/WebCore/platform/network/soup/GRefPtrSoup.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef GRefPtrSoup_h +#define GRefPtrSoup_h + +#if USE(SOUP) + +#include <libsoup/soup.h> +#include <wtf/glib/GRefPtr.h> + +namespace WTF { + +template <> SoupBuffer* refGPtr(SoupBuffer*); +template <> void derefGPtr(SoupBuffer*); + +} // namespace WTF + +#endif // USE(SOUP) + +#endif diff --git a/Source/WebCore/platform/network/soup/GUniquePtrSoup.h b/Source/WebCore/platform/network/soup/GUniquePtrSoup.h index 32775fb46..86dc63741 100644 --- a/Source/WebCore/platform/network/soup/GUniquePtrSoup.h +++ b/Source/WebCore/platform/network/soup/GUniquePtrSoup.h @@ -21,7 +21,7 @@ #define GUniquePtrSoup_h #include <libsoup/soup.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> namespace WTF { diff --git a/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp b/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp index 67f195049..8417691e8 100644 --- a/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp +++ b/Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2013 Apple Inc. All rights reserved. * Copyright (C) 2013 University of Szeged. All rights reserved. + * Copyright (C) 2016 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,21 +28,39 @@ #include "config.h" #include "NetworkStorageSession.h" +#if USE(SOUP) + #include "ResourceHandle.h" #include "SoupNetworkSession.h" +#include <libsoup/soup.h> #include <wtf/MainThread.h> #include <wtf/NeverDestroyed.h> +#include <wtf/glib/GUniquePtr.h> + +#if USE(LIBSECRET) +#include "GRefPtrGtk.h" +#include <glib/gi18n-lib.h> +#define SECRET_WITH_UNSTABLE 1 +#define SECRET_API_SUBJECT_TO_CHANGE 1 +#include <libsecret/secret.h> +#endif namespace WebCore { -NetworkStorageSession::NetworkStorageSession(std::unique_ptr<SoupNetworkSession> session) - : m_session(std::move(session)) - , m_isPrivate(false) +NetworkStorageSession::NetworkStorageSession(SessionID sessionID, std::unique_ptr<SoupNetworkSession>&& session) + : m_sessionID(sessionID) + , m_session(WTFMove(session)) { + setCookieStorage(m_session ? m_session->cookieJar() : nullptr); } NetworkStorageSession::~NetworkStorageSession() { + g_signal_handlers_disconnect_matched(m_cookieStorage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + +#if USE(LIBSECRET) + g_cancellable_cancel(m_persisentStorageCancellable.get()); +#endif } static std::unique_ptr<NetworkStorageSession>& defaultSession() @@ -54,30 +73,207 @@ static std::unique_ptr<NetworkStorageSession>& defaultSession() NetworkStorageSession& NetworkStorageSession::defaultStorageSession() { if (!defaultSession()) - defaultSession() = std::make_unique<NetworkStorageSession>(nullptr); + defaultSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), nullptr); return *defaultSession(); } -std::unique_ptr<NetworkStorageSession> NetworkStorageSession::createPrivateBrowsingSession(const String&) +void NetworkStorageSession::ensurePrivateBrowsingSession(SessionID sessionID, const String&) { - auto session = std::make_unique<NetworkStorageSession>(SoupNetworkSession::createPrivateBrowsingSession()); - session->m_isPrivate = true; - return session; + ASSERT(sessionID != SessionID::defaultSessionID()); + ASSERT(!globalSessionMap().contains(sessionID)); + globalSessionMap().add(sessionID, std::make_unique<NetworkStorageSession>(sessionID, std::make_unique<SoupNetworkSession>())); } void NetworkStorageSession::switchToNewTestingSession() { - defaultSession() = std::make_unique<NetworkStorageSession>(SoupNetworkSession::createTestingSession()); + defaultSession() = std::make_unique<NetworkStorageSession>(SessionID::defaultSessionID(), std::make_unique<SoupNetworkSession>()); +} + +SoupNetworkSession& NetworkStorageSession::getOrCreateSoupNetworkSession() const +{ + if (!m_session) + m_session = std::make_unique<SoupNetworkSession>(m_cookieStorage.get()); + return *m_session; +} + +void NetworkStorageSession::cookiesDidChange(NetworkStorageSession* session) +{ + if (session->m_cookieObserverHandler) + session->m_cookieObserverHandler(); +} + +SoupCookieJar* NetworkStorageSession::cookieStorage() const +{ + RELEASE_ASSERT(!m_session || m_session->cookieJar() == m_cookieStorage.get()); + return m_cookieStorage.get(); +} + +void NetworkStorageSession::setCookieStorage(SoupCookieJar* jar) +{ + if (m_cookieStorage) + g_signal_handlers_disconnect_matched(m_cookieStorage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + + // We always have a valid cookieStorage. + if (jar) + m_cookieStorage = jar; + else { + m_cookieStorage = adoptGRef(soup_cookie_jar_new()); + soup_cookie_jar_set_accept_policy(m_cookieStorage.get(), SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); + } + g_signal_connect_swapped(m_cookieStorage.get(), "changed", G_CALLBACK(cookiesDidChange), this); + if (m_session && m_session->cookieJar() != m_cookieStorage.get()) + m_session->setCookieJar(m_cookieStorage.get()); +} + +void NetworkStorageSession::setCookieObserverHandler(Function<void ()>&& handler) +{ + m_cookieObserverHandler = WTFMove(handler); } -SoupNetworkSession& NetworkStorageSession::soupNetworkSession() const +#if USE(LIBSECRET) +static const char* schemeFromProtectionSpaceServerType(ProtectionSpaceServerType serverType) { - return m_session ? *m_session : SoupNetworkSession::defaultSession(); + switch (serverType) { + case ProtectionSpaceServerHTTP: + case ProtectionSpaceProxyHTTP: + return SOUP_URI_SCHEME_HTTP; + case ProtectionSpaceServerHTTPS: + case ProtectionSpaceProxyHTTPS: + return SOUP_URI_SCHEME_HTTPS; + case ProtectionSpaceServerFTP: + case ProtectionSpaceProxyFTP: + return SOUP_URI_SCHEME_FTP; + case ProtectionSpaceServerFTPS: + case ProtectionSpaceProxySOCKS: + break; + } + + ASSERT_NOT_REACHED(); + return SOUP_URI_SCHEME_HTTP; } -void NetworkStorageSession::setSoupNetworkSession(std::unique_ptr<SoupNetworkSession> session) +static const char* authTypeFromProtectionSpaceAuthenticationScheme(ProtectionSpaceAuthenticationScheme scheme) { - m_session = std::move(session); + switch (scheme) { + case ProtectionSpaceAuthenticationSchemeDefault: + case ProtectionSpaceAuthenticationSchemeHTTPBasic: + return "Basic"; + case ProtectionSpaceAuthenticationSchemeHTTPDigest: + return "Digest"; + case ProtectionSpaceAuthenticationSchemeNTLM: + return "NTLM"; + case ProtectionSpaceAuthenticationSchemeNegotiate: + return "Negotiate"; + case ProtectionSpaceAuthenticationSchemeHTMLForm: + case ProtectionSpaceAuthenticationSchemeClientCertificateRequested: + case ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested: + ASSERT_NOT_REACHED(); + break; + case ProtectionSpaceAuthenticationSchemeUnknown: + return "unknown"; + } + + ASSERT_NOT_REACHED(); + return "unknown"; } +#endif // USE(LIBSECRET) + +void NetworkStorageSession::getCredentialFromPersistentStorage(const ProtectionSpace& protectionSpace, Function<void (Credential&&)> completionHandler) +{ +#if USE(LIBSECRET) + if (m_sessionID.isEphemeral()) { + completionHandler({ }); + return; + } + + const String& realm = protectionSpace.realm(); + if (realm.isEmpty()) { + completionHandler({ }); + return; + } + + GRefPtr<GHashTable> attributes = adoptGRef(secret_attributes_build(SECRET_SCHEMA_COMPAT_NETWORK, + "domain", realm.utf8().data(), + "server", protectionSpace.host().utf8().data(), + "port", protectionSpace.port(), + "protocol", schemeFromProtectionSpaceServerType(protectionSpace.serverType()), + "authtype", authTypeFromProtectionSpaceAuthenticationScheme(protectionSpace.authenticationScheme()), + nullptr)); + if (!attributes) { + completionHandler({ }); + return; + } + + m_persisentStorageCancellable = adoptGRef(g_cancellable_new()); + m_persisentStorageCompletionHandler = WTFMove(completionHandler); + secret_service_search(nullptr, SECRET_SCHEMA_COMPAT_NETWORK, attributes.get(), + static_cast<SecretSearchFlags>(SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS), m_persisentStorageCancellable.get(), + [](GObject* source, GAsyncResult* result, gpointer userData) { + GUniqueOutPtr<GError> error; + GUniquePtr<GList> elements(secret_service_search_finish(SECRET_SERVICE(source), result, &error.outPtr())); + if (g_error_matches (error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + NetworkStorageSession* session = static_cast<NetworkStorageSession*>(userData); + auto completionHandler = std::exchange(session->m_persisentStorageCompletionHandler, nullptr); + if (error || !elements || !elements->data) { + completionHandler({ }); + return; + } + + GRefPtr<SecretItem> secretItem = adoptGRef(static_cast<SecretItem*>(elements->data)); + GRefPtr<GHashTable> attributes = adoptGRef(secret_item_get_attributes(secretItem.get())); + String user = String::fromUTF8(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "user"))); + if (user.isEmpty()) { + completionHandler({ }); + return; + } + size_t length; + GRefPtr<SecretValue> secretValue = adoptGRef(secret_item_get_secret(secretItem.get())); + const char* passwordData = secret_value_get(secretValue.get(), &length); + completionHandler(Credential(user, String::fromUTF8(passwordData, length), CredentialPersistencePermanent)); + }, this); +#else + UNUSED_PARAM(protectionSpace); + completionHandler({ }); +#endif } + +void NetworkStorageSession::saveCredentialToPersistentStorage(const ProtectionSpace& protectionSpace, const Credential& credential) +{ +#if USE(LIBSECRET) + if (m_sessionID.isEphemeral()) + return; + + if (credential.isEmpty()) + return; + + const String& realm = protectionSpace.realm(); + if (realm.isEmpty()) + return; + + GRefPtr<GHashTable> attributes = adoptGRef(secret_attributes_build(SECRET_SCHEMA_COMPAT_NETWORK, + "domain", realm.utf8().data(), + "server", protectionSpace.host().utf8().data(), + "port", protectionSpace.port(), + "protocol", schemeFromProtectionSpaceServerType(protectionSpace.serverType()), + "authtype", authTypeFromProtectionSpaceAuthenticationScheme(protectionSpace.authenticationScheme()), + nullptr)); + if (!attributes) + return; + + g_hash_table_insert(attributes.get(), g_strdup("user"), g_strdup(credential.user().utf8().data())); + CString utf8Password = credential.password().utf8(); + GRefPtr<SecretValue> newSecretValue = adoptGRef(secret_value_new(utf8Password.data(), utf8Password.length(), "text/plain")); + secret_service_store(nullptr, SECRET_SCHEMA_COMPAT_NETWORK, attributes.get(), SECRET_COLLECTION_DEFAULT, _("WebKitGTK+ password"), + newSecretValue.get(), nullptr, nullptr, nullptr); +#else + UNUSED_PARAM(protectionSpace); + UNUSED_PARAM(credential); +#endif +} + +} // namespace WebCore + +#endif // USE(SOUP) diff --git a/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp b/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp index c85fe6891..4be57ca66 100644 --- a/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp +++ b/Source/WebCore/platform/network/soup/ProxyServerSoup.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "ProxyServer.h" +#if USE(SOUP) + #include "URL.h" namespace WebCore { @@ -37,3 +39,5 @@ Vector<ProxyServer> proxyServersForURL(const URL&, const NetworkingContext*) } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceError.h b/Source/WebCore/platform/network/soup/ResourceError.h index 1a705f15e..bf0291848 100644 --- a/Source/WebCore/platform/network/soup/ResourceError.h +++ b/Source/WebCore/platform/network/soup/ResourceError.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -27,7 +27,10 @@ #define ResourceError_h #include "ResourceErrorBase.h" -#include <wtf/gobject/GRefPtr.h> + +#if USE(SOUP) + +#include <wtf/glib/GRefPtr.h> typedef struct _GTlsCertificate GTlsCertificate; typedef struct _SoupRequest SoupRequest; @@ -38,14 +41,15 @@ namespace WebCore { class ResourceError : public ResourceErrorBase { public: - ResourceError(const String& domain, int errorCode, const String& failingURL, const String& localizedDescription) - : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription) + ResourceError(Type type = Type::Null) + : ResourceErrorBase(type) , m_tlsErrors(0) { } - ResourceError() - : m_tlsErrors(0) + ResourceError(const String& domain, int errorCode, const URL& failingURL, const String& localizedDescription, Type type = Type::General) + : ResourceErrorBase(domain, errorCode, failingURL, localizedDescription, type) + , m_tlsErrors(0) { } @@ -53,7 +57,7 @@ public: static ResourceError transportError(SoupRequest*, int statusCode, const String& reasonPhrase); static ResourceError genericGError(GError*, SoupRequest*); static ResourceError tlsError(SoupRequest*, unsigned tlsErrors, GTlsCertificate*); - static ResourceError timeoutError(const String& failingURL); + static ResourceError timeoutError(const URL& failingURL); static ResourceError authenticationError(SoupMessage*); unsigned tlsErrors() const { return m_tlsErrors; } @@ -61,14 +65,18 @@ public: GTlsCertificate* certificate() const { return m_certificate.get(); } void setCertificate(GTlsCertificate* certificate) { m_certificate = certificate; } -private: - void platformCopy(ResourceError&) const; static bool platformCompare(const ResourceError& a, const ResourceError& b); +private: + friend class ResourceErrorBase; + void doPlatformIsolatedCopy(const ResourceError&); + unsigned m_tlsErrors; GRefPtr<GTlsCertificate> m_certificate; }; } +#endif + #endif // ResourceError_h_ diff --git a/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp b/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp index bb72c64c4..a608e7e10 100644 --- a/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp @@ -13,7 +13,7 @@ * THIS SOFTWARE IS PROVIDED BY IGALIA S.L. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,21 +26,22 @@ #include "config.h" #include "ResourceError.h" +#if USE(SOUP) + #include "LocalizedStrings.h" #include <libsoup/soup.h> -#include <wtf/gobject/GUniquePtr.h> +#include <wtf/glib/GUniquePtr.h> #include <wtf/text/CString.h> namespace WebCore { -static String failingURI(SoupURI* soupURI) +static URL failingURI(SoupURI* soupURI) { ASSERT(soupURI); - GUniquePtr<char> uri(soup_uri_to_string(soupURI, FALSE)); - return uri.get(); + return URL(soupURI); } -static String failingURI(SoupRequest* request) +static URL failingURI(SoupRequest* request) { ASSERT(request); return failingURI(soup_request_get_uri(request)); @@ -83,23 +84,21 @@ ResourceError ResourceError::tlsError(SoupRequest* request, unsigned tlsErrors, return resourceError; } -ResourceError ResourceError::timeoutError(const String& failingURL) +ResourceError ResourceError::timeoutError(const URL& failingURL) { - // FIXME: This should probably either be integrated into Errors(Gtk/EFL).h or the - // networking errors from those files should be moved here. + // FIXME: This should probably either be integrated into ErrorsGtk.h or the + // networking errors from that file should be moved here. // Use the same value as in NSURLError.h static const int timeoutError = -1001; static const char* const errorDomain = "WebKitNetworkError"; - ResourceError error = ResourceError(errorDomain, timeoutError, failingURL, "Request timed out"); - error.setIsTimeout(true); - return error; + return ResourceError(errorDomain, timeoutError, failingURL, "Request timed out", ResourceError::Type::Timeout); } -void ResourceError::platformCopy(ResourceError& errorCopy) const +void ResourceError::doPlatformIsolatedCopy(const ResourceError& other) { - errorCopy.m_certificate = m_certificate; - errorCopy.m_tlsErrors = m_tlsErrors; + m_certificate = other.m_certificate; + m_tlsErrors = other.m_tlsErrors; } bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& b) @@ -108,3 +107,5 @@ bool ResourceError::platformCompare(const ResourceError& a, const ResourceError& } } // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp index dffa31eaa..43be3e6a5 100644 --- a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -29,15 +29,16 @@ #include "config.h" #include "ResourceHandle.h" -#include "CookieJarSoup.h" +#if USE(SOUP) + #include "CredentialStorage.h" #include "FileSystem.h" #include "GUniquePtrSoup.h" #include "HTTPParsers.h" #include "LocalizedStrings.h" #include "MIMETypeRegistry.h" +#include "NetworkStorageSession.h" #include "NetworkingContext.h" -#include "NotImplemented.h" #include "ResourceError.h" #include "ResourceHandleClient.h" #include "ResourceHandleInternal.h" @@ -52,205 +53,26 @@ #include <libsoup/soup.h> #include <sys/stat.h> #include <sys/types.h> +#if !COMPILER(MSVC) #include <unistd.h> +#endif #include <wtf/CurrentTime.h> -#include <wtf/SHA1.h> -#include <wtf/gobject/GRefPtr.h> -#include <wtf/text/Base64.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/CString.h> -#if ENABLE(BLOB) -#include "BlobData.h" -#include "BlobRegistryImpl.h" -#include "BlobStorageData.h" -#endif - -#if PLATFORM(GTK) -#include "CredentialBackingStore.h" -#endif - namespace WebCore { -static bool loadingSynchronousRequest = false; static const size_t gDefaultReadBufferSize = 8192; -class WebCoreSynchronousLoader final : public ResourceHandleClient { - WTF_MAKE_NONCOPYABLE(WebCoreSynchronousLoader); -public: - - WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, SoupSession* session, Vector<char>& data, StoredCredentials storedCredentials) - : m_error(error) - , m_response(response) - , m_session(session) - , m_data(data) - , m_finished(false) - , m_storedCredentials(storedCredentials) - { - // We don't want any timers to fire while we are doing our synchronous load - // so we replace the thread default main context. The main loop iterations - // will only process GSources associated with this inner context. - loadingSynchronousRequest = true; - GRefPtr<GMainContext> innerMainContext = adoptGRef(g_main_context_new()); - g_main_context_push_thread_default(innerMainContext.get()); - m_mainLoop = adoptGRef(g_main_loop_new(innerMainContext.get(), false)); - -#if !SOUP_CHECK_VERSION(2, 49, 91) - adjustMaxConnections(1); -#endif - } - - ~WebCoreSynchronousLoader() - { -#if !SOUP_CHECK_VERSION(2, 49, 91) - adjustMaxConnections(-1); -#endif - - GMainContext* context = g_main_context_get_thread_default(); - while (g_main_context_pending(context)) - g_main_context_iteration(context, FALSE); - - g_main_context_pop_thread_default(context); - loadingSynchronousRequest = false; - } - -#if !SOUP_CHECK_VERSION(2, 49, 91) - void adjustMaxConnections(int adjustment) - { - int maxConnections, maxConnectionsPerHost; - g_object_get(m_session, - SOUP_SESSION_MAX_CONNS, &maxConnections, - SOUP_SESSION_MAX_CONNS_PER_HOST, &maxConnectionsPerHost, - NULL); - maxConnections += adjustment; - maxConnectionsPerHost += adjustment; - g_object_set(m_session, - SOUP_SESSION_MAX_CONNS, maxConnections, - SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost, - NULL); - - } -#endif // SOUP_CHECK_VERSION(2, 49, 91) - - virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse& response) override - { - m_response = response; - } - - virtual void didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int) override - { - ASSERT_NOT_REACHED(); - } - - virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */) override - { - // This pattern is suggested by SharedBuffer.h. - const char* segment; - unsigned position = 0; - while (unsigned length = buffer->getSomeData(segment, position)) { - m_data.append(segment, length); - position += length; - } - } - - virtual void didFinishLoading(ResourceHandle*, double) override - { - if (g_main_loop_is_running(m_mainLoop.get())) - g_main_loop_quit(m_mainLoop.get()); - m_finished = true; - } - - virtual void didFail(ResourceHandle* handle, const ResourceError& error) override - { - m_error = error; - didFinishLoading(handle, 0); - } - - virtual void didReceiveAuthenticationChallenge(ResourceHandle*, const AuthenticationChallenge& challenge) override - { - // We do not handle authentication for synchronous XMLHttpRequests. - challenge.authenticationClient()->receivedRequestToContinueWithoutCredential(challenge); - } - - virtual bool shouldUseCredentialStorage(ResourceHandle*) override - { - return m_storedCredentials == AllowStoredCredentials; - } - - void run() - { - if (!m_finished) - g_main_loop_run(m_mainLoop.get()); - } - -private: - ResourceError& m_error; - ResourceResponse& m_response; - SoupSession* m_session; - Vector<char>& m_data; - bool m_finished; - GRefPtr<GMainLoop> m_mainLoop; - StoredCredentials m_storedCredentials; -}; - -class HostTLSCertificateSet { -public: - void add(GTlsCertificate* certificate) - { - String certificateHash = computeCertificateHash(certificate); - if (!certificateHash.isEmpty()) - m_certificates.add(certificateHash); - } - - bool contains(GTlsCertificate* certificate) - { - return m_certificates.contains(computeCertificateHash(certificate)); - } - -private: - static String computeCertificateHash(GTlsCertificate* certificate) - { - GRefPtr<GByteArray> certificateData; - g_object_get(G_OBJECT(certificate), "certificate", &certificateData.outPtr(), NULL); - if (!certificateData) - return String(); - - SHA1 sha1; - sha1.addBytes(certificateData->data, certificateData->len); - - SHA1::Digest digest; - sha1.computeHash(digest); - - return base64Encode(reinterpret_cast<const char*>(digest.data()), SHA1::hashSize); - } - - HashSet<String> m_certificates; -}; - -static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&, bool isHTTPFamilyRequest); +static bool createSoupRequestAndMessageForHandle(ResourceHandle*, const ResourceRequest&); static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying = false); static void sendRequestCallback(GObject*, GAsyncResult*, gpointer); static void readCallback(GObject*, GAsyncResult*, gpointer); -static gboolean requestTimeoutCallback(void*); #if ENABLE(WEB_TIMING) -static int milisecondsSinceRequest(double requestTime); +static double milisecondsSinceRequest(double requestTime); #endif static void continueAfterDidReceiveResponse(ResourceHandle*); -static bool gIgnoreSSLErrors = false; - -static HashSet<String>& allowsAnyHTTPSCertificateHosts() -{ - DEFINE_STATIC_LOCAL(HashSet<String>, hosts, ()); - return hosts; -} - -typedef HashMap<String, HostTLSCertificateSet> CertificatesMap; -static CertificatesMap& clientCertificates() -{ - DEFINE_STATIC_LOCAL(CertificatesMap, certificates, ()); - return certificates; -} - ResourceHandleInternal::~ResourceHandleInternal() { } @@ -258,8 +80,8 @@ ResourceHandleInternal::~ResourceHandleInternal() static SoupSession* sessionFromContext(NetworkingContext* context) { if (!context || !context->isValid()) - return SoupNetworkSession::defaultSession().soupSession(); - return context->storageSession().soupNetworkSession().soupSession(); + return NetworkStorageSession::defaultStorageSession().getOrCreateSoupNetworkSession().soupSession(); + return context->storageSession().getOrCreateSoupNetworkSession().soupSession(); } ResourceHandle::~ResourceHandle() @@ -269,7 +91,36 @@ ResourceHandle::~ResourceHandle() SoupSession* ResourceHandleInternal::soupSession() { - return sessionFromContext(m_context.get()); + return m_session ? m_session->soupSession() : sessionFromContext(m_context.get()); +} + +RefPtr<ResourceHandle> ResourceHandle::create(SoupNetworkSession& session, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) +{ + auto newHandle = adoptRef(*new ResourceHandle(session, request, client, defersLoading, shouldContentSniff)); + + if (newHandle->d->m_scheduledFailureType != NoFailure) + return WTFMove(newHandle); + + if (newHandle->start()) + return WTFMove(newHandle); + + return nullptr; +} + +ResourceHandle::ResourceHandle(SoupNetworkSession& session, const ResourceRequest& request, ResourceHandleClient* client, bool defersLoading, bool shouldContentSniff) + : d(std::make_unique<ResourceHandleInternal>(this, nullptr, request, client, defersLoading, shouldContentSniff && shouldContentSniffURL(request.url()))) +{ + if (!request.url().isValid()) { + scheduleFailure(InvalidURLFailure); + return; + } + + if (!portAllowed(request.url())) { + scheduleFailure(BlockedFailure); + return; + } + + d->m_session = &session; } bool ResourceHandle::cancelledOrClientless() @@ -305,39 +156,19 @@ static bool isAuthenticationFailureStatusCode(int httpStatusCode) return httpStatusCode == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || httpStatusCode == SOUP_STATUS_UNAUTHORIZED; } -static bool handleUnignoredTLSErrors(ResourceHandle* handle, SoupMessage* message) -{ - if (gIgnoreSSLErrors) - return false; - - GTlsCertificate* certificate = nullptr; - GTlsCertificateFlags tlsErrors = static_cast<GTlsCertificateFlags>(0); - soup_message_get_https_status(message, &certificate, &tlsErrors); - if (!tlsErrors) - return false; - - String lowercaseHostURL = handle->firstRequest().url().host().lower(); - if (allowsAnyHTTPSCertificateHosts().contains(lowercaseHostURL)) - return false; - - // We aren't ignoring errors globally, but the user may have already decided to accept this certificate. - auto it = clientCertificates().find(lowercaseHostURL); - if (it != clientCertificates().end() && it->value.contains(certificate)) - return false; - - ResourceHandleInternal* d = handle->getInternal(); - handle->client()->didFail(handle, ResourceError::tlsError(d->m_soupRequest.get(), tlsErrors, certificate)); - return true; -} - static void tlsErrorsChangedCallback(SoupMessage* message, GParamSpec*, gpointer data) { - ResourceHandle* handle = static_cast<ResourceHandle*>(data); + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); if (!handle || handle->cancelledOrClientless()) return; - if (handleUnignoredTLSErrors(handle, message)) + SoupNetworkSession::checkTLSErrors(handle->getInternal()->m_soupRequest.get(), message, [handle] (const ResourceError& error) { + if (error.isNull()) + return; + + handle->client()->didFail(handle.get(), error); handle->cancel(); + }); } static void gotHeadersCallback(SoupMessage* message, gpointer data) @@ -348,22 +179,17 @@ static void gotHeadersCallback(SoupMessage* message, gpointer data) ResourceHandleInternal* d = handle->getInternal(); -#if ENABLE(WEB_TIMING) - if (d->m_response.resourceLoadTiming()) - d->m_response.resourceLoadTiming()->receiveHeadersEnd = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); -#endif - -#if PLATFORM(GTK) - // We are a bit more conservative with the persistent credential storage than the session store, - // since we are waiting until we know that this authentication succeeded before actually storing. - // This is because we want to avoid hitting the disk twice (once to add and once to remove) for - // incorrect credentials or polluting the keychain with invalid credentials. - if (!isAuthenticationFailureStatusCode(message->status_code) && message->status_code < 500 && !d->m_credentialDataToSaveInPersistentStore.credential.isEmpty()) { - credentialBackingStore().storeCredentialsForChallenge( - d->m_credentialDataToSaveInPersistentStore.challenge, - d->m_credentialDataToSaveInPersistentStore.credential); + if (d->m_context && d->m_context->isValid()) { + // We are a bit more conservative with the persistent credential storage than the session store, + // since we are waiting until we know that this authentication succeeded before actually storing. + // This is because we want to avoid hitting the disk twice (once to add and once to remove) for + // incorrect credentials or polluting the keychain with invalid credentials. + if (!isAuthenticationFailureStatusCode(message->status_code) && message->status_code < 500) { + d->m_context->storageSession().saveCredentialToPersistentStorage( + d->m_credentialDataToSaveInPersistentStore.protectionSpace, + d->m_credentialDataToSaveInPersistentStore.credential); + } } -#endif // The original response will be needed later to feed to willSendRequest in // doRedirect() in case we are redirected. For this reason, we store it here. @@ -375,15 +201,17 @@ static void applyAuthenticationToRequest(ResourceHandle* handle, ResourceRequest // m_user/m_pass are credentials given manually, for instance, by the arguments passed to XMLHttpRequest.open(). ResourceHandleInternal* d = handle->getInternal(); + String partition = request.cachePartition(); + if (handle->shouldUseCredentialStorage()) { if (d->m_user.isEmpty() && d->m_pass.isEmpty()) - d->m_initialCredential = CredentialStorage::get(request.url()); + d->m_initialCredential = CredentialStorage::defaultCredentialStorage().get(partition, request.url()); else if (!redirect) { // If there is already a protection space known for the URL, update stored credentials // before sending a request. This makes it possible to implement logout by sending an // XMLHttpRequest with known incorrect credentials, and aborting it immediately (so that // an authentication dialog doesn't pop up). - CredentialStorage::set(Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url()); + CredentialStorage::defaultCredentialStorage().set(partition, Credential(d->m_user, d->m_pass, CredentialPersistenceNone), request.url()); } } @@ -419,10 +247,7 @@ static void restartedCallback(SoupMessage*, gpointer data) if (!handle || handle->cancelledOrClientless()) return; - ResourceHandleInternal* d = handle->getInternal(); - ResourceResponse& redirectResponse = d->m_response; - redirectResponse.setResourceLoadTiming(ResourceLoadTiming::create()); - redirectResponse.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime(); + handle->m_requestTime = monotonicallyIncreasingTime(); } #endif @@ -465,18 +290,17 @@ static bool shouldRedirectAsGET(SoupMessage* message, URL& newURL, bool crossOri return false; } -static void continueAfterWillSendRequest(ResourceHandle* handle, const ResourceRequest& request) +static void continueAfterWillSendRequest(ResourceHandle* handle, ResourceRequest&& request) { // willSendRequest might cancel the load. if (handle->cancelledOrClientless()) return; - ResourceRequest newRequest(request); ResourceHandleInternal* d = handle->getInternal(); - if (protocolHostAndPortAreEqual(newRequest.url(), d->m_response.url())) - applyAuthenticationToRequest(handle, newRequest, true); + if (protocolHostAndPortAreEqual(request.url(), d->m_response.url())) + applyAuthenticationToRequest(handle, request, true); - if (!createSoupRequestAndMessageForHandle(handle, newRequest, true)) { + if (!createSoupRequestAndMessageForHandle(handle, request)) { d->client()->cannotShowURL(handle); return; } @@ -501,14 +325,13 @@ static void doRedirect(ResourceHandle* handle) URL newURL = URL(URL(soup_message_get_uri(message)), location); bool crossOrigin = !protocolHostAndPortAreEqual(handle->firstRequest().url(), newURL); newRequest.setURL(newURL); - newRequest.setFirstPartyForCookies(newURL); if (newRequest.httpMethod() != "GET") { // Change newRequest method to GET if change was made during a previous redirection // or if current redirection says so if (message->method == SOUP_METHOD_GET || shouldRedirectAsGET(message, newURL, crossOrigin)) { newRequest.setHTTPMethod("GET"); - newRequest.setHTTPBody(0); + newRequest.setHTTPBody(nullptr); newRequest.clearHTTPContentType(); } } @@ -533,11 +356,12 @@ static void doRedirect(ResourceHandle* handle) cleanupSoupRequestOperation(handle); + ResourceResponse responseCopy = d->m_response; if (d->client()->usesAsyncCallbacks()) - d->client()->willSendRequestAsync(handle, newRequest, d->m_response); + d->client()->willSendRequestAsync(handle, WTFMove(newRequest), WTFMove(responseCopy)); else { - d->client()->willSendRequest(handle, newRequest, d->m_response); - continueAfterWillSendRequest(handle, newRequest); + auto request = d->client()->willSendRequest(handle, WTFMove(newRequest), WTFMove(responseCopy)); + continueAfterWillSendRequest(handle, WTFMove(request)); } } @@ -603,10 +427,7 @@ static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroyin d->m_soupMessage.clear(); } - if (d->m_timeoutSource) { - g_source_destroy(d->m_timeoutSource.get()); - d->m_timeoutSource.clear(); - } + d->m_timeoutSource.stop(); if (!isDestroying) handle->deref(); @@ -657,12 +478,7 @@ static void nextMultipartResponsePartCallback(GObject* /*source*/, GAsyncResult* d->m_previousPosition = 0; - if (handle->client()->usesAsyncCallbacks()) - handle->client()->didReceiveResponseAsync(handle.get(), d->m_response); - else { - handle->client()->didReceiveResponse(handle.get(), d->m_response); - continueAfterDidReceiveResponse(handle.get()); - } + handle->didReceiveResponse(ResourceResponse(d->m_response)); } static void sendRequestCallback(GObject*, GAsyncResult* result, gpointer data) @@ -712,17 +528,21 @@ static void sendRequestCallback(GObject*, GAsyncResult* result, gpointer data) d->m_response.setExpectedContentLength(soup_request_get_content_length(d->m_soupRequest.get())); } +#if ENABLE(WEB_TIMING) + d->m_response.networkLoadTiming().responseStart = milisecondsSinceRequest(handle->m_requestTime); +#endif + if (soupMessage && d->m_response.isMultipart()) d->m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(soupMessage, inputStream.get())); else d->m_inputStream = inputStream; - if (d->client()->usesAsyncCallbacks()) - handle->client()->didReceiveResponseAsync(handle.get(), d->m_response); - else { - handle->client()->didReceiveResponse(handle.get(), d->m_response); - continueAfterDidReceiveResponse(handle.get()); - } + handle->didReceiveResponse(ResourceResponse(d->m_response)); +} + +void ResourceHandle::platformContinueSynchronousDidReceiveResponse() +{ + continueAfterDidReceiveResponse(this); } static void continueAfterDidReceiveResponse(ResourceHandle* handle) @@ -745,141 +565,23 @@ static void continueAfterDidReceiveResponse(ResourceHandle* handle) G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, handle); } -static bool addFileToSoupMessageBody(SoupMessage* message, const String& fileNameString, size_t offset, size_t lengthToSend, unsigned long& totalBodySize) -{ - GUniqueOutPtr<GError> error; - CString fileName = fileSystemRepresentation(fileNameString); - GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); - if (error) - return false; - - gsize bufferLength = lengthToSend; - if (!lengthToSend) - bufferLength = g_mapped_file_get_length(fileMapping); - totalBodySize += bufferLength; - - SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping) + offset, - bufferLength, - fileMapping, - reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); - soup_message_body_append_buffer(message->request_body, soupBuffer); - soup_buffer_free(soupBuffer); - return true; -} - -#if ENABLE(BLOB) -static bool blobIsOutOfDate(const BlobDataItem& blobItem) -{ - ASSERT(blobItem.type == BlobDataItem::File); - if (!isValidFileTime(blobItem.expectedModificationTime)) - return false; - - time_t fileModificationTime; - if (!getFileModificationTime(blobItem.path, fileModificationTime)) - return true; - - return fileModificationTime != static_cast<time_t>(blobItem.expectedModificationTime); -} - -static void addEncodedBlobItemToSoupMessageBody(SoupMessage* message, const BlobDataItem& blobItem, unsigned long& totalBodySize) -{ - if (blobItem.type == BlobDataItem::Data) { - totalBodySize += blobItem.length; - soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY, - blobItem.data->data() + blobItem.offset, blobItem.length); - return; - } - - ASSERT(blobItem.type == BlobDataItem::File); - if (blobIsOutOfDate(blobItem)) - return; - - addFileToSoupMessageBody(message, - blobItem.path, - blobItem.offset, - blobItem.length == BlobDataItem::toEndOfFile ? 0 : blobItem.length, - totalBodySize); -} - -static void addEncodedBlobToSoupMessageBody(SoupMessage* message, const FormDataElement& element, unsigned long& totalBodySize) -{ - RefPtr<BlobStorageData> blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(URL(ParsedURLString, element.m_url)); - if (!blobData) - return; - - for (size_t i = 0; i < blobData->items().size(); ++i) - addEncodedBlobItemToSoupMessageBody(message, blobData->items()[i], totalBodySize); -} -#endif // ENABLE(BLOB) - -static bool addFormElementsToSoupMessage(SoupMessage* message, const char*, FormData* httpBody, unsigned long& totalBodySize) -{ - soup_message_body_set_accumulate(message->request_body, FALSE); - size_t numElements = httpBody->elements().size(); - for (size_t i = 0; i < numElements; i++) { - const FormDataElement& element = httpBody->elements()[i]; - - if (element.m_type == FormDataElement::data) { - totalBodySize += element.m_data.size(); - soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY, - element.m_data.data(), element.m_data.size()); - continue; - } - - if (element.m_type == FormDataElement::encodedFile) { - if (!addFileToSoupMessageBody(message , - element.m_filename, - 0 /* offset */, - 0 /* lengthToSend */, - totalBodySize)) - return false; - continue; - } - -#if ENABLE(BLOB) - ASSERT(element.m_type == FormDataElement::encodedBlob); - addEncodedBlobToSoupMessageBody(message, element, totalBodySize); -#endif - } - return true; -} - #if ENABLE(WEB_TIMING) -static int milisecondsSinceRequest(double requestTime) +static double milisecondsSinceRequest(double requestTime) { - return static_cast<int>((monotonicallyIncreasingTime() - requestTime) * 1000.0); + return (monotonicallyIncreasingTime() - requestTime) * 1000.0; } -static void wroteBodyCallback(SoupMessage*, gpointer data) +void ResourceHandle::didStartRequest() { - RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); - if (!handle) - return; - - ResourceHandleInternal* d = handle->getInternal(); - if (!d->m_response.resourceLoadTiming()) - return; - - d->m_response.resourceLoadTiming()->sendEnd = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); + getInternal()->m_response.networkLoadTiming().requestStart = milisecondsSinceRequest(m_requestTime); } -void ResourceHandle::didStartRequest() +#if SOUP_CHECK_VERSION(2, 49, 91) +static void startingCallback(SoupMessage*, ResourceHandle* handle) { - ResourceHandleInternal* d = getInternal(); - if (!d->m_response.resourceLoadTiming()) - return; - - d->m_response.resourceLoadTiming()->sendStart = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); - if (d->m_response.resourceLoadTiming()->sslStart != -1) { - // WebCore/inspector/front-end/RequestTimingView.js assumes - // that SSL time is included in connection time so must - // substract here the SSL delta that will be added later (see - // WebInspector.RequestTimingView.createTimingTable in the - // file above for more details). - d->m_response.resourceLoadTiming()->sendStart -= - d->m_response.resourceLoadTiming()->sslEnd - d->m_response.resourceLoadTiming()->sslStart; - } + handle->didStartRequest(); } +#endif // SOUP_CHECK_VERSION(2, 49, 91) static void networkEventCallback(SoupMessage*, GSocketClientEvent event, GIOStream*, gpointer data) { @@ -891,43 +593,41 @@ static void networkEventCallback(SoupMessage*, GSocketClientEvent event, GIOStre return; ResourceHandleInternal* d = handle->getInternal(); - int deltaTime = milisecondsSinceRequest(d->m_response.resourceLoadTiming()->requestTime); + double deltaTime = milisecondsSinceRequest(handle->m_requestTime); switch (event) { case G_SOCKET_CLIENT_RESOLVING: - d->m_response.resourceLoadTiming()->dnsStart = deltaTime; + d->m_response.networkLoadTiming().domainLookupStart = deltaTime; break; case G_SOCKET_CLIENT_RESOLVED: - d->m_response.resourceLoadTiming()->dnsEnd = deltaTime; + d->m_response.networkLoadTiming().domainLookupEnd = deltaTime; break; case G_SOCKET_CLIENT_CONNECTING: - d->m_response.resourceLoadTiming()->connectStart = deltaTime; - if (d->m_response.resourceLoadTiming()->dnsStart != -1) + d->m_response.networkLoadTiming().connectStart = deltaTime; + if (d->m_response.networkLoadTiming().domainLookupStart != -1) { // WebCore/inspector/front-end/RequestTimingView.js assumes // that DNS time is included in connection time so must // substract here the DNS delta that will be added later (see // WebInspector.RequestTimingView.createTimingTable in the // file above for more details). - d->m_response.resourceLoadTiming()->connectStart -= - d->m_response.resourceLoadTiming()->dnsEnd - d->m_response.resourceLoadTiming()->dnsStart; + d->m_response.networkLoadTiming().connectStart -= + d->m_response.networkLoadTiming().domainLookupEnd - d->m_response.networkLoadTiming().domainLookupStart; + } break; case G_SOCKET_CLIENT_CONNECTED: // Web Timing considers that connection time involves dns, proxy & TLS negotiation... // so we better pick G_SOCKET_CLIENT_COMPLETE for connectEnd break; case G_SOCKET_CLIENT_PROXY_NEGOTIATING: - d->m_response.resourceLoadTiming()->proxyStart = deltaTime; break; case G_SOCKET_CLIENT_PROXY_NEGOTIATED: - d->m_response.resourceLoadTiming()->proxyEnd = deltaTime; break; case G_SOCKET_CLIENT_TLS_HANDSHAKING: - d->m_response.resourceLoadTiming()->sslStart = deltaTime; + d->m_response.networkLoadTiming().secureConnectionStart = deltaTime; break; case G_SOCKET_CLIENT_TLS_HANDSHAKED: - d->m_response.resourceLoadTiming()->sslEnd = deltaTime; break; case G_SOCKET_CLIENT_COMPLETE: - d->m_response.resourceLoadTiming()->connectEnd = deltaTime; + d->m_response.networkLoadTiming().connectEnd = deltaTime; break; default: ASSERT_NOT_REACHED(); @@ -949,6 +649,7 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R SoupMessage* soupMessage = d->m_soupMessage.get(); request.updateSoupMessage(soupMessage); + d->m_bodySize = soupMessage->request_body->length; g_object_set_data(G_OBJECT(soupMessage), "handle", handle); if (!handle->shouldContentSniff()) @@ -956,14 +657,6 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R if (!d->m_useAuthenticationManager) soup_message_disable_feature(soupMessage, SOUP_TYPE_AUTH_MANAGER); - FormData* httpBody = request.httpBody(); - CString contentType = request.httpContentType().utf8().data(); - if (httpBody && !httpBody->isEmpty() && !addFormElementsToSoupMessage(soupMessage, contentType.data(), httpBody, d->m_bodySize)) { - // We failed to prepare the body data, so just fail this load. - d->m_soupMessage.clear(); - return false; - } - // Make sure we have an Accept header for subresources; some sites // want this to serve some of their subresources if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept")) @@ -973,8 +666,7 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R // for consistency with other backends (e.g. Chromium's) and other UA implementations like FF. It's done // in the backend here instead of in XHR code since in XHR CORS checking prevents us from this kind of // late header manipulation. - if ((request.httpMethod() == "POST" || request.httpMethod() == "PUT") - && (!request.httpBody() || request.httpBody()->isEmpty())) + if ((request.httpMethod() == "POST" || request.httpMethod() == "PUT") && !d->m_bodySize) soup_message_headers_set_content_length(soupMessage->request_headers, 0); g_signal_connect(d->m_soupMessage.get(), "notify::tls-errors", G_CALLBACK(tlsErrorsChangedCallback), handle); @@ -982,19 +674,14 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R g_signal_connect(d->m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle); unsigned flags = SOUP_MESSAGE_NO_REDIRECT; -#if SOUP_CHECK_VERSION(2, 49, 91) - // Ignore the connection limits in synchronous loads to avoid freezing the networking process. - // See https://bugs.webkit.org/show_bug.cgi?id=141508. - if (loadingSynchronousRequest) - flags |= SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS; -#endif soup_message_set_flags(d->m_soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(d->m_soupMessage.get()) | flags)); #if ENABLE(WEB_TIMING) - d->m_response.setResourceLoadTiming(ResourceLoadTiming::create()); +#if SOUP_CHECK_VERSION(2, 49, 91) + g_signal_connect(d->m_soupMessage.get(), "starting", G_CALLBACK(startingCallback), handle); +#endif g_signal_connect(d->m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), handle); g_signal_connect(d->m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), handle); - g_signal_connect(d->m_soupMessage.get(), "wrote-body", G_CALLBACK(wroteBodyCallback), handle); #endif #if SOUP_CHECK_VERSION(2, 43, 1) @@ -1004,7 +691,7 @@ static bool createSoupMessageForHandleAndRequest(ResourceHandle* handle, const R return true; } -static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request, bool isHTTPFamilyRequest) +static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const ResourceRequest& request) { ResourceHandleInternal* d = handle->getInternal(); @@ -1020,7 +707,7 @@ static bool createSoupRequestAndMessageForHandle(ResourceHandle* handle, const R } // SoupMessages are only applicable to HTTP-family requests. - if (isHTTPFamilyRequest && !createSoupMessageForHandleAndRequest(handle, request)) { + if (request.url().protocolIsInHTTPFamily() && !createSoupMessageForHandleAndRequest(handle, request)) { d->m_soupRequest.clear(); return false; } @@ -1044,15 +731,14 @@ bool ResourceHandle::start() // Only allow the POST and GET methods for non-HTTP requests. const ResourceRequest& request = firstRequest(); - bool isHTTPFamilyRequest = request.url().protocolIsInHTTPFamily(); - if (!isHTTPFamilyRequest && request.httpMethod() != "GET" && request.httpMethod() != "POST") { + if (!request.url().protocolIsInHTTPFamily() && request.httpMethod() != "GET" && request.httpMethod() != "POST") { this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately return true; } applyAuthenticationToRequest(this, firstRequest(), false); - if (!createSoupRequestAndMessageForHandle(this, request, isHTTPFamilyRequest)) { + if (!createSoupRequestAndMessageForHandle(this, request)) { this->scheduleFailure(InvalidURLFailure); // Error must not be reported immediately return true; } @@ -1064,19 +750,36 @@ bool ResourceHandle::start() return true; } +RefPtr<ResourceHandle> ResourceHandle::releaseForDownload(ResourceHandleClient* downloadClient) +{ + // We don't adopt the ref, as it will be released by cleanupSoupRequestOperation, which should always run. + ResourceHandle* newHandle = new ResourceHandle(d->m_context.get(), firstRequest(), nullptr, d->m_defersLoading, d->m_shouldContentSniff); + newHandle->relaxAdoptionRequirement(); + std::swap(d, newHandle->d); + + g_signal_handlers_disconnect_matched(newHandle->d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this); + g_object_set_data(G_OBJECT(newHandle->d->m_soupMessage.get()), "handle", newHandle); + + newHandle->d->m_client = downloadClient; + continueAfterDidReceiveResponse(newHandle); + + return newHandle; +} + +void ResourceHandle::timeoutFired() +{ + client()->didFail(this, ResourceError::timeoutError(firstRequest().url())); + cancel(); +} + void ResourceHandle::sendPendingRequest() { #if ENABLE(WEB_TIMING) - if (d->m_response.resourceLoadTiming()) - d->m_response.resourceLoadTiming()->requestTime = monotonicallyIncreasingTime(); + m_requestTime = monotonicallyIncreasingTime(); #endif - if (d->m_firstRequest.timeoutInterval() > 0) { - // soup_add_timeout returns a GSource* whose only reference is owned by - // the context. We need to have our own reference to it, hence not using adoptRef. - d->m_timeoutSource = soup_add_timeout(g_main_context_get_thread_default(), - d->m_firstRequest.timeoutInterval() * 1000, requestTimeoutCallback, this); - } + if (d->m_firstRequest.timeoutInterval() > 0) + d->m_timeoutSource.startOneShot(d->m_firstRequest.timeoutInterval()); // Balanced by a deref() in cleanupSoupRequestOperation, which should always run. ref(); @@ -1099,46 +802,21 @@ bool ResourceHandle::shouldUseCredentialStorage() return (!client() || client()->shouldUseCredentialStorage(this)) && firstRequest().url().protocolIsInHTTPFamily(); } -void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) -{ - allowsAnyHTTPSCertificateHosts().add(host.lower()); -} - -void ResourceHandle::setClientCertificate(const String& host, GTlsCertificate* certificate) -{ - clientCertificates().add(host.lower(), HostTLSCertificateSet()).iterator->value.add(certificate); -} - -void ResourceHandle::setIgnoreSSLErrors(bool ignoreSSLErrors) -{ - gIgnoreSSLErrors = ignoreSSLErrors; -} - -#if PLATFORM(GTK) -void getCredentialFromPersistentStoreCallback(const Credential& credential, void* data) -{ - static_cast<ResourceHandle*>(data)->continueDidReceiveAuthenticationChallenge(credential); -} -#endif - void ResourceHandle::continueDidReceiveAuthenticationChallenge(const Credential& credentialFromPersistentStorage) { ASSERT(!d->m_currentWebChallenge.isNull()); AuthenticationChallenge& challenge = d->m_currentWebChallenge; - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); + ASSERT(d->m_soupMessage); if (!credentialFromPersistentStorage.isEmpty()) challenge.setProposedCredential(credentialFromPersistentStorage); if (!client()) { - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); clearAuthentication(); return; } - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); client()->didReceiveAuthenticationChallenge(this, challenge); } @@ -1146,6 +824,8 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall { ASSERT(d->m_currentWebChallenge.isNull()); + String partition = firstRequest().cachePartition(); + // FIXME: Per the specification, the user shouldn't be asked for credentials if there were incorrect ones provided explicitly. bool useCredentialStorage = shouldUseCredentialStorage(); if (useCredentialStorage) { @@ -1153,17 +833,17 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall // The stored credential wasn't accepted, stop using it. There is a race condition // here, since a different credential might have already been stored by another // ResourceHandle, but the observable effect should be very minor, if any. - CredentialStorage::remove(challenge.protectionSpace()); + CredentialStorage::defaultCredentialStorage().remove(partition, challenge.protectionSpace()); } if (!challenge.previousFailureCount()) { - Credential credential = CredentialStorage::get(challenge.protectionSpace()); + Credential credential = CredentialStorage::defaultCredentialStorage().get(partition, challenge.protectionSpace()); if (!credential.isEmpty() && credential != d->m_initialCredential) { ASSERT(credential.persistence() == CredentialPersistenceNone); // Store the credential back, possibly adding it as a default for this directory. if (isAuthenticationFailureStatusCode(challenge.failureResponse().httpStatusCode())) - CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url()); + CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url()); soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data()); return; @@ -1172,18 +852,18 @@ void ResourceHandle::didReceiveAuthenticationChallenge(const AuthenticationChall } d->m_currentWebChallenge = challenge; - soup_session_pause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_pause_message(d->soupSession(), d->m_soupMessage.get()); -#if PLATFORM(GTK) // We could also do this before we even start the request, but that would be at the expense // of all request latency, versus a one-time latency for the small subset of requests that // use HTTP authentication. In the end, this doesn't matter much, because persistent credentials // will become session credentials after the first use. - if (useCredentialStorage) { - credentialBackingStore().credentialForChallenge(challenge, getCredentialFromPersistentStoreCallback, this); + if (useCredentialStorage && d->m_context && d->m_context->isValid()) { + d->m_context->storageSession().getCredentialFromPersistentStorage(challenge.protectionSpace(), [this, protectedThis = makeRef(*this)] (Credential&& credential) { + continueDidReceiveAuthenticationChallenge(WTFMove(credential)); + }); return; } -#endif continueDidReceiveAuthenticationChallenge(Credential()); } @@ -1193,7 +873,7 @@ void ResourceHandle::receivedRequestToContinueWithoutCredential(const Authentica ASSERT(!challenge.isNull()); if (challenge != d->m_currentWebChallenge) return; - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); clearAuthentication(); } @@ -1210,26 +890,25 @@ void ResourceHandle::receivedCredential(const AuthenticationChallenge& challenge return; } + String partition = firstRequest().cachePartition(); + if (shouldUseCredentialStorage()) { // Eventually we will manage per-session credentials only internally or use some newly-exposed API from libsoup, // because once we authenticate via libsoup, there is no way to ignore it for a particular request. Right now, // we place the credentials in the store even though libsoup will never fire the authenticate signal again for // this protection space. if (credential.persistence() == CredentialPersistenceForSession || credential.persistence() == CredentialPersistencePermanent) - CredentialStorage::set(credential, challenge.protectionSpace(), challenge.failureResponse().url()); + CredentialStorage::defaultCredentialStorage().set(partition, credential, challenge.protectionSpace(), challenge.failureResponse().url()); -#if PLATFORM(GTK) if (credential.persistence() == CredentialPersistencePermanent) { d->m_credentialDataToSaveInPersistentStore.credential = credential; - d->m_credentialDataToSaveInPersistentStore.challenge = challenge; + d->m_credentialDataToSaveInPersistentStore.protectionSpace = challenge.protectionSpace(); } -#endif } - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); + ASSERT(d->m_soupMessage); soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data()); - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); clearAuthentication(); } @@ -1245,9 +924,8 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen return; } - ASSERT(challenge.soupSession()); - ASSERT(challenge.soupMessage()); - soup_session_unpause_message(challenge.soupSession(), challenge.soupMessage()); + ASSERT(d->m_soupMessage); + soup_session_unpause_message(d->soupSession(), d->m_soupMessage.get()); if (client()) client()->receivedCancellation(this, challenge); @@ -1255,6 +933,18 @@ void ResourceHandle::receivedCancellation(const AuthenticationChallenge& challen clearAuthentication(); } +void ResourceHandle::receivedRequestToPerformDefaultHandling(const AuthenticationChallenge&) +{ + ASSERT_NOT_REACHED(); +} + +void ResourceHandle::receivedChallengeRejection(const AuthenticationChallenge& challenge) +{ + // This is only used by layout tests, soup based ports don't implement this. + notImplemented(); + receivedRequestToContinueWithoutCredential(challenge); +} + static bool waitingToSendRequest(ResourceHandle* handle) { // We need to check for d->m_soupRequest because the request may have raised a failure @@ -1270,10 +960,7 @@ void ResourceHandle::platformSetDefersLoading(bool defersLoading) // Except when canceling a possible timeout timer, we only need to take action here to UN-defer loading. if (defersLoading) { - if (d->m_timeoutSource) { - g_source_destroy(d->m_timeoutSource.get()); - d->m_timeoutSource.clear(); - } + d->m_timeoutSource.stop(); return; } @@ -1292,27 +979,9 @@ void ResourceHandle::platformSetDefersLoading(bool defersLoading) } } -bool ResourceHandle::loadsBlocked() +void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>&) { - return false; -} - -void ResourceHandle::platformLoadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials storedCredentials, ResourceError& error, ResourceResponse& response, Vector<char>& data) -{ - ASSERT(!loadingSynchronousRequest); - if (loadingSynchronousRequest) // In practice this cannot happen, but if for some reason it does, - return; // we want to avoid accidentally going into an infinite loop of requests. - - WebCoreSynchronousLoader syncLoader(error, response, sessionFromContext(context), data, storedCredentials); - RefPtr<ResourceHandle> handle = create(context, request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/); - if (!handle) - return; - - // If the request has already failed, do not run the main loop, or else we'll block indefinitely. - if (handle->d->m_scheduledFailureType != NoFailure) - return; - - syncLoader.run(); + ASSERT_NOT_REACHED(); } static void readCallback(GObject*, GAsyncResult* asyncResult, gpointer data) @@ -1378,34 +1047,18 @@ static void readCallback(GObject*, GAsyncResult* asyncResult, gpointer data) d->m_cancellable.get(), readCallback, handle.get()); } -void ResourceHandle::continueWillSendRequest(const ResourceRequest& request) +void ResourceHandle::continueWillSendRequest(ResourceRequest&& request) { - ASSERT(client()); - ASSERT(client()->usesAsyncCallbacks()); - continueAfterWillSendRequest(this, request); + ASSERT(!client() || client()->usesAsyncCallbacks()); + continueAfterWillSendRequest(this, WTFMove(request)); } void ResourceHandle::continueDidReceiveResponse() { - ASSERT(client()); - ASSERT(client()->usesAsyncCallbacks()); + ASSERT(!client() || client()->usesAsyncCallbacks()); continueAfterDidReceiveResponse(this); } -void ResourceHandle::continueShouldUseCredentialStorage(bool) -{ - ASSERT(client()); - ASSERT(client()->usesAsyncCallbacks()); - // FIXME: Implement this method if needed: https://bugs.webkit.org/show_bug.cgi?id=126114. } -static gboolean requestTimeoutCallback(gpointer data) -{ - RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); - handle->client()->didFail(handle.get(), ResourceError::timeoutError(handle->getInternal()->m_firstRequest.url().string())); - handle->cancel(); - - return FALSE; -} - -} +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceRequest.h b/Source/WebCore/platform/network/soup/ResourceRequest.h index 45b1ce880..04d5d4841 100644 --- a/Source/WebCore/platform/network/soup/ResourceRequest.h +++ b/Source/WebCore/platform/network/soup/ResourceRequest.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006 Apple Inc. All rights reserved. * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> * * Redistribution and use in source and binary forms, with or without @@ -11,10 +11,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -86,7 +86,7 @@ namespace WebCore { updateFromSoupRequest(soupRequest); } - void updateFromDelegatePreservingOldHTTPBody(const ResourceRequest& delegateProvidedRequest) { *this = delegateProvidedRequest; } + void updateFromDelegatePreservingOldProperties(const ResourceRequest& delegateProvidedRequest) { *this = delegateProvidedRequest; } bool acceptEncoding() const { return m_acceptEncoding; } void setAcceptEncoding(bool acceptEncoding) { m_acceptEncoding = acceptEncoding; } @@ -94,7 +94,6 @@ namespace WebCore { void updateSoupMessageHeaders(SoupMessageHeaders*) const; void updateFromSoupMessageHeaders(SoupMessageHeaders*); void updateSoupMessage(SoupMessage*) const; - SoupMessage* toSoupMessage() const; void updateFromSoupMessage(SoupMessage*); void updateSoupRequest(SoupRequest*) const; void updateFromSoupRequest(SoupRequest*); @@ -107,6 +106,9 @@ namespace WebCore { GUniquePtr<SoupURI> createSoupURI() const; + template<class Encoder> void encodeWithPlatformData(Encoder&) const; + template<class Decoder> bool decodeWithPlatformData(Decoder&); + private: friend class ResourceRequestBase; @@ -115,33 +117,74 @@ namespace WebCore { uint64_t m_initiatingPageID; void updateSoupMessageMembers(SoupMessage*) const; + void updateSoupMessageBody(SoupMessage*) const; void doUpdatePlatformRequest() { } void doUpdateResourceRequest() { } void doUpdatePlatformHTTPBody() { } void doUpdateResourceHTTPBody() { } - PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } - void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + void doPlatformSetAsIsolatedCopy(const ResourceRequest&) { } }; - struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { - }; +template<class Encoder> +void ResourceRequest::encodeWithPlatformData(Encoder& encoder) const +{ + encodeBase(encoder); + + // FIXME: Do not encode HTTP message body. + // 1. It can be large and thus costly to send across. + // 2. It is misleading to provide a body with some requests, while others use body streams, which cannot be serialized at all. + encoder << static_cast<bool>(m_httpBody); + if (m_httpBody) + encoder << m_httpBody->flattenToString(); + + encoder << static_cast<uint32_t>(m_soupFlags); + encoder << m_initiatingPageID; +} + +template<class Decoder> +bool ResourceRequest::decodeWithPlatformData(Decoder& decoder) +{ + if (!decodeBase(decoder)) + return false; + + bool hasHTTPBody; + if (!decoder.decode(hasHTTPBody)) + return false; + if (hasHTTPBody) { + String httpBody; + if (!decoder.decode(httpBody)) + return false; + setHTTPBody(FormData::create(httpBody.utf8())); + } + + uint32_t soupMessageFlags; + if (!decoder.decode(soupMessageFlags)) + return false; + m_soupFlags = static_cast<SoupMessageFlags>(soupMessageFlags); + + uint64_t initiatingPageID; + if (!decoder.decode(initiatingPageID)) + return false; + m_initiatingPageID = initiatingPageID; + + return true; +} + #if SOUP_CHECK_VERSION(2, 43, 1) inline SoupMessagePriority toSoupMessagePriority(ResourceLoadPriority priority) { switch (priority) { - case ResourceLoadPriorityUnresolved: - return SOUP_MESSAGE_PRIORITY_NORMAL; - case ResourceLoadPriorityVeryLow: + case ResourceLoadPriority::VeryLow: return SOUP_MESSAGE_PRIORITY_VERY_LOW; - case ResourceLoadPriorityLow: + case ResourceLoadPriority::Low: return SOUP_MESSAGE_PRIORITY_LOW; - case ResourceLoadPriorityMedium: + case ResourceLoadPriority::Medium: return SOUP_MESSAGE_PRIORITY_NORMAL; - case ResourceLoadPriorityHigh: + case ResourceLoadPriority::High: return SOUP_MESSAGE_PRIORITY_HIGH; - case ResourceLoadPriorityVeryHigh: + case ResourceLoadPriority::VeryHigh: return SOUP_MESSAGE_PRIORITY_VERY_HIGH; } diff --git a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp index cadb84b29..64578615c 100644 --- a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp @@ -18,16 +18,82 @@ */ #include "config.h" + +#if USE(SOUP) #include "ResourceRequest.h" +#include "BlobData.h" +#include "BlobRegistryImpl.h" #include "GUniquePtrSoup.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" +#include "SharedBuffer.h" +#include "WebKitSoupRequestGeneric.h" #include <wtf/text/CString.h> #include <wtf/text/WTFString.h> namespace WebCore { +static uint64_t appendEncodedBlobItemToSoupMessageBody(SoupMessage* soupMessage, const BlobDataItem& blobItem) +{ + switch (blobItem.type()) { + case BlobDataItem::Type::Data: + soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, blobItem.data().data()->data() + blobItem.offset(), blobItem.length()); + return blobItem.length(); + case BlobDataItem::Type::File: { + if (!isValidFileTime(blobItem.file()->expectedModificationTime())) + return 0; + + time_t fileModificationTime; + if (!getFileModificationTime(blobItem.file()->path(), fileModificationTime) + || fileModificationTime != static_cast<time_t>(blobItem.file()->expectedModificationTime())) + return 0; + + if (RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(blobItem.file()->path())) { + GUniquePtr<SoupBuffer> soupBuffer(buffer->createSoupBuffer(blobItem.offset(), blobItem.length() == BlobDataItem::toEndOfFile ? 0 : blobItem.length())); + soup_message_body_append_buffer(soupMessage->request_body, soupBuffer.get()); + return soupBuffer->length; + } + break; + } + } + + return 0; +} + +void ResourceRequest::updateSoupMessageBody(SoupMessage* soupMessage) const +{ + auto* formData = httpBody(); + if (!formData || formData->isEmpty()) + return; + + soup_message_body_set_accumulate(soupMessage->request_body, FALSE); + uint64_t bodySize = 0; + for (const auto& element : formData->elements()) { + switch (element.m_type) { + case FormDataElement::Type::Data: + bodySize += element.m_data.size(); + soup_message_body_append(soupMessage->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size()); + break; + case FormDataElement::Type::EncodedFile: + if (RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(element.m_filename)) { + GUniquePtr<SoupBuffer> soupBuffer(buffer->createSoupBuffer()); + bodySize += buffer->size(); + soup_message_body_append_buffer(soupMessage->request_body, soupBuffer.get()); + } + break; + case FormDataElement::Type::EncodedBlob: + if (auto* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(element.m_url)) { + for (const auto& item : blobData->items()) + bodySize += appendEncodedBlobItemToSoupMessageBody(soupMessage, item); + } + break; + } + } + + ASSERT(bodySize == static_cast<uint64_t>(soupMessage->request_body->length)); +} + void ResourceRequest::updateSoupMessageMembers(SoupMessage* soupMessage) const { updateSoupMessageHeaders(soupMessage->request_headers); @@ -50,7 +116,7 @@ void ResourceRequest::updateSoupMessageHeaders(SoupMessageHeaders* soupHeaders) if (!headers.isEmpty()) { HTTPHeaderMap::const_iterator end = headers.end(); for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) - soup_message_headers_append(soupHeaders, it->key.string().utf8().data(), it->value.utf8().data()); + soup_message_headers_append(soupHeaders, it->key.utf8().data(), it->value.utf8().data()); } } @@ -73,25 +139,12 @@ void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const soup_message_set_uri(soupMessage, uri.get()); updateSoupMessageMembers(soupMessage); -} - -SoupMessage* ResourceRequest::toSoupMessage() const -{ - SoupMessage* soupMessage = soup_message_new(httpMethod().ascii().data(), url().string().utf8().data()); - if (!soupMessage) - return 0; - - updateSoupMessageMembers(soupMessage); - - // Body data is only handled at ResourceHandleSoup::startHttp for - // now; this is because this may not be a good place to go - // openning and mmapping files. We should maybe revisit this. - return soupMessage; + updateSoupMessageBody(soupMessage); } void ResourceRequest::updateFromSoupMessage(SoupMessage* soupMessage) { - bool shouldPortBeResetToZero = m_url.hasPort() && !m_url.port(); + bool shouldPortBeResetToZero = m_url.port() && !m_url.port().value(); m_url = URL(soup_message_get_uri(soupMessage)); // SoupURI cannot differeniate between an explicitly specified port 0 and @@ -119,12 +172,14 @@ static const char* gSoupRequestInitiatingPageIDKey = "wk-soup-request-initiating void ResourceRequest::updateSoupRequest(SoupRequest* soupRequest) const { - if (!m_initiatingPageID) - return; + if (m_initiatingPageID) { + uint64_t* initiatingPageIDPtr = static_cast<uint64_t*>(fastMalloc(sizeof(uint64_t))); + *initiatingPageIDPtr = m_initiatingPageID; + g_object_set_data_full(G_OBJECT(soupRequest), g_intern_static_string(gSoupRequestInitiatingPageIDKey), initiatingPageIDPtr, fastFree); + } - uint64_t* initiatingPageIDPtr = static_cast<uint64_t*>(fastMalloc(sizeof(uint64_t))); - *initiatingPageIDPtr = m_initiatingPageID; - g_object_set_data_full(G_OBJECT(soupRequest), g_intern_static_string(gSoupRequestInitiatingPageIDKey), initiatingPageIDPtr, fastFree); + if (WEBKIT_IS_SOUP_REQUEST_GENERIC(soupRequest)) + webkitSoupRequestGenericSetRequest(WEBKIT_SOUP_REQUEST_GENERIC(soupRequest), *this); } void ResourceRequest::updateFromSoupRequest(SoupRequest* soupRequest) @@ -153,13 +208,7 @@ GUniquePtr<SoupURI> ResourceRequest::createSoupURI() const return GUniquePtr<SoupURI>(soup_uri_new(urlString.utf8().data())); } - GUniquePtr<SoupURI> soupURI; - if (m_url.hasFragmentIdentifier()) { - URL url = m_url; - url.removeFragmentIdentifier(); - soupURI.reset(soup_uri_new(url.string().utf8().data())); - } else - soupURI = m_url.createSoupURI(); + GUniquePtr<SoupURI> soupURI = m_url.createSoupURI(); // Versions of libsoup prior to 2.42 have a soup_uri_new that will convert empty passwords that are not // prefixed by a colon into null. Some parts of soup like the SoupAuthenticationManager will only be active @@ -176,3 +225,5 @@ GUniquePtr<SoupURI> ResourceRequest::createSoupURI() const } } + +#endif diff --git a/Source/WebCore/platform/network/soup/ResourceResponse.h b/Source/WebCore/platform/network/soup/ResourceResponse.h index 4891f935f..9e206bba9 100644 --- a/Source/WebCore/platform/network/soup/ResourceResponse.h +++ b/Source/WebCore/platform/network/soup/ResourceResponse.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,13 +23,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ResourceResponse_h -#define ResourceResponse_h +#pragma once #include "ResourceResponseBase.h" #include <libsoup/soup.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> namespace WebCore { @@ -42,8 +41,8 @@ public: { } - ResourceResponse(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName, const String& filename) - : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) + ResourceResponse(const URL& url, const String& mimeType, long long expectedLength, const String& textEncodingName) + : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName) , m_soupFlags(static_cast<SoupMessageFlags>(0)) , m_tlsErrors(static_cast<GTlsCertificateFlags>(0)) { @@ -57,7 +56,7 @@ public: updateFromSoupMessage(soupMessage); } - SoupMessage* toSoupMessage() const; + void updateSoupMessageHeaders(SoupMessageHeaders*) const; void updateFromSoupMessage(SoupMessage*); void updateFromSoupMessageHeaders(const SoupMessageHeaders*); @@ -73,7 +72,8 @@ public: GTlsCertificateFlags soupMessageTLSErrors() const { return m_tlsErrors; } void setSoupMessageTLSErrors(GTlsCertificateFlags tlsErrors) { m_tlsErrors = tlsErrors; } - bool platformResponseIsUpToDate() const { return false; } + template<class Encoder> void encode(Encoder&) const; + template<class Decoder> static bool decode(Decoder&, ResourceResponse&); private: friend class ResourceResponseBase; @@ -84,14 +84,25 @@ private: GTlsCertificateFlags m_tlsErrors; void doUpdateResourceResponse() { } - - PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } - void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } + String platformSuggestedFilename() const; + CertificateInfo platformCertificateInfo() const; }; -struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { -}; +template<class Encoder> +void ResourceResponse::encode(Encoder& encoder) const +{ + ResourceResponseBase::encode(encoder); + encoder.encodeEnum(m_soupFlags); +} + +template<class Decoder> +bool ResourceResponse::decode(Decoder& decoder, ResourceResponse& response) +{ + if (!ResourceResponseBase::decode(decoder, response)) + return false; + if (!decoder.decodeEnum(response.m_soupFlags)) + return false; + return true; +} } // namespace WebCore - -#endif // ResourceResponse_h diff --git a/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp b/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp index a7a8fa435..50cfa3e6e 100644 --- a/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp @@ -19,45 +19,37 @@ */ #include "config.h" + +#if USE(SOUP) + #include "ResourceResponse.h" +#include "HTTPHeaderNames.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" #include <wtf/text/CString.h> -#include <wtf/text/StringBuilder.h> #include <wtf/text/WTFString.h> namespace WebCore { -SoupMessage* ResourceResponse::toSoupMessage() const +void ResourceResponse::updateSoupMessageHeaders(SoupMessageHeaders* soupHeaders) const { - // This GET here is just because SoupMessage wants it, we dn't really know. - SoupMessage* soupMessage = soup_message_new("GET", url().string().utf8().data()); - if (!soupMessage) - return 0; - - soupMessage->status_code = httpStatusCode(); - - const HTTPHeaderMap& headers = httpHeaderFields(); - SoupMessageHeaders* soupHeaders = soupMessage->response_headers; - if (!headers.isEmpty()) { - HTTPHeaderMap::const_iterator end = headers.end(); - for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) - soup_message_headers_append(soupHeaders, it->key.string().utf8().data(), it->value.utf8().data()); - } - - soup_message_set_flags(soupMessage, m_soupFlags); - - g_object_set(G_OBJECT(soupMessage), "tls-certificate", m_certificate.get(), "tls-errors", m_tlsErrors, NULL); - - // Body data is not in the message. - return soupMessage; + for (const auto& header : httpHeaderFields()) + soup_message_headers_append(soupHeaders, header.key.utf8().data(), header.value.utf8().data()); } void ResourceResponse::updateFromSoupMessage(SoupMessage* soupMessage) { m_url = URL(soup_message_get_uri(soupMessage)); + switch (soup_message_get_http_version(soupMessage)) { + case SOUP_HTTP_1_0: + m_httpVersion = AtomicString("HTTP/1.0", AtomicString::ConstructFromLiteral); + break; + case SOUP_HTTP_1_1: + m_httpVersion = AtomicString("HTTP/1.1", AtomicString::ConstructFromLiteral); + break; + } m_httpStatusCode = soupMessage->status_code; setHTTPStatusText(soupMessage->reason_phrase); @@ -95,6 +87,19 @@ void ResourceResponse::updateFromSoupMessageHeaders(const SoupMessageHeaders* me setTextEncodingName(extractCharsetFromMediaType(contentType)); setExpectedContentLength(soup_message_headers_get_content_length(headers)); - setSuggestedFilename(filenameFromHTTPContentDisposition(httpHeaderField("Content-Disposition")));} +} +CertificateInfo ResourceResponse::platformCertificateInfo() const +{ + return CertificateInfo(m_certificate.get(), m_tlsErrors); +} + +String ResourceResponse::platformSuggestedFilename() const +{ + String contentDisposition(httpHeaderField(HTTPHeaderName::ContentDisposition)); + return filenameFromHTTPContentDisposition(String::fromUTF8WithLatin1Fallback(contentDisposition.characters8(), contentDisposition.length())); } + +} + +#endif diff --git a/Source/WebCore/platform/network/soup/SocketStreamError.h b/Source/WebCore/platform/network/soup/SocketStreamError.h deleted file mode 100644 index 0676893ce..000000000 --- a/Source/WebCore/platform/network/soup/SocketStreamError.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2009, 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SocketStreamError_h -#define SocketStreamError_h - -#include "SocketStreamErrorBase.h" - -namespace WebCore { - - class SocketStreamError : public SocketStreamErrorBase { - public: - SocketStreamError() { } - SocketStreamError(int errorCode, const gchar* description) - : SocketStreamErrorBase(errorCode, String(), String(description)) - { - } - - }; - -} // namespace WebCore - -#endif // SocketStreamError_h diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandle.h b/Source/WebCore/platform/network/soup/SocketStreamHandle.h deleted file mode 100644 index fa0168805..000000000 --- a/Source/WebCore/platform/network/soup/SocketStreamHandle.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * Copyright (C) 2009 Google Inc. All rights reserved. - * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef SocketStreamHandle_h -#define SocketStreamHandle_h - -#include "SocketStreamHandleBase.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/gobject/GRefPtr.h> - -namespace WebCore { - - class AuthenticationChallenge; - class Credential; - class SocketStreamHandleClient; - - class SocketStreamHandle : public RefCounted<SocketStreamHandle>, public SocketStreamHandleBase { - public: - static PassRefPtr<SocketStreamHandle> create(const URL& url, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(url, client)); } - static PassRefPtr<SocketStreamHandle> create(GSocketConnection* socketConnection, SocketStreamHandleClient* client) { return adoptRef(new SocketStreamHandle(socketConnection, client)); } - - virtual ~SocketStreamHandle(); - void connected(GSocketConnection*, GError*); - void readBytes(signed long, GError*); - void writeReady(); - void* id() { return m_id; } - - protected: - virtual int platformSend(const char* data, int length); - virtual void platformClose(); - - private: - GRefPtr<GSocketConnection> m_socketConnection; - GRefPtr<GInputStream> m_inputStream; - GRefPtr<GPollableOutputStream> m_outputStream; - GRefPtr<GSource> m_writeReadySource; - std::unique_ptr<char[]> m_readBuffer; - void* m_id; - - SocketStreamHandle(const URL&, SocketStreamHandleClient*); - SocketStreamHandle(GSocketConnection*, SocketStreamHandleClient*); - - // No authentication for streams per se, but proxy may ask for credentials. - void didReceiveAuthenticationChallenge(const AuthenticationChallenge&); - void receivedCredential(const AuthenticationChallenge&, const Credential&); - void receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&); - void receivedCancellation(const AuthenticationChallenge&); - void beginWaitingForSocketWritability(); - void stopWaitingForSocketWritability(); - }; - -} // namespace WebCore - -#endif // SocketStreamHandle_h diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleImpl.h b/Source/WebCore/platform/network/soup/SocketStreamHandleImpl.h new file mode 100644 index 000000000..0459e907e --- /dev/null +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleImpl.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include "SocketStreamHandle.h" + +#if USE(SOUP) + +#include "SessionID.h" +#include <wtf/RefCounted.h> +#include <wtf/glib/GRefPtr.h> + +namespace WebCore { + +class SocketStreamError; +class SocketStreamHandleClient; + +class SocketStreamHandleImpl final : public SocketStreamHandle { +public: + static Ref<SocketStreamHandleImpl> create(const URL&, SocketStreamHandleClient&, SessionID, const String&); + static Ref<SocketStreamHandle> create(GSocketConnection*, SocketStreamHandleClient&); + + virtual ~SocketStreamHandleImpl(); + +private: + SocketStreamHandleImpl(const URL&, SocketStreamHandleClient&); + + std::optional<size_t> platformSend(const char* data, size_t length) final; + void platformClose() final; + + void beginWaitingForSocketWritability(); + void stopWaitingForSocketWritability(); + + static void connectedCallback(GSocketClient*, GAsyncResult*, SocketStreamHandleImpl*); + static void readReadyCallback(GInputStream*, GAsyncResult*, SocketStreamHandleImpl*); + static gboolean writeReadyCallback(GPollableOutputStream*, SocketStreamHandleImpl*); + + void connected(GRefPtr<GSocketConnection>&&); + void readBytes(gssize); + void didFail(SocketStreamError&&); + void writeReady(); + + GRefPtr<GSocketConnection> m_socketConnection; + GRefPtr<GInputStream> m_inputStream; + GRefPtr<GPollableOutputStream> m_outputStream; + GRefPtr<GSource> m_writeReadySource; + GRefPtr<GCancellable> m_cancellable; + std::unique_ptr<char[]> m_readBuffer; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp new file mode 100644 index 000000000..20d19e3b1 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2009, 2011 Google Inc. All rights reserved. + * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SocketStreamHandleImpl.h" + +#if USE(SOUP) + +#include "Logging.h" +#include "Settings.h" +#include "SocketStreamError.h" +#include "SocketStreamHandleClient.h" +#include "URL.h" +#include <gio/gio.h> +#include <glib.h> +#include <wtf/Vector.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> + +#define READ_BUFFER_SIZE 1024 + +namespace WebCore { + +static gboolean wssConnectionAcceptCertificateCallback(GTlsConnection*, GTlsCertificate*, GTlsCertificateFlags) +{ + return TRUE; +} + +static void wssSocketClientEventCallback(GSocketClient*, GSocketClientEvent event, GSocketConnectable*, GIOStream* connection) +{ + if (event != G_SOCKET_CLIENT_TLS_HANDSHAKING) + return; + + g_signal_connect(connection, "accept-certificate", G_CALLBACK(wssConnectionAcceptCertificateCallback), nullptr); +} + +Ref<SocketStreamHandleImpl> SocketStreamHandleImpl::create(const URL& url, SocketStreamHandleClient& client, SessionID, const String&) +{ + Ref<SocketStreamHandleImpl> socket = adoptRef(*new SocketStreamHandleImpl(url, client)); + + unsigned port = url.port() ? url.port().value() : (url.protocolIs("wss") ? 443 : 80); + GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); + if (url.protocolIs("wss")) { + g_socket_client_set_tls(socketClient.get(), TRUE); + // FIXME: this is only used by tests, but using Settings from here is a layering violation. + if (Settings::allowsAnySSLCertificate()) + g_signal_connect(socketClient.get(), "event", G_CALLBACK(wssSocketClientEventCallback), nullptr); + } + Ref<SocketStreamHandle> protectedSocketStreamHandle = socket.copyRef(); + g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, socket->m_cancellable.get(), + reinterpret_cast<GAsyncReadyCallback>(connectedCallback), &protectedSocketStreamHandle.leakRef()); + return socket; +} + +Ref<SocketStreamHandle> SocketStreamHandleImpl::create(GSocketConnection* socketConnection, SocketStreamHandleClient& client) +{ + Ref<SocketStreamHandleImpl> socket = adoptRef(*new SocketStreamHandleImpl(URL(), client)); + + GRefPtr<GSocketConnection> connection = socketConnection; + socket->connected(WTFMove(connection)); + return WTFMove(socket); +} + +SocketStreamHandleImpl::SocketStreamHandleImpl(const URL& url, SocketStreamHandleClient& client) + : SocketStreamHandle(url, client) + , m_cancellable(adoptGRef(g_cancellable_new())) +{ + LOG(Network, "SocketStreamHandle %p new client %p", this, &m_client); +} + +SocketStreamHandleImpl::~SocketStreamHandleImpl() +{ + LOG(Network, "SocketStreamHandle %p delete", this); +} + +void SocketStreamHandleImpl::connected(GRefPtr<GSocketConnection>&& socketConnection) +{ + m_socketConnection = WTFMove(socketConnection); + m_outputStream = G_POLLABLE_OUTPUT_STREAM(g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get()))); + m_inputStream = g_io_stream_get_input_stream(G_IO_STREAM(m_socketConnection.get())); + m_readBuffer = std::make_unique<char[]>(READ_BUFFER_SIZE); + + RefPtr<SocketStreamHandleImpl> protectedThis(this); + g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, m_cancellable.get(), + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), protectedThis.leakRef()); + + m_state = Open; + m_client.didOpenSocketStream(*this); +} + +void SocketStreamHandleImpl::connectedCallback(GSocketClient* client, GAsyncResult* result, SocketStreamHandleImpl* handle) +{ + RefPtr<SocketStreamHandle> protectedThis = adoptRef(handle); + + // Always finish the connection, even if this SocketStreamHandle was cancelled earlier. + GUniqueOutPtr<GError> error; + GRefPtr<GSocketConnection> socketConnection = adoptGRef(g_socket_client_connect_to_host_finish(client, result, &error.outPtr())); + + // The SocketStreamHandle has been cancelled, so just close the connection, ignoring errors. + if (g_cancellable_is_cancelled(handle->m_cancellable.get())) { + if (socketConnection) + g_io_stream_close(G_IO_STREAM(socketConnection.get()), nullptr, nullptr); + return; + } + + if (error) + handle->didFail(SocketStreamError(error->code, String(), error->message)); + else + handle->connected(WTFMove(socketConnection)); +} + +void SocketStreamHandleImpl::readBytes(gssize bytesRead) +{ + if (!bytesRead) { + close(); + return; + } + + // The client can close the handle, potentially removing the last reference. + RefPtr<SocketStreamHandle> protectedThis(this); + std::optional<size_t> optionalLength; + if (bytesRead != -1) + optionalLength = static_cast<size_t>(bytesRead); + m_client.didReceiveSocketStreamData(*this, m_readBuffer.get(), optionalLength); + if (m_inputStream) { + g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, m_cancellable.get(), + reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), protectedThis.leakRef()); + } +} + +void SocketStreamHandleImpl::readReadyCallback(GInputStream* stream, GAsyncResult* result, SocketStreamHandleImpl* handle) +{ + RefPtr<SocketStreamHandle> protectedThis = adoptRef(handle); + + // Always finish the read, even if this SocketStreamHandle was cancelled earlier. + GUniqueOutPtr<GError> error; + gssize bytesRead = g_input_stream_read_finish(stream, result, &error.outPtr()); + + if (g_cancellable_is_cancelled(handle->m_cancellable.get())) + return; + + if (error) + handle->didFail(SocketStreamError(error->code, String(), error->message)); + else + handle->readBytes(bytesRead); +} + +void SocketStreamHandleImpl::didFail(SocketStreamError&& error) +{ + m_client.didFailSocketStream(*this, WTFMove(error)); +} + +void SocketStreamHandleImpl::writeReady() +{ + // We no longer have buffered data, so stop waiting for the socket to be writable. + if (!bufferedAmount()) { + stopWaitingForSocketWritability(); + return; + } + + sendPendingData(); +} + +std::optional<size_t> SocketStreamHandleImpl::platformSend(const char* data, size_t length) +{ + LOG(Network, "SocketStreamHandle %p platformSend", this); + if (!m_outputStream || !data) + return 0; + + GUniqueOutPtr<GError> error; + gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), data, length, m_cancellable.get(), &error.outPtr()); + if (error) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + beginWaitingForSocketWritability(); + else + didFail(SocketStreamError(error->code, String(), error->message)); + return std::nullopt; + } + + // If we did not send all the bytes we were given, we know that + // SocketStreamHandle will need to send more in the future. + if (written == -1 || static_cast<size_t>(written) < length) + beginWaitingForSocketWritability(); + + if (written == -1) + return std::nullopt; + + return static_cast<size_t>(written); +} + +void SocketStreamHandleImpl::platformClose() +{ + LOG(Network, "SocketStreamHandle %p platformClose", this); + // We cancel this handle first to disable all callbacks. + g_cancellable_cancel(m_cancellable.get()); + stopWaitingForSocketWritability(); + + if (m_socketConnection) { + GUniqueOutPtr<GError> error; + g_io_stream_close(G_IO_STREAM(m_socketConnection.get()), nullptr, &error.outPtr()); + if (error) + didFail(SocketStreamError(error->code, String(), error->message)); + m_socketConnection = nullptr; + } + + m_outputStream = nullptr; + m_inputStream = nullptr; + m_readBuffer = nullptr; + + m_client.didCloseSocketStream(*this); +} + +void SocketStreamHandleImpl::beginWaitingForSocketWritability() +{ + if (m_writeReadySource) // Already waiting. + return; + + m_writeReadySource = adoptGRef(g_pollable_output_stream_create_source(m_outputStream.get(), m_cancellable.get())); + ref(); + g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(writeReadyCallback), this, [](gpointer handle) { + static_cast<SocketStreamHandleImpl*>(handle)->deref(); + }); + g_source_attach(m_writeReadySource.get(), g_main_context_get_thread_default()); +} + +void SocketStreamHandleImpl::stopWaitingForSocketWritability() +{ + if (!m_writeReadySource) // Not waiting. + return; + + g_source_destroy(m_writeReadySource.get()); + m_writeReadySource = nullptr; +} + +gboolean SocketStreamHandleImpl::writeReadyCallback(GPollableOutputStream*, SocketStreamHandleImpl* handle) +{ + if (g_cancellable_is_cancelled(handle->m_cancellable.get())) + return G_SOURCE_REMOVE; + + handle->writeReady(); + return G_SOURCE_CONTINUE; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp deleted file mode 100644 index 71c21216d..000000000 --- a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2009, 2011 Google Inc. All rights reserved. - * Copyright (C) 2012 Samsung Electronics Ltd. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SocketStreamHandle.h" - -#include "URL.h" -#include "Logging.h" -#include "NotImplemented.h" -#include "SocketStreamError.h" -#include "SocketStreamHandleClient.h" - -#include <gio/gio.h> -#include <glib.h> - -#include <wtf/Vector.h> -#include <wtf/gobject/GUniquePtr.h> -#include <wtf/text/CString.h> - -#define READ_BUFFER_SIZE 1024 - -namespace WebCore { - -// These functions immediately call the similarly named SocketStreamHandle methods. -static void connectedCallback(GSocketClient*, GAsyncResult*, void*); -static void readReadyCallback(GInputStream*, GAsyncResult*, void*); -static gboolean writeReadyCallback(GPollableOutputStream*, void*); - -// Having a list of active handles means that we do not have to worry about WebCore -// reference counting in GLib callbacks. Once the handle is off the active handles list -// we just ignore it in the callback. We avoid a lot of extra checks and tricky -// situations this way. -static HashMap<void*, SocketStreamHandle*> gActiveHandles; -COMPILE_ASSERT(HashTraits<SocketStreamHandle*>::emptyValueIsZero, emptyMapValue_is_0); - -static SocketStreamHandle* getHandleFromId(void* id) -{ - return gActiveHandles.get(id); -} - -static void deactivateHandle(SocketStreamHandle* handle) -{ - gActiveHandles.remove(handle->id()); -} - -static void* activateHandle(SocketStreamHandle* handle) -{ - // The first id cannot be 0, because it conflicts with the HashMap emptyValue. - static gint currentHandleId = 1; - void* id = GINT_TO_POINTER(currentHandleId++); - gActiveHandles.set(id, handle); - return id; -} - -SocketStreamHandle::SocketStreamHandle(const URL& url, SocketStreamHandleClient* client) - : SocketStreamHandleBase(url, client) -{ - LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); - unsigned int port = url.hasPort() ? url.port() : (url.protocolIs("wss") ? 443 : 80); - - m_id = activateHandle(this); - GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); - if (url.protocolIs("wss")) - g_socket_client_set_tls(socketClient.get(), TRUE); - g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, 0, - reinterpret_cast<GAsyncReadyCallback>(connectedCallback), m_id); -} - -SocketStreamHandle::SocketStreamHandle(GSocketConnection* socketConnection, SocketStreamHandleClient* client) - : SocketStreamHandleBase(URL(), client) -{ - LOG(Network, "SocketStreamHandle %p new client %p", this, m_client); - m_id = activateHandle(this); - connected(socketConnection, 0); -} - -SocketStreamHandle::~SocketStreamHandle() -{ - LOG(Network, "SocketStreamHandle %p delete", this); - // If for some reason we were destroyed without closing, ensure that we are deactivated. - deactivateHandle(this); - setClient(0); -} - -void SocketStreamHandle::connected(GSocketConnection* socketConnection, GError* error) -{ - if (error) { - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - return; - } - - m_socketConnection = socketConnection; - m_outputStream = G_POLLABLE_OUTPUT_STREAM(g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get()))); - m_inputStream = g_io_stream_get_input_stream(G_IO_STREAM(m_socketConnection.get())); - - m_readBuffer = std::make_unique<char[]>(READ_BUFFER_SIZE); - g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, - reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), m_id); - - m_state = Open; - m_client->didOpenSocketStream(this); -} - -void SocketStreamHandle::readBytes(signed long bytesRead, GError* error) -{ - if (error) { - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - return; - } - - if (!bytesRead) { - close(); - return; - } - - // The client can close the handle, potentially removing the last reference. - RefPtr<SocketStreamHandle> protect(this); - m_client->didReceiveSocketStreamData(this, m_readBuffer.get(), bytesRead); - if (m_inputStream) // The client may have closed the connection. - g_input_stream_read_async(m_inputStream.get(), m_readBuffer.get(), READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, 0, - reinterpret_cast<GAsyncReadyCallback>(readReadyCallback), m_id); -} - -void SocketStreamHandle::writeReady() -{ - // We no longer have buffered data, so stop waiting for the socket to be writable. - if (!bufferedAmount()) { - stopWaitingForSocketWritability(); - return; - } - - sendPendingData(); -} - -int SocketStreamHandle::platformSend(const char* data, int length) -{ - LOG(Network, "SocketStreamHandle %p platformSend", this); - if (!m_outputStream || !data) - return 0; - - GUniqueOutPtr<GError> error; - gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), data, length, 0, &error.outPtr()); - if (error) { - if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) - beginWaitingForSocketWritability(); - else - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - return 0; - } - - // If we did not send all the bytes we were given, we know that - // SocketStreamHandleBase will need to send more in the future. - if (written < length) - beginWaitingForSocketWritability(); - - return written; -} - -void SocketStreamHandle::platformClose() -{ - LOG(Network, "SocketStreamHandle %p platformClose", this); - // We remove this handle from the active handles list first, to disable all callbacks. - deactivateHandle(this); - stopWaitingForSocketWritability(); - - if (m_socketConnection) { - GUniqueOutPtr<GError> error; - g_io_stream_close(G_IO_STREAM(m_socketConnection.get()), 0, &error.outPtr()); - if (error) - m_client->didFailSocketStream(this, SocketStreamError(error->code, error->message)); - m_socketConnection = 0; - } - - m_outputStream = 0; - m_inputStream = 0; - m_readBuffer = nullptr; - - m_client->didCloseSocketStream(this); -} - -void SocketStreamHandle::didReceiveAuthenticationChallenge(const AuthenticationChallenge&) -{ - notImplemented(); -} - -void SocketStreamHandle::receivedCredential(const AuthenticationChallenge&, const Credential&) -{ - notImplemented(); -} - -void SocketStreamHandle::receivedRequestToContinueWithoutCredential(const AuthenticationChallenge&) -{ - notImplemented(); -} - -void SocketStreamHandle::receivedCancellation(const AuthenticationChallenge&) -{ - notImplemented(); -} - -void SocketStreamHandle::beginWaitingForSocketWritability() -{ - if (m_writeReadySource) // Already waiting. - return; - - m_writeReadySource = adoptGRef(g_pollable_output_stream_create_source(m_outputStream.get(), 0)); - g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(writeReadyCallback), m_id, 0); - g_source_attach(m_writeReadySource.get(), 0); -} - -void SocketStreamHandle::stopWaitingForSocketWritability() -{ - if (!m_writeReadySource) // Not waiting. - return; - - g_source_remove(g_source_get_id(m_writeReadySource.get())); - m_writeReadySource = 0; -} - -static void connectedCallback(GSocketClient* client, GAsyncResult* result, void* id) -{ - // Always finish the connection, even if this SocketStreamHandle was deactivated earlier. - GUniqueOutPtr<GError> error; - GSocketConnection* socketConnection = g_socket_client_connect_to_host_finish(client, result, &error.outPtr()); - - // The SocketStreamHandle has been deactivated, so just close the connection, ignoring errors. - SocketStreamHandle* handle = getHandleFromId(id); - if (!handle) { - if (socketConnection) - g_io_stream_close(G_IO_STREAM(socketConnection), 0, 0); - return; - } - - handle->connected(socketConnection, error.get()); -} - -static void readReadyCallback(GInputStream* stream, GAsyncResult* result, void* id) -{ - // Always finish the read, even if this SocketStreamHandle was deactivated earlier. - GUniqueOutPtr<GError> error; - gssize bytesRead = g_input_stream_read_finish(stream, result, &error.outPtr()); - - SocketStreamHandle* handle = getHandleFromId(id); - if (!handle) - return; - - handle->readBytes(bytesRead, error.get()); -} - -static gboolean writeReadyCallback(GPollableOutputStream*, void* id) -{ - SocketStreamHandle* handle = getHandleFromId(id); - if (!handle) - return FALSE; - - handle->writeReady(); - return TRUE; -} - -} // namespace WebCore diff --git a/Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h b/Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h new file mode 100644 index 000000000..273267350 --- /dev/null +++ b/Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2017 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <wtf/HashMap.h> +#include <wtf/glib/GUniquePtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +struct SoupNetworkProxySettings { + enum class Mode { Default, NoProxy, Custom }; + + SoupNetworkProxySettings() = default; + + explicit SoupNetworkProxySettings(Mode proxyMode) + : mode(proxyMode) + { + } + + SoupNetworkProxySettings(const WebCore::SoupNetworkProxySettings& other) + : mode(other.mode) + , defaultProxyURL(other.defaultProxyURL) + , ignoreHosts(g_strdupv(other.ignoreHosts.get())) + , proxyMap(other.proxyMap) + { + } + + SoupNetworkProxySettings& operator=(const WebCore::SoupNetworkProxySettings& other) + { + mode = other.mode; + defaultProxyURL = other.defaultProxyURL; + ignoreHosts.reset(g_strdupv(other.ignoreHosts.get())); + proxyMap = other.proxyMap; + return *this; + } + + bool isEmpty() const { return mode == Mode::Custom && defaultProxyURL.isNull() && !ignoreHosts && proxyMap.isEmpty(); } + + Mode mode { Mode::Default }; + CString defaultProxyURL; + GUniquePtr<char*> ignoreHosts; + HashMap<CString, CString> proxyMap; +}; + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp b/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp index 715e39f04..7c17e604e 100644 --- a/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp +++ b/Source/WebCore/platform/network/soup/SoupNetworkSession.cpp @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -24,23 +24,32 @@ */ #include "config.h" + +#if USE(SOUP) + #include "SoupNetworkSession.h" #include "AuthenticationChallenge.h" -#include "CookieJarSoup.h" +#include "FileSystem.h" #include "GUniquePtrSoup.h" #include "Logging.h" #include "ResourceHandle.h" +#include "SoupNetworkProxySettings.h" +#include <glib/gstdio.h> #include <libsoup/soup.h> +#include <pal/crypto/CryptoDigest.h> +#include <wtf/HashSet.h> +#include <wtf/NeverDestroyed.h> +#include <wtf/text/Base64.h> #include <wtf/text/CString.h> -#include <wtf/text/StringBuilder.h> - -#if PLATFORM(EFL) -#include "ProxyResolverSoup.h" -#endif namespace WebCore { +static bool gIgnoreTLSErrors; +static CString gInitialAcceptLanguages; +static SoupNetworkProxySettings gProxySettings; +static GType gCustomProtocolRequestType; + #if !LOG_DISABLED inline static void soupLogPrinter(SoupLogger*, SoupLoggerLogLevel, char direction, const char* data, gpointer) { @@ -48,37 +57,53 @@ inline static void soupLogPrinter(SoupLogger*, SoupLoggerLogLevel, char directio } #endif -SoupNetworkSession& SoupNetworkSession::defaultSession() -{ - static SoupNetworkSession networkSession(soupCookieJar()); - return networkSession; -} +class HostTLSCertificateSet { +public: + void add(GTlsCertificate* certificate) + { + String certificateHash = computeCertificateHash(certificate); + if (!certificateHash.isEmpty()) + m_certificates.add(certificateHash); + } -std::unique_ptr<SoupNetworkSession> SoupNetworkSession::createPrivateBrowsingSession() -{ - return std::unique_ptr<SoupNetworkSession>(new SoupNetworkSession(soupCookieJar())); -} + bool contains(GTlsCertificate* certificate) const + { + return m_certificates.contains(computeCertificateHash(certificate)); + } -std::unique_ptr<SoupNetworkSession> SoupNetworkSession::createTestingSession() -{ - GRefPtr<SoupCookieJar> cookieJar = adoptGRef(createPrivateBrowsingCookieJar()); - return std::unique_ptr<SoupNetworkSession>(new SoupNetworkSession(cookieJar.get())); -} +private: + static String computeCertificateHash(GTlsCertificate* certificate) + { + GRefPtr<GByteArray> certificateData; + g_object_get(G_OBJECT(certificate), "certificate", &certificateData.outPtr(), nullptr); + if (!certificateData) + return String(); + + auto digest = PAL::CryptoDigest::create(PAL::CryptoDigest::Algorithm::SHA_256); + digest->addBytes(certificateData->data, certificateData->len); + + auto hash = digest->computeHash(); + return base64Encode(reinterpret_cast<const char*>(hash.data()), hash.size()); + } -std::unique_ptr<SoupNetworkSession> SoupNetworkSession::createForSoupSession(SoupSession* soupSession) + HashSet<String> m_certificates; +}; + +static HashMap<String, HostTLSCertificateSet, ASCIICaseInsensitiveHash>& clientCertificates() { - return std::unique_ptr<SoupNetworkSession>(new SoupNetworkSession(soupSession)); + static NeverDestroyed<HashMap<String, HostTLSCertificateSet, ASCIICaseInsensitiveHash>> certificates; + return certificates; } -static void authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying) +static void authenticateCallback(SoupSession*, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying) { RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(G_OBJECT(soupMessage), "handle")); if (!handle) return; - handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(session, soupMessage, soupAuth, retrying, handle.get())); + handle->didReceiveAuthenticationChallenge(AuthenticationChallenge(soupMessage, soupAuth, retrying, handle.get())); } -#if ENABLE(WEB_TIMING) +#if ENABLE(WEB_TIMING) && !SOUP_CHECK_VERSION(2, 49, 91) static void requestStartedCallback(SoupSession*, SoupMessage* soupMessage, SoupSocket*, gpointer) { RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(G_OBJECT(soupMessage), "handle")); @@ -95,33 +120,50 @@ SoupNetworkSession::SoupNetworkSession(SoupCookieJar* cookieJar) // the rule "Do What Every Other Modern Browser Is Doing". They seem // to significantly improve page loading time compared to soup's // default values. - static const int maxConnections = 35; + static const int maxConnections = 17; static const int maxConnectionsPerHost = 6; + GRefPtr<SoupCookieJar> jar = cookieJar; + if (!jar) { + jar = adoptGRef(soup_cookie_jar_new()); + soup_cookie_jar_set_accept_policy(jar.get(), SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); + } + g_object_set(m_soupSession.get(), SOUP_SESSION_MAX_CONNS, maxConnections, SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_SNIFFER, SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_PROXY_RESOLVER_DEFAULT, - SOUP_SESSION_ADD_FEATURE, cookieJar, + SOUP_SESSION_ADD_FEATURE, jar.get(), SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, + SOUP_SESSION_SSL_STRICT, FALSE, nullptr); + setupCustomProtocols(); + + if (!gInitialAcceptLanguages.isNull()) + setAcceptLanguages(gInitialAcceptLanguages); + +#if SOUP_CHECK_VERSION(2, 53, 92) + if (soup_auth_negotiate_supported()) { + g_object_set(m_soupSession.get(), + SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_AUTH_NEGOTIATE, + nullptr); + } +#endif + + if (gProxySettings.mode != SoupNetworkProxySettings::Mode::Default) + setupProxy(); setupLogger(); g_signal_connect(m_soupSession.get(), "authenticate", G_CALLBACK(authenticateCallback), nullptr); -#if ENABLE(WEB_TIMING) +#if ENABLE(WEB_TIMING) && !SOUP_CHECK_VERSION(2, 49, 91) g_signal_connect(m_soupSession.get(), "request-started", G_CALLBACK(requestStartedCallback), nullptr); #endif } -SoupNetworkSession::SoupNetworkSession(SoupSession* soupSession) - : m_soupSession(soupSession) -{ - setupLogger(); -} - SoupNetworkSession::~SoupNetworkSession() { } @@ -150,134 +192,137 @@ SoupCookieJar* SoupNetworkSession::cookieJar() const return SOUP_COOKIE_JAR(soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_COOKIE_JAR)); } -void SoupNetworkSession::setCache(SoupCache* cache) +static inline bool stringIsNumeric(const char* str) { - ASSERT(!soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_CACHE)); - soup_session_add_feature(m_soupSession.get(), SOUP_SESSION_FEATURE(cache)); + while (*str) { + if (!g_ascii_isdigit(*str)) + return false; + str++; + } + return true; } -SoupCache* SoupNetworkSession::cache() const +// Old versions of WebKit created this cache. +void SoupNetworkSession::clearOldSoupCache(const String& cacheDirectory) { - SoupSessionFeature* soupCache = soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_CACHE); - return soupCache ? SOUP_CACHE(soupCache) : nullptr; -} + CString cachePath = fileSystemRepresentation(cacheDirectory); + GUniquePtr<char> cacheFile(g_build_filename(cachePath.data(), "soup.cache2", nullptr)); + if (!g_file_test(cacheFile.get(), G_FILE_TEST_IS_REGULAR)) + return; -void SoupNetworkSession::setSSLPolicy(SSLPolicy flags) -{ - g_object_set(m_soupSession.get(), - SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, flags & SSLUseSystemCAFile ? TRUE : FALSE, - SOUP_SESSION_SSL_STRICT, flags & SSLStrict ? TRUE : FALSE, - nullptr); -} + GUniquePtr<GDir> dir(g_dir_open(cachePath.data(), 0, nullptr)); + if (!dir) + return; -SoupNetworkSession::SSLPolicy SoupNetworkSession::sslPolicy() const -{ - gboolean useSystemCAFile, strict; - g_object_get(m_soupSession.get(), - SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, &useSystemCAFile, - SOUP_SESSION_SSL_STRICT, &strict, - nullptr); + while (const char* name = g_dir_read_name(dir.get())) { + if (!g_str_has_prefix(name, "soup.cache") && !stringIsNumeric(name)) + continue; - SSLPolicy flags = 0; - if (useSystemCAFile) - flags |= SSLUseSystemCAFile; - if (strict) - flags |= SSLStrict; - return flags; + GUniquePtr<gchar> filename(g_build_filename(cachePath.data(), name, nullptr)); + if (g_file_test(filename.get(), G_FILE_TEST_IS_REGULAR)) + g_unlink(filename.get()); + } } -void SoupNetworkSession::setHTTPProxy(const char* httpProxy, const char* httpProxyExceptions) +void SoupNetworkSession::setupProxy() { -#if PLATFORM(EFL) - // Only for EFL because GTK port uses the default resolver, which uses GIO's proxy resolver. - if (!httpProxy) { - soup_session_remove_feature_by_type(m_soupSession.get(), SOUP_TYPE_PROXY_URI_RESOLVER); - return; + GRefPtr<GProxyResolver> resolver; + switch (gProxySettings.mode) { + case SoupNetworkProxySettings::Mode::Default: { + GRefPtr<GProxyResolver> currentResolver; + g_object_get(m_soupSession.get(), SOUP_SESSION_PROXY_RESOLVER, ¤tResolver.outPtr(), nullptr); + GProxyResolver* defaultResolver = g_proxy_resolver_get_default(); + if (currentResolver.get() == defaultResolver) + return; + resolver = defaultResolver; + break; + } + case SoupNetworkProxySettings::Mode::NoProxy: + // Do nothing in this case, resolver is nullptr so that when set it will disable proxies. + break; + case SoupNetworkProxySettings::Mode::Custom: + resolver = adoptGRef(g_simple_proxy_resolver_new(nullptr, nullptr)); + if (!gProxySettings.defaultProxyURL.isNull()) + g_simple_proxy_resolver_set_default_proxy(G_SIMPLE_PROXY_RESOLVER(resolver.get()), gProxySettings.defaultProxyURL.data()); + if (gProxySettings.ignoreHosts) + g_simple_proxy_resolver_set_ignore_hosts(G_SIMPLE_PROXY_RESOLVER(resolver.get()), gProxySettings.ignoreHosts.get()); + for (const auto& iter : gProxySettings.proxyMap) + g_simple_proxy_resolver_set_uri_proxy(G_SIMPLE_PROXY_RESOLVER(resolver.get()), iter.key.data(), iter.value.data()); + break; } - GRefPtr<SoupProxyURIResolver> resolver = adoptGRef(soupProxyResolverWkNew(httpProxy, httpProxyExceptions)); - soup_session_add_feature(m_soupSession.get(), SOUP_SESSION_FEATURE(resolver.get())); -#else - UNUSED_PARAM(httpProxy); - UNUSED_PARAM(httpProxyExceptions); -#endif + g_object_set(m_soupSession.get(), SOUP_SESSION_PROXY_RESOLVER, resolver.get(), nullptr); + soup_session_abort(m_soupSession.get()); } -char* SoupNetworkSession::httpProxy() const +void SoupNetworkSession::setProxySettings(const SoupNetworkProxySettings& settings) { -#if PLATFORM(EFL) - SoupSessionFeature* soupResolver = soup_session_get_feature(m_soupSession.get(), SOUP_TYPE_PROXY_URI_RESOLVER); - if (!soupResolver) - return nullptr; + gProxySettings = settings; +} - GUniqueOutPtr<SoupURI> uri; - g_object_get(soupResolver, SOUP_PROXY_RESOLVER_WK_PROXY_URI, &uri.outPtr(), nullptr); +void SoupNetworkSession::setInitialAcceptLanguages(const CString& languages) +{ + gInitialAcceptLanguages = languages; +} - return uri ? soup_uri_to_string(uri.get(), FALSE) : nullptr; -#else - return nullptr; -#endif +void SoupNetworkSession::setAcceptLanguages(const CString& languages) +{ + g_object_set(m_soupSession.get(), "accept-language", languages.data(), nullptr); } -void SoupNetworkSession::setupHTTPProxyFromEnvironment() +void SoupNetworkSession::setCustomProtocolRequestType(GType requestType) { -#if PLATFORM(EFL) - const char* httpProxy = getenv("http_proxy"); - if (!httpProxy) + ASSERT(g_type_is_a(requestType, SOUP_TYPE_REQUEST)); + gCustomProtocolRequestType = requestType; +} + +void SoupNetworkSession::setupCustomProtocols() +{ + if (!g_type_is_a(gCustomProtocolRequestType, SOUP_TYPE_REQUEST)) return; - setHTTPProxy(httpProxy, getenv("no_proxy")); -#endif + auto* requestClass = static_cast<SoupRequestClass*>(g_type_class_peek(gCustomProtocolRequestType)); + if (!requestClass || !requestClass->schemes) + return; + + soup_session_add_feature_by_type(m_soupSession.get(), gCustomProtocolRequestType); } -static CString buildAcceptLanguages(const Vector<String>& languages) +void SoupNetworkSession::setShouldIgnoreTLSErrors(bool ignoreTLSErrors) { - size_t languagesCount = languages.size(); - - // Ignore "C" locale. - size_t cLocalePosition = languages.find("c"); - if (cLocalePosition != notFound) - languagesCount--; - - // Fallback to "en" if the list is empty. - if (!languagesCount) - return "en"; - - // Calculate deltas for the quality values. - int delta; - if (languagesCount < 10) - delta = 10; - else if (languagesCount < 20) - delta = 5; - else - delta = 1; - - // Set quality values for each language. - StringBuilder builder; - for (size_t i = 0; i < languages.size(); ++i) { - if (i == cLocalePosition) - continue; + gIgnoreTLSErrors = ignoreTLSErrors; +} - if (i) - builder.appendLiteral(", "); +void SoupNetworkSession::checkTLSErrors(SoupRequest* soupRequest, SoupMessage* message, std::function<void (const ResourceError&)>&& completionHandler) +{ + if (gIgnoreTLSErrors) { + completionHandler({ }); + return; + } - builder.append(languages[i]); + GTlsCertificate* certificate = nullptr; + GTlsCertificateFlags tlsErrors = static_cast<GTlsCertificateFlags>(0); + soup_message_get_https_status(message, &certificate, &tlsErrors); + if (!tlsErrors) { + completionHandler({ }); + return; + } - int quality = 100 - i * delta; - if (quality > 0 && quality < 100) { - char buffer[8]; - g_ascii_formatd(buffer, 8, "%.2f", quality / 100.0); - builder.append(String::format(";q=%s", buffer)); - } + URL url(soup_request_get_uri(soupRequest)); + auto it = clientCertificates().find(url.host()); + if (it != clientCertificates().end() && it->value.contains(certificate)) { + completionHandler({ }); + return; } - return builder.toString().utf8(); + completionHandler(ResourceError::tlsError(soupRequest, tlsErrors, certificate)); } -void SoupNetworkSession::setAcceptLanguages(const Vector<String>& languages) +void SoupNetworkSession::allowSpecificHTTPSCertificateForHost(const CertificateInfo& certificateInfo, const String& host) { - g_object_set(m_soupSession.get(), "accept-language", buildAcceptLanguages(languages).data(), nullptr); + clientCertificates().add(host, HostTLSCertificateSet()).iterator->value.add(certificateInfo.certificate()); } } // namespace WebCore +#endif diff --git a/Source/WebCore/platform/network/soup/SoupNetworkSession.h b/Source/WebCore/platform/network/soup/SoupNetworkSession.h index b9801f9f9..908b6cb6d 100644 --- a/Source/WebCore/platform/network/soup/SoupNetworkSession.h +++ b/Source/WebCore/platform/network/soup/SoupNetworkSession.h @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -26,54 +26,52 @@ #ifndef SoupNetworkSession_h #define SoupNetworkSession_h +#include <functional> +#include <glib-object.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> -#include <wtf/gobject/GRefPtr.h> +#include <wtf/glib/GRefPtr.h> #include <wtf/text/WTFString.h> typedef struct _SoupCache SoupCache; typedef struct _SoupCookieJar SoupCookieJar; +typedef struct _SoupMessage SoupMessage; +typedef struct _SoupRequest SoupRequest; typedef struct _SoupSession SoupSession; namespace WebCore { +class CertificateInfo; +class ResourceError; +struct SoupNetworkProxySettings; + class SoupNetworkSession { WTF_MAKE_NONCOPYABLE(SoupNetworkSession); WTF_MAKE_FAST_ALLOCATED; public: + explicit SoupNetworkSession(SoupCookieJar* = nullptr); ~SoupNetworkSession(); - static SoupNetworkSession& defaultSession(); - static std::unique_ptr<SoupNetworkSession> createPrivateBrowsingSession(); - static std::unique_ptr<SoupNetworkSession> createTestingSession(); - static std::unique_ptr<SoupNetworkSession> createForSoupSession(SoupSession*); - - enum SSLPolicyFlags { - SSLStrict = 1 << 0, - SSLUseSystemCAFile = 1 << 1 - }; - typedef unsigned SSLPolicy; - SoupSession* soupSession() const { return m_soupSession.get(); } void setCookieJar(SoupCookieJar*); SoupCookieJar* cookieJar() const; - void setCache(SoupCache*); - SoupCache* cache() const; + static void clearOldSoupCache(const String& cacheDirectory); - void setSSLPolicy(SSLPolicy); - SSLPolicy sslPolicy() const; + static void setProxySettings(const SoupNetworkProxySettings&); + void setupProxy(); - void setHTTPProxy(const char* httpProxy, const char* httpProxyExceptions); - char* httpProxy() const; - void setupHTTPProxyFromEnvironment(); + static void setInitialAcceptLanguages(const CString&); + void setAcceptLanguages(const CString&); - void setAcceptLanguages(const Vector<String>&); + static void setShouldIgnoreTLSErrors(bool); + static void checkTLSErrors(SoupRequest*, SoupMessage*, std::function<void (const ResourceError&)>&&); + static void allowSpecificHTTPSCertificateForHost(const CertificateInfo&, const String& host); -private: - SoupNetworkSession(SoupCookieJar*); - SoupNetworkSession(SoupSession*); + static void setCustomProtocolRequestType(GType); + void setupCustomProtocols(); +private: void setupLogger(); GRefPtr<SoupSession> m_soupSession; diff --git a/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp b/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp index cfc01aed7..09e42d8e9 100644 --- a/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp +++ b/Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp @@ -24,6 +24,9 @@ */ #include "config.h" + +#if USE(SOUP) + #include "SynchronousLoaderClient.h" #include "AuthenticationChallenge.h" @@ -44,3 +47,6 @@ ResourceError SynchronousLoaderClient::platformBadResponseError() } } + +#endif + diff --git a/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp new file mode 100644 index 000000000..ec1325842 --- /dev/null +++ b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "WebKitSoupRequestGeneric.h" + +#include "ResourceRequest.h" +#include <wtf/text/CString.h> + +using namespace WebCore; + +G_DEFINE_TYPE(WebKitSoupRequestGeneric, webkit_soup_request_generic, SOUP_TYPE_REQUEST) + +struct _WebKitSoupRequestGenericPrivate { + CString mimeType; + goffset contentLength; + ResourceRequest resourceRequest; +}; + +static void webkitSoupRequestGenericFinalize(GObject* object) +{ + WEBKIT_SOUP_REQUEST_GENERIC(object)->priv->~WebKitSoupRequestGenericPrivate(); + G_OBJECT_CLASS(webkit_soup_request_generic_parent_class)->finalize(object); +} + +static void webkit_soup_request_generic_init(WebKitSoupRequestGeneric* request) +{ + WebKitSoupRequestGenericPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(request, WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGenericPrivate); + request->priv = priv; + new (priv) WebKitSoupRequestGenericPrivate(); +} + +static void webkitSoupRequestGenericSendAsync(SoupRequest* request, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) +{ + WebKitSoupRequestGenericClient* client = WEBKIT_SOUP_REQUEST_GENERIC_GET_CLASS(request)->client; + ASSERT(client); + client->startRequest(adoptGRef(g_task_new(request, cancellable, callback, userData))); +} + +static GInputStream* webkitSoupRequestGenericSendFinish(SoupRequest* request, GAsyncResult* result, GError** error) +{ + g_return_val_if_fail(g_task_is_valid(result, request), nullptr); + auto* inputStream = g_task_propagate_pointer(G_TASK(result), error); + return inputStream ? G_INPUT_STREAM(inputStream) : nullptr; +} + +static goffset webkitSoupRequestGenericGetContentLength(SoupRequest* request) +{ + return WEBKIT_SOUP_REQUEST_GENERIC(request)->priv->contentLength; +} + +static const char* webkitSoupRequestGenericGetContentType(SoupRequest* request) +{ + return WEBKIT_SOUP_REQUEST_GENERIC(request)->priv->mimeType.data(); +} + +static void webkit_soup_request_generic_class_init(WebKitSoupRequestGenericClass* requestGenericClass) +{ + GObjectClass* gObjectClass = G_OBJECT_CLASS(requestGenericClass); + gObjectClass->finalize = webkitSoupRequestGenericFinalize; + + SoupRequestClass* requestClass = SOUP_REQUEST_CLASS(requestGenericClass); + requestClass->send_async = webkitSoupRequestGenericSendAsync; + requestClass->send_finish = webkitSoupRequestGenericSendFinish; + requestClass->get_content_length = webkitSoupRequestGenericGetContentLength; + requestClass->get_content_type = webkitSoupRequestGenericGetContentType; + + g_type_class_add_private(requestGenericClass, sizeof(WebKitSoupRequestGenericPrivate)); +} + +void webkitSoupRequestGenericSetContentLength(WebKitSoupRequestGeneric* request, goffset contentLength) +{ + request->priv->contentLength = contentLength; +} + +void webkitSoupRequestGenericSetContentType(WebKitSoupRequestGeneric* request, const char* mimeType) +{ + request->priv->mimeType = mimeType; +} + +void webkitSoupRequestGenericSetRequest(WebKitSoupRequestGeneric* request, const ResourceRequest& resourceRequest) +{ + request->priv->resourceRequest = resourceRequest; +} + +const ResourceRequest& webkitSoupRequestGenericGetRequest(WebKitSoupRequestGeneric* request) +{ + return request->priv->resourceRequest; +} diff --git a/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h new file mode 100644 index 000000000..09cbd2725 --- /dev/null +++ b/Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef WebKitSoupRequestGeneric_h +#define WebKitSoupRequestGeneric_h + +#include "WebKitSoupRequestGenericClient.h" +#include <glib-object.h> +#include <libsoup/soup.h> + +G_BEGIN_DECLS + +namespace WebCore { +class ResourceRequest; +} + +#define WEBKIT_TYPE_SOUP_REQUEST_GENERIC (webkit_soup_request_generic_get_type()) +#define WEBKIT_SOUP_REQUEST_GENERIC(object) (G_TYPE_CHECK_INSTANCE_CAST((object), WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGeneric)) +#define WEBKIT_IS_SOUP_REQUEST_GENERIC(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), WEBKIT_TYPE_SOUP_REQUEST_GENERIC)) +#define WEBKIT_SOUP_REQUEST_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGenericClass)) +#define WEBKIT_IS_SOUP_REQUEST_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), WEBKIT_TYPE_SOUP_REQUEST_GENERIC)) +#define WEBKIT_SOUP_REQUEST_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), WEBKIT_TYPE_SOUP_REQUEST_GENERIC, WebKitSoupRequestGenericClass)) + +typedef struct _WebKitSoupRequestGeneric WebKitSoupRequestGeneric; +typedef struct _WebKitSoupRequestGenericClass WebKitSoupRequestGenericClass; +typedef struct _WebKitSoupRequestGenericPrivate WebKitSoupRequestGenericPrivate; + +struct _WebKitSoupRequestGeneric { + SoupRequest parent; + + WebKitSoupRequestGenericPrivate *priv; +}; + +struct _WebKitSoupRequestGenericClass { + SoupRequestClass parent; + + WebCore::WebKitSoupRequestGenericClient* client; +}; + +GType webkit_soup_request_generic_get_type(); + +void webkitSoupRequestGenericSetContentLength(WebKitSoupRequestGeneric*, goffset contentLength); +void webkitSoupRequestGenericSetContentType(WebKitSoupRequestGeneric*, const char* mimeType); +void webkitSoupRequestGenericSetRequest(WebKitSoupRequestGeneric*, const WebCore::ResourceRequest&); +const WebCore::ResourceRequest& webkitSoupRequestGenericGetRequest(WebKitSoupRequestGeneric*); + +G_END_DECLS + +#endif // WebKitSoupRequestGeneric_h diff --git a/Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h b/Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h new file mode 100644 index 000000000..db470a67f --- /dev/null +++ b/Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 Igalia S.L. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include <wtf/glib/GRefPtr.h> + +typedef struct _GTask GTask; + +namespace WebCore { + +class WebKitSoupRequestGenericClient { +public: + virtual void startRequest(GRefPtr<GTask>&&) = 0; +}; + +} // namespace WebCore + |