summaryrefslogtreecommitdiff
path: root/Source/WebCore/platform/network/soup
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/network/soup')
-rw-r--r--Source/WebCore/platform/network/soup/AuthenticationChallenge.h10
-rw-r--r--Source/WebCore/platform/network/soup/AuthenticationChallengeSoup.cpp15
-rw-r--r--Source/WebCore/platform/network/soup/CertificateInfo.cpp5
-rw-r--r--Source/WebCore/platform/network/soup/CertificateInfo.h5
-rw-r--r--Source/WebCore/platform/network/soup/CookieJarSoup.cpp133
-rw-r--r--Source/WebCore/platform/network/soup/CookieJarSoup.h41
-rw-r--r--Source/WebCore/platform/network/soup/CookieStorageSoup.cpp26
-rw-r--r--Source/WebCore/platform/network/soup/CredentialStorageSoup.cpp4
-rw-r--r--Source/WebCore/platform/network/soup/DNSSoup.cpp65
-rw-r--r--Source/WebCore/platform/network/soup/GRefPtrSoup.cpp35
-rw-r--r--Source/WebCore/platform/network/soup/GRefPtrSoup.h37
-rw-r--r--Source/WebCore/platform/network/soup/GUniquePtrSoup.h2
-rw-r--r--Source/WebCore/platform/network/soup/NetworkStorageSessionSoup.cpp222
-rw-r--r--Source/WebCore/platform/network/soup/ProxyServerSoup.cpp4
-rw-r--r--Source/WebCore/platform/network/soup/ResourceError.h30
-rw-r--r--Source/WebCore/platform/network/soup/ResourceErrorSoup.cpp31
-rw-r--r--Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp699
-rw-r--r--Source/WebCore/platform/network/soup/ResourceRequest.h75
-rw-r--r--Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp107
-rw-r--r--Source/WebCore/platform/network/soup/ResourceResponse.h45
-rw-r--r--Source/WebCore/platform/network/soup/ResourceResponseSoup.cpp53
-rw-r--r--Source/WebCore/platform/network/soup/SocketStreamError.h50
-rw-r--r--Source/WebCore/platform/network/soup/SocketStreamHandle.h84
-rw-r--r--Source/WebCore/platform/network/soup/SocketStreamHandleImpl.h83
-rw-r--r--Source/WebCore/platform/network/soup/SocketStreamHandleImplSoup.cpp274
-rw-r--r--Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp288
-rw-r--r--Source/WebCore/platform/network/soup/SoupNetworkProxySettings.h69
-rw-r--r--Source/WebCore/platform/network/soup/SoupNetworkSession.cpp305
-rw-r--r--Source/WebCore/platform/network/soup/SoupNetworkSession.h48
-rw-r--r--Source/WebCore/platform/network/soup/SynchronousLoaderClientSoup.cpp6
-rw-r--r--Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.cpp105
-rw-r--r--Source/WebCore/platform/network/soup/WebKitSoupRequestGeneric.h65
-rw-r--r--Source/WebCore/platform/network/soup/WebKitSoupRequestGenericClient.h34
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, &currentResolver.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
+