summaryrefslogtreecommitdiff
path: root/Source/WebKit2/NetworkProcess/soup
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebKit2/NetworkProcess/soup')
-rw-r--r--Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.cpp1102
-rw-r--r--Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.h145
-rw-r--r--Source/WebKit2/NetworkProcess/soup/NetworkProcessMainSoup.cpp40
-rw-r--r--Source/WebKit2/NetworkProcess/soup/NetworkProcessSoup.cpp149
-rw-r--r--Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.cpp65
-rw-r--r--Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.h (renamed from Source/WebKit2/NetworkProcess/soup/NetworkResourceLoadSchedulerSoup.cpp)37
-rw-r--r--Source/WebKit2/NetworkProcess/soup/RemoteNetworkingContextSoup.cpp17
7 files changed, 1468 insertions, 87 deletions
diff --git a/Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.cpp b/Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.cpp
new file mode 100644
index 000000000..8e77fc407
--- /dev/null
+++ b/Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.cpp
@@ -0,0 +1,1102 @@
+/*
+ * 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
+ * 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.
+ */
+
+#include "config.h"
+#include "NetworkDataTaskSoup.h"
+
+#include "AuthenticationManager.h"
+#include "DataReference.h"
+#include "Download.h"
+#include "DownloadSoupErrors.h"
+#include "NetworkLoad.h"
+#include "NetworkProcess.h"
+#include "NetworkSessionSoup.h"
+#include "WebErrors.h"
+#include <WebCore/AuthenticationChallenge.h>
+#include <WebCore/HTTPParsers.h>
+#include <WebCore/MIMETypeRegistry.h>
+#include <WebCore/NetworkStorageSession.h>
+#include <WebCore/SharedBuffer.h>
+#include <WebCore/SoupNetworkSession.h>
+#include <wtf/MainThread.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+static const size_t gDefaultReadBufferSize = 8192;
+
+NetworkDataTaskSoup::NetworkDataTaskSoup(NetworkSession& session, NetworkDataTaskClient& client, const ResourceRequest& requestWithCredentials, StoredCredentials storedCredentials, ContentSniffingPolicy shouldContentSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect)
+ : NetworkDataTask(session, client, requestWithCredentials, storedCredentials, shouldClearReferrerOnHTTPSToHTTPRedirect)
+ , m_shouldContentSniff(shouldContentSniff)
+ , m_timeoutSource(RunLoop::main(), this, &NetworkDataTaskSoup::timeoutFired)
+{
+ m_session->registerNetworkDataTask(*this);
+ if (m_scheduledFailureType != NoFailure)
+ return;
+
+ auto request = requestWithCredentials;
+ if (request.url().protocolIsInHTTPFamily()) {
+#if ENABLE(WEB_TIMING)
+ m_startTime = monotonicallyIncreasingTimeMS();
+#endif
+ auto url = request.url();
+ if (m_storedCredentials == AllowStoredCredentials) {
+ m_user = url.user();
+ m_password = url.pass();
+ request.removeCredentials();
+
+ if (m_user.isEmpty() && m_password.isEmpty())
+ m_initialCredential = m_session->networkStorageSession().credentialStorage().get(m_partition, request.url());
+ else
+ m_session->networkStorageSession().credentialStorage().set(m_partition, Credential(m_user, m_password, CredentialPersistenceNone), request.url());
+ }
+ applyAuthenticationToRequest(request);
+ }
+ createRequest(WTFMove(request));
+}
+
+NetworkDataTaskSoup::~NetworkDataTaskSoup()
+{
+ clearRequest();
+ m_session->unregisterNetworkDataTask(*this);
+}
+
+String NetworkDataTaskSoup::suggestedFilename() const
+{
+ if (!m_suggestedFilename.isEmpty())
+ return m_suggestedFilename;
+
+ String suggestedFilename = m_response.suggestedFilename();
+ if (!suggestedFilename.isEmpty())
+ return suggestedFilename;
+
+ return decodeURLEscapeSequences(m_response.url().lastPathComponent());
+}
+
+void NetworkDataTaskSoup::setPendingDownloadLocation(const String& filename, const SandboxExtension::Handle& sandboxExtensionHandle, bool allowOverwrite)
+{
+ NetworkDataTask::setPendingDownloadLocation(filename, sandboxExtensionHandle, allowOverwrite);
+ m_allowOverwriteDownload = allowOverwrite;
+}
+
+void NetworkDataTaskSoup::createRequest(ResourceRequest&& request)
+{
+ m_currentRequest = WTFMove(request);
+
+ GUniquePtr<SoupURI> soupURI = m_currentRequest.createSoupURI();
+ if (!soupURI) {
+ scheduleFailure(InvalidURLFailure);
+ return;
+ }
+
+ GRefPtr<SoupRequest> soupRequest = adoptGRef(soup_session_request_uri(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), soupURI.get(), nullptr));
+ if (!soupRequest) {
+ scheduleFailure(InvalidURLFailure);
+ return;
+ }
+
+ m_currentRequest.updateSoupRequest(soupRequest.get());
+
+ if (!m_currentRequest.url().protocolIsInHTTPFamily()) {
+ m_soupRequest = WTFMove(soupRequest);
+ return;
+ }
+
+ // HTTP request.
+ GRefPtr<SoupMessage> soupMessage = adoptGRef(soup_request_http_get_message(SOUP_REQUEST_HTTP(soupRequest.get())));
+ if (!soupMessage) {
+ scheduleFailure(InvalidURLFailure);
+ return;
+ }
+
+ unsigned messageFlags = SOUP_MESSAGE_NO_REDIRECT;
+
+ m_currentRequest.updateSoupMessage(soupMessage.get());
+ if (m_shouldContentSniff == DoNotSniffContent)
+ soup_message_disable_feature(soupMessage.get(), SOUP_TYPE_CONTENT_SNIFFER);
+ if (m_user.isEmpty() && m_password.isEmpty() && m_storedCredentials == DoNotAllowStoredCredentials) {
+#if SOUP_CHECK_VERSION(2, 57, 1)
+ messageFlags |= SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE;
+#else
+ // In case credential is not available and credential storage should not to be used,
+ // disable authentication manager so that credentials stored in libsoup are not used.
+ soup_message_disable_feature(soupMessage.get(), SOUP_TYPE_AUTH_MANAGER);
+#endif
+ }
+
+ // 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"))
+ soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*");
+
+ // In the case of XHR .send() and .send("") explicitly tell libsoup to send a zero content-lenght header
+ // for consistency with other UA implementations like Firefox. 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 ((soupMessage->method == SOUP_METHOD_POST || soupMessage->method == SOUP_METHOD_PUT) && !soupMessage->request_body->length)
+ soup_message_headers_set_content_length(soupMessage->request_headers, 0);
+
+ soup_message_set_flags(soupMessage.get(), static_cast<SoupMessageFlags>(soup_message_get_flags(soupMessage.get()) | messageFlags));
+
+#if SOUP_CHECK_VERSION(2, 43, 1)
+ soup_message_set_priority(soupMessage.get(), toSoupMessagePriority(m_currentRequest.priority()));
+#endif
+
+ m_soupRequest = WTFMove(soupRequest);
+ m_soupMessage = WTFMove(soupMessage);
+
+ g_signal_connect(m_soupMessage.get(), "notify::tls-errors", G_CALLBACK(tlsErrorsChangedCallback), this);
+ g_signal_connect(m_soupMessage.get(), "got-headers", G_CALLBACK(gotHeadersCallback), this);
+ g_signal_connect(m_soupMessage.get(), "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), this);
+ g_signal_connect(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), "authenticate", G_CALLBACK(authenticateCallback), this);
+#if ENABLE(WEB_TIMING)
+ g_signal_connect(m_soupMessage.get(), "network-event", G_CALLBACK(networkEventCallback), this);
+ g_signal_connect(m_soupMessage.get(), "restarted", G_CALLBACK(restartedCallback), this);
+#if SOUP_CHECK_VERSION(2, 49, 91)
+ g_signal_connect(m_soupMessage.get(), "starting", G_CALLBACK(startingCallback), this);
+#else
+ g_signal_connect(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), "request-started", G_CALLBACK(requestStartedCallback), this);
+#endif
+#endif
+}
+
+void NetworkDataTaskSoup::clearRequest()
+{
+ if (m_state == State::Completed)
+ return;
+
+ m_state = State::Completed;
+
+ stopTimeout();
+ m_pendingResult = nullptr;
+ m_soupRequest = nullptr;
+ m_inputStream = nullptr;
+ m_multipartInputStream = nullptr;
+ m_downloadOutputStream = nullptr;
+ g_cancellable_cancel(m_cancellable.get());
+ m_cancellable = nullptr;
+ if (m_soupMessage) {
+ g_signal_handlers_disconnect_matched(m_soupMessage.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+ soup_session_cancel_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get(), SOUP_STATUS_CANCELLED);
+ m_soupMessage = nullptr;
+ }
+ g_signal_handlers_disconnect_matched(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+}
+
+void NetworkDataTaskSoup::resume()
+{
+ ASSERT(m_state != State::Running);
+ if (m_state == State::Canceling || m_state == State::Completed)
+ return;
+
+ m_state = State::Running;
+
+ if (m_scheduledFailureType != NoFailure) {
+ ASSERT(m_failureTimer.isActive());
+ return;
+ }
+
+ startTimeout();
+
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ if (m_soupRequest && !m_cancellable) {
+ m_cancellable = adoptGRef(g_cancellable_new());
+ soup_request_send_async(m_soupRequest.get(), m_cancellable.get(), reinterpret_cast<GAsyncReadyCallback>(sendRequestCallback), protectedThis.leakRef());
+ return;
+ }
+
+ if (m_pendingResult) {
+ GRefPtr<GAsyncResult> pendingResult = WTFMove(m_pendingResult);
+ if (m_inputStream)
+ readCallback(m_inputStream.get(), pendingResult.get(), protectedThis.leakRef());
+ else if (m_multipartInputStream)
+ requestNextPartCallback(m_multipartInputStream.get(), pendingResult.get(), protectedThis.leakRef());
+ else if (m_soupRequest)
+ sendRequestCallback(m_soupRequest.get(), pendingResult.get(), protectedThis.leakRef());
+ else
+ ASSERT_NOT_REACHED();
+ }
+}
+
+void NetworkDataTaskSoup::suspend()
+{
+ ASSERT(m_state != State::Suspended);
+ if (m_state == State::Canceling || m_state == State::Completed)
+ return;
+ m_state = State::Suspended;
+
+ stopTimeout();
+}
+
+void NetworkDataTaskSoup::cancel()
+{
+ if (m_state == State::Canceling || m_state == State::Completed)
+ return;
+
+ m_state = State::Canceling;
+
+ if (m_soupMessage)
+ soup_session_cancel_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get(), SOUP_STATUS_CANCELLED);
+
+ g_cancellable_cancel(m_cancellable.get());
+
+ if (isDownload())
+ cleanDownloadFiles();
+}
+
+void NetworkDataTaskSoup::invalidateAndCancel()
+{
+ cancel();
+ clearRequest();
+}
+
+NetworkDataTask::State NetworkDataTaskSoup::state() const
+{
+ return m_state;
+}
+
+void NetworkDataTaskSoup::timeoutFired()
+{
+ if (m_state == State::Canceling || m_state == State::Completed || !m_client) {
+ clearRequest();
+ return;
+ }
+
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ invalidateAndCancel();
+ m_client->didCompleteWithError(ResourceError::timeoutError(m_firstRequest.url()));
+}
+
+void NetworkDataTaskSoup::startTimeout()
+{
+ if (m_firstRequest.timeoutInterval() > 0)
+ m_timeoutSource.startOneShot(m_firstRequest.timeoutInterval());
+}
+
+void NetworkDataTaskSoup::stopTimeout()
+{
+ m_timeoutSource.stop();
+}
+
+void NetworkDataTaskSoup::sendRequestCallback(SoupRequest* soupRequest, GAsyncResult* result, NetworkDataTaskSoup* task)
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(soupRequest == task->m_soupRequest.get());
+
+ if (task->state() == State::Suspended) {
+ ASSERT(!task->m_pendingResult);
+ task->m_pendingResult = result;
+ return;
+ }
+
+ GUniqueOutPtr<GError> error;
+ GRefPtr<GInputStream> inputStream = adoptGRef(soup_request_send_finish(soupRequest, result, &error.outPtr()));
+ if (error)
+ task->didFail(ResourceError::httpError(task->m_soupMessage.get(), error.get(), soupRequest));
+ else
+ task->didSendRequest(WTFMove(inputStream));
+}
+
+void NetworkDataTaskSoup::didSendRequest(GRefPtr<GInputStream>&& inputStream)
+{
+ if (m_soupMessage) {
+ if (m_shouldContentSniff == SniffContent && m_soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED)
+ m_response.setSniffedContentType(soup_request_get_content_type(m_soupRequest.get()));
+ m_response.updateFromSoupMessage(m_soupMessage.get());
+ if (m_response.mimeType().isEmpty() && m_soupMessage->status_code != SOUP_STATUS_NOT_MODIFIED)
+ m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(m_response.url().path()));
+
+ if (shouldStartHTTPRedirection()) {
+ m_inputStream = WTFMove(inputStream);
+ skipInputStreamForRedirection();
+ return;
+ }
+
+ if (m_response.isMultipart())
+ m_multipartInputStream = adoptGRef(soup_multipart_input_stream_new(m_soupMessage.get(), inputStream.get()));
+ else
+ m_inputStream = WTFMove(inputStream);
+
+#if ENABLE(WEB_TIMING)
+ m_response.networkLoadTiming().responseStart = monotonicallyIncreasingTimeMS() - m_startTime;
+#endif
+ } else {
+ m_response.setURL(m_firstRequest.url());
+ const gchar* contentType = soup_request_get_content_type(m_soupRequest.get());
+ m_response.setMimeType(extractMIMETypeFromMediaType(contentType));
+ m_response.setTextEncodingName(extractCharsetFromMediaType(contentType));
+ m_response.setExpectedContentLength(soup_request_get_content_length(m_soupRequest.get()));
+ if (m_response.mimeType().isEmpty())
+ m_response.setMimeType(MIMETypeRegistry::getMIMETypeForPath(m_response.url().path()));
+
+ m_inputStream = WTFMove(inputStream);
+ }
+
+ dispatchDidReceiveResponse();
+}
+
+void NetworkDataTaskSoup::dispatchDidReceiveResponse()
+{
+ ASSERT(!m_response.isNull());
+
+ didReceiveResponse(ResourceResponse(m_response), [this, protectedThis = makeRef(*this)](PolicyAction policyAction) {
+ if (m_state == State::Canceling || m_state == State::Completed) {
+ clearRequest();
+ return;
+ }
+
+ switch (policyAction) {
+ case PolicyAction::PolicyUse:
+ if (m_inputStream)
+ read();
+ else if (m_multipartInputStream)
+ requestNextPart();
+ else
+ ASSERT_NOT_REACHED();
+
+ break;
+ case PolicyAction::PolicyIgnore:
+ clearRequest();
+ break;
+ case PolicyAction::PolicyDownload:
+ download();
+ break;
+ }
+ });
+}
+
+void NetworkDataTaskSoup::tlsErrorsChangedCallback(SoupMessage* soupMessage, GParamSpec*, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+
+ ASSERT(soupMessage == task->m_soupMessage.get());
+ task->tlsErrorsChanged();
+}
+
+void NetworkDataTaskSoup::tlsErrorsChanged()
+{
+ ASSERT(m_soupRequest);
+ SoupNetworkSession::checkTLSErrors(m_soupRequest.get(), m_soupMessage.get(), [this] (const ResourceError& error) {
+ if (error.isNull())
+ return;
+
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ invalidateAndCancel();
+ m_client->didCompleteWithError(error);
+ });
+}
+
+void NetworkDataTaskSoup::applyAuthenticationToRequest(ResourceRequest& request)
+{
+ if (m_user.isEmpty() && m_password.isEmpty())
+ return;
+
+ auto url = request.url();
+ url.setUser(m_user);
+ url.setPass(m_password);
+ request.setURL(url);
+
+ m_user = String();
+ m_password = String();
+}
+
+void NetworkDataTaskSoup::authenticateCallback(SoupSession* session, SoupMessage* soupMessage, SoupAuth* soupAuth, gboolean retrying, NetworkDataTaskSoup* task)
+{
+ ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
+ if (soupMessage != task->m_soupMessage.get())
+ return;
+
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+
+ task->authenticate(AuthenticationChallenge(soupMessage, soupAuth, retrying));
+}
+
+static inline bool isAuthenticationFailureStatusCode(int httpStatusCode)
+{
+ return httpStatusCode == SOUP_STATUS_PROXY_AUTHENTICATION_REQUIRED || httpStatusCode == SOUP_STATUS_UNAUTHORIZED;
+}
+
+void NetworkDataTaskSoup::authenticate(AuthenticationChallenge&& challenge)
+{
+ ASSERT(m_soupMessage);
+ if (m_storedCredentials == AllowStoredCredentials) {
+ if (!m_initialCredential.isEmpty() || challenge.previousFailureCount()) {
+ // 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
+ // NetworkDataTask, but the observable effect should be very minor, if any.
+ m_session->networkStorageSession().credentialStorage().remove(m_partition, challenge.protectionSpace());
+ }
+
+ if (!challenge.previousFailureCount()) {
+ auto credential = m_session->networkStorageSession().credentialStorage().get(m_partition, challenge.protectionSpace());
+ if (!credential.isEmpty() && credential != m_initialCredential) {
+ ASSERT(credential.persistence() == CredentialPersistenceNone);
+
+ if (isAuthenticationFailureStatusCode(challenge.failureResponse().httpStatusCode())) {
+ // Store the credential back, possibly adding it as a default for this directory.
+ m_session->networkStorageSession().credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
+ }
+ soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
+ return;
+ }
+ }
+ }
+
+ soup_session_pause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
+
+ // 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 (m_storedCredentials == AllowStoredCredentials) {
+ auto protectionSpace = challenge.protectionSpace();
+ m_session->networkStorageSession().getCredentialFromPersistentStorage(protectionSpace,
+ [this, protectedThis = makeRef(*this), authChallenge = WTFMove(challenge)] (Credential&& credential) mutable {
+ if (m_state == State::Canceling || m_state == State::Completed || !m_client) {
+ clearRequest();
+ return;
+ }
+
+ authChallenge.setProposedCredential(WTFMove(credential));
+ continueAuthenticate(WTFMove(authChallenge));
+ });
+ } else
+ continueAuthenticate(WTFMove(challenge));
+}
+
+void NetworkDataTaskSoup::continueAuthenticate(AuthenticationChallenge&& challenge)
+{
+ m_client->didReceiveChallenge(challenge, [this, protectedThis = makeRef(*this), challenge](AuthenticationChallengeDisposition disposition, const Credential& credential) {
+ if (m_state == State::Canceling || m_state == State::Completed) {
+ clearRequest();
+ return;
+ }
+
+ if (disposition == AuthenticationChallengeDisposition::Cancel) {
+ cancel();
+ didFail(cancelledError(m_soupRequest.get()));
+ return;
+ }
+
+ if (disposition == AuthenticationChallengeDisposition::UseCredential && !credential.isEmpty()) {
+ if (m_storedCredentials == AllowStoredCredentials) {
+ // 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)
+ m_session->networkStorageSession().credentialStorage().set(m_partition, credential, challenge.protectionSpace(), challenge.failureResponse().url());
+
+ if (credential.persistence() == CredentialPersistencePermanent) {
+ m_protectionSpaceForPersistentStorage = challenge.protectionSpace();
+ m_credentialForPersistentStorage = credential;
+ }
+ }
+
+ soup_auth_authenticate(challenge.soupAuth(), credential.user().utf8().data(), credential.password().utf8().data());
+ }
+
+ soup_session_unpause_message(static_cast<NetworkSessionSoup&>(m_session.get()).soupSession(), m_soupMessage.get());
+ });
+}
+
+void NetworkDataTaskSoup::skipInputStreamForRedirectionCallback(GInputStream* inputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(inputStream == task->m_inputStream.get());
+
+ GUniqueOutPtr<GError> error;
+ gssize bytesSkipped = g_input_stream_skip_finish(inputStream, result, &error.outPtr());
+ if (error)
+ task->didFail(ResourceError::genericGError(error.get(), task->m_soupRequest.get()));
+ else if (bytesSkipped > 0)
+ task->skipInputStreamForRedirection();
+ else
+ task->didFinishSkipInputStreamForRedirection();
+}
+
+void NetworkDataTaskSoup::skipInputStreamForRedirection()
+{
+ ASSERT(m_inputStream);
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ g_input_stream_skip_async(m_inputStream.get(), gDefaultReadBufferSize, G_PRIORITY_DEFAULT, m_cancellable.get(),
+ reinterpret_cast<GAsyncReadyCallback>(skipInputStreamForRedirectionCallback), protectedThis.leakRef());
+}
+
+void NetworkDataTaskSoup::didFinishSkipInputStreamForRedirection()
+{
+ g_input_stream_close(m_inputStream.get(), nullptr, nullptr);
+ continueHTTPRedirection();
+}
+
+static bool shouldRedirectAsGET(SoupMessage* message, bool crossOrigin)
+{
+ if (message->method == SOUP_METHOD_GET || message->method == SOUP_METHOD_HEAD)
+ return false;
+
+ switch (message->status_code) {
+ case SOUP_STATUS_SEE_OTHER:
+ return true;
+ case SOUP_STATUS_FOUND:
+ case SOUP_STATUS_MOVED_PERMANENTLY:
+ if (message->method == SOUP_METHOD_POST)
+ return true;
+ break;
+ }
+
+ if (crossOrigin && message->method == SOUP_METHOD_DELETE)
+ return true;
+
+ return false;
+}
+
+bool NetworkDataTaskSoup::shouldStartHTTPRedirection()
+{
+ ASSERT(m_soupMessage);
+ ASSERT(!m_response.isNull());
+
+ auto status = m_response.httpStatusCode();
+ if (!SOUP_STATUS_IS_REDIRECTION(status))
+ return false;
+
+ // Some 3xx status codes aren't actually redirects.
+ if (status == 300 || status == 304 || status == 305 || status == 306)
+ return false;
+
+ if (m_response.httpHeaderField(HTTPHeaderName::Location).isEmpty())
+ return false;
+
+ return true;
+}
+
+void NetworkDataTaskSoup::continueHTTPRedirection()
+{
+ ASSERT(m_soupMessage);
+ ASSERT(!m_response.isNull());
+
+ static const unsigned maxRedirects = 20;
+ if (m_redirectCount++ > maxRedirects) {
+ didFail(ResourceError::transportError(m_soupRequest.get(), SOUP_STATUS_TOO_MANY_REDIRECTS, "Too many redirects"));
+ return;
+ }
+
+ ResourceRequest request = m_currentRequest;
+ URL redirectedURL = URL(m_response.url(), m_response.httpHeaderField(HTTPHeaderName::Location));
+ if (!redirectedURL.hasFragmentIdentifier() && request.url().hasFragmentIdentifier())
+ redirectedURL.setFragmentIdentifier(request.url().fragmentIdentifier());
+ request.setURL(redirectedURL);
+
+ // Should not set Referer after a redirect from a secure resource to non-secure one.
+ if (m_shouldClearReferrerOnHTTPSToHTTPRedirect && !request.url().protocolIs("https") && protocolIs(request.httpReferrer(), "https"))
+ request.clearHTTPReferrer();
+
+ bool isCrossOrigin = !protocolHostAndPortAreEqual(m_currentRequest.url(), request.url());
+ if (!equalLettersIgnoringASCIICase(request.httpMethod(), "get")) {
+ // Change newRequest method to GET if change was made during a previous redirection or if current redirection says so.
+ if (m_soupMessage->method == SOUP_METHOD_GET || !request.url().protocolIsInHTTPFamily() || shouldRedirectAsGET(m_soupMessage.get(), isCrossOrigin)) {
+ request.setHTTPMethod("GET");
+ request.setHTTPBody(nullptr);
+ request.clearHTTPContentType();
+ }
+ }
+
+ const auto& url = request.url();
+ m_user = url.user();
+ m_password = url.pass();
+ m_lastHTTPMethod = request.httpMethod();
+ request.removeCredentials();
+
+ if (isCrossOrigin) {
+ // The network layer might carry over some headers from the original request that
+ // we want to strip here because the redirect is cross-origin.
+ request.clearHTTPAuthorization();
+ request.clearHTTPOrigin();
+ } else if (url.protocolIsInHTTPFamily() && m_storedCredentials == AllowStoredCredentials) {
+ if (m_user.isEmpty() && m_password.isEmpty()) {
+ auto credential = m_session->networkStorageSession().credentialStorage().get(m_partition, request.url());
+ if (!credential.isEmpty())
+ m_initialCredential = credential;
+ }
+ }
+
+ clearRequest();
+
+ auto response = ResourceResponse(m_response);
+ m_client->willPerformHTTPRedirection(WTFMove(response), WTFMove(request), [this, protectedThis = makeRef(*this), isCrossOrigin](const ResourceRequest& newRequest) {
+ if (newRequest.isNull() || m_state == State::Canceling)
+ return;
+
+ auto request = newRequest;
+ if (request.url().protocolIsInHTTPFamily()) {
+#if ENABLE(WEB_TIMING)
+ if (isCrossOrigin)
+ m_startTime = monotonicallyIncreasingTimeMS();
+#endif
+ applyAuthenticationToRequest(request);
+ }
+ createRequest(WTFMove(request));
+ if (m_soupRequest && m_state != State::Suspended) {
+ m_state = State::Suspended;
+ resume();
+ }
+ });
+}
+
+void NetworkDataTaskSoup::readCallback(GInputStream* inputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
+ if (task->state() == State::Canceling || task->state() == State::Completed || (!task->m_client && !task->isDownload())) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(inputStream == task->m_inputStream.get());
+
+ if (task->state() == State::Suspended) {
+ ASSERT(!task->m_pendingResult);
+ task->m_pendingResult = result;
+ return;
+ }
+
+ GUniqueOutPtr<GError> error;
+ gssize bytesRead = g_input_stream_read_finish(inputStream, result, &error.outPtr());
+ if (error)
+ task->didFail(ResourceError::genericGError(error.get(), task->m_soupRequest.get()));
+ else if (bytesRead > 0)
+ task->didRead(bytesRead);
+ else
+ task->didFinishRead();
+}
+
+void NetworkDataTaskSoup::read()
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ ASSERT(m_inputStream);
+ m_readBuffer.grow(gDefaultReadBufferSize);
+ g_input_stream_read_async(m_inputStream.get(), m_readBuffer.data(), m_readBuffer.size(), G_PRIORITY_DEFAULT, m_cancellable.get(),
+ reinterpret_cast<GAsyncReadyCallback>(readCallback), protectedThis.leakRef());
+}
+
+void NetworkDataTaskSoup::didRead(gssize bytesRead)
+{
+ m_readBuffer.shrink(bytesRead);
+ if (m_downloadOutputStream) {
+ ASSERT(isDownload());
+ writeDownload();
+ } else {
+ ASSERT(m_client);
+ m_client->didReceiveData(SharedBuffer::adoptVector(m_readBuffer));
+ read();
+ }
+}
+
+void NetworkDataTaskSoup::didFinishRead()
+{
+ ASSERT(m_inputStream);
+ g_input_stream_close(m_inputStream.get(), nullptr, nullptr);
+ m_inputStream = nullptr;
+ if (m_multipartInputStream) {
+ requestNextPart();
+ return;
+ }
+
+ if (m_downloadOutputStream) {
+ didFinishDownload();
+ return;
+ }
+
+ clearRequest();
+ ASSERT(m_client);
+ m_client->didCompleteWithError({ });
+}
+
+void NetworkDataTaskSoup::requestNextPartCallback(SoupMultipartInputStream* multipartInputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(multipartInputStream == task->m_multipartInputStream.get());
+
+ if (task->state() == State::Suspended) {
+ ASSERT(!task->m_pendingResult);
+ task->m_pendingResult = result;
+ return;
+ }
+
+ GUniqueOutPtr<GError> error;
+ GRefPtr<GInputStream> inputStream = adoptGRef(soup_multipart_input_stream_next_part_finish(multipartInputStream, result, &error.outPtr()));
+ if (error)
+ task->didFail(ResourceError::httpError(task->m_soupMessage.get(), error.get(), task->m_soupRequest.get()));
+ else if (inputStream)
+ task->didRequestNextPart(WTFMove(inputStream));
+ else
+ task->didFinishRequestNextPart();
+}
+
+void NetworkDataTaskSoup::requestNextPart()
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ ASSERT(m_multipartInputStream);
+ ASSERT(!m_inputStream);
+ soup_multipart_input_stream_next_part_async(m_multipartInputStream.get(), G_PRIORITY_DEFAULT, m_cancellable.get(),
+ reinterpret_cast<GAsyncReadyCallback>(requestNextPartCallback), protectedThis.leakRef());
+}
+
+void NetworkDataTaskSoup::didRequestNextPart(GRefPtr<GInputStream>&& inputStream)
+{
+ ASSERT(!m_inputStream);
+ m_inputStream = WTFMove(inputStream);
+ m_response = ResourceResponse();
+ m_response.setURL(m_firstRequest.url());
+ m_response.updateFromSoupMessageHeaders(soup_multipart_input_stream_get_headers(m_multipartInputStream.get()));
+ dispatchDidReceiveResponse();
+}
+
+void NetworkDataTaskSoup::didFinishRequestNextPart()
+{
+ ASSERT(!m_inputStream);
+ ASSERT(m_multipartInputStream);
+ g_input_stream_close(G_INPUT_STREAM(m_multipartInputStream.get()), nullptr, nullptr);
+ clearRequest();
+ m_client->didCompleteWithError({ });
+}
+
+void NetworkDataTaskSoup::gotHeadersCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(task->m_soupMessage.get() == soupMessage);
+ task->didGetHeaders();
+}
+
+void NetworkDataTaskSoup::didGetHeaders()
+{
+ // 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(m_soupMessage->status_code) && m_soupMessage->status_code < 500) {
+ m_session->networkStorageSession().saveCredentialToPersistentStorage(m_protectionSpaceForPersistentStorage, m_credentialForPersistentStorage);
+ m_protectionSpaceForPersistentStorage = ProtectionSpace();
+ m_credentialForPersistentStorage = Credential();
+ }
+}
+
+void NetworkDataTaskSoup::wroteBodyDataCallback(SoupMessage* soupMessage, SoupBuffer* buffer, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(task->m_soupMessage.get() == soupMessage);
+ task->didWriteBodyData(buffer->length);
+}
+
+void NetworkDataTaskSoup::didWriteBodyData(uint64_t bytesSent)
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+ m_bodyDataTotalBytesSent += bytesSent;
+ m_client->didSendData(m_bodyDataTotalBytesSent, m_soupMessage->request_body->length);
+}
+
+void NetworkDataTaskSoup::download()
+{
+ ASSERT(isDownload());
+ ASSERT(m_pendingDownloadLocation);
+ ASSERT(!m_response.isNull());
+
+ if (m_response.httpStatusCode() >= 400) {
+ didFailDownload(platformDownloadNetworkError(m_response.httpStatusCode(), m_response.url(), m_response.httpStatusText()));
+ return;
+ }
+
+ CString downloadDestinationPath = m_pendingDownloadLocation.utf8();
+ m_downloadDestinationFile = adoptGRef(g_file_new_for_path(downloadDestinationPath.data()));
+ GRefPtr<GFileOutputStream> outputStream;
+ GUniqueOutPtr<GError> error;
+ if (m_allowOverwriteDownload)
+ outputStream = adoptGRef(g_file_replace(m_downloadDestinationFile.get(), nullptr, FALSE, G_FILE_CREATE_NONE, nullptr, &error.outPtr()));
+ else
+ outputStream = adoptGRef(g_file_create(m_downloadDestinationFile.get(), G_FILE_CREATE_NONE, nullptr, &error.outPtr()));
+ if (!outputStream) {
+ didFailDownload(platformDownloadDestinationError(m_response, error->message));
+ return;
+ }
+
+ GUniquePtr<char> intermediatePath(g_strdup_printf("%s.wkdownload", downloadDestinationPath.data()));
+ m_downloadIntermediateFile = adoptGRef(g_file_new_for_path(intermediatePath.get()));
+ outputStream = adoptGRef(g_file_replace(m_downloadIntermediateFile.get(), nullptr, TRUE, G_FILE_CREATE_NONE, nullptr, &error.outPtr()));
+ if (!outputStream) {
+ didFailDownload(platformDownloadDestinationError(m_response, error->message));
+ return;
+ }
+ m_downloadOutputStream = adoptGRef(G_OUTPUT_STREAM(outputStream.leakRef()));
+
+ auto& downloadManager = NetworkProcess::singleton().downloadManager();
+ auto download = std::make_unique<Download>(downloadManager, m_pendingDownloadID, *this, m_session->sessionID(), suggestedFilename());
+ auto* downloadPtr = download.get();
+ downloadManager.dataTaskBecameDownloadTask(m_pendingDownloadID, WTFMove(download));
+ downloadPtr->didCreateDestination(m_pendingDownloadLocation);
+
+ ASSERT(!m_client);
+ read();
+}
+
+void NetworkDataTaskSoup::writeDownloadCallback(GOutputStream* outputStream, GAsyncResult* result, NetworkDataTaskSoup* task)
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis = adoptRef(task);
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->isDownload()) {
+ task->clearRequest();
+ return;
+ }
+ ASSERT(outputStream == task->m_downloadOutputStream.get());
+
+ GUniqueOutPtr<GError> error;
+ gsize bytesWritten;
+#if GLIB_CHECK_VERSION(2, 44, 0)
+ g_output_stream_write_all_finish(outputStream, result, &bytesWritten, &error.outPtr());
+#else
+ gssize writeTaskResult = g_task_propagate_int(G_TASK(result), &error.outPtr());
+ if (writeTaskResult != -1)
+ bytesWritten = writeTaskResult;
+#endif
+ if (error)
+ task->didFailDownload(platformDownloadDestinationError(task->m_response, error->message));
+ else
+ task->didWriteDownload(bytesWritten);
+}
+
+void NetworkDataTaskSoup::writeDownload()
+{
+ RefPtr<NetworkDataTaskSoup> protectedThis(this);
+#if GLIB_CHECK_VERSION(2, 44, 0)
+ g_output_stream_write_all_async(m_downloadOutputStream.get(), m_readBuffer.data(), m_readBuffer.size(), G_PRIORITY_DEFAULT, m_cancellable.get(),
+ reinterpret_cast<GAsyncReadyCallback>(writeDownloadCallback), protectedThis.leakRef());
+#else
+ GRefPtr<GTask> writeTask = adoptGRef(g_task_new(m_downloadOutputStream.get(), m_cancellable.get(),
+ reinterpret_cast<GAsyncReadyCallback>(writeDownloadCallback), protectedThis.leakRef()));
+ g_task_set_task_data(writeTask.get(), this, nullptr);
+ g_task_run_in_thread(writeTask.get(), [](GTask* writeTask, gpointer source, gpointer userData, GCancellable* cancellable) {
+ auto* task = static_cast<NetworkDataTaskSoup*>(userData);
+ GOutputStream* outputStream = G_OUTPUT_STREAM(source);
+ RELEASE_ASSERT(task->m_downloadOutputStream.get() == outputStream);
+ RELEASE_ASSERT(task->m_cancellable.get() == cancellable);
+ GError* error = nullptr;
+ if (g_cancellable_set_error_if_cancelled(cancellable, &error)) {
+ g_task_return_error(writeTask, error);
+ return;
+ }
+
+ gsize bytesWritten;
+ if (g_output_stream_write_all(outputStream, task->m_readBuffer.data(), task->m_readBuffer.size(), &bytesWritten, cancellable, &error))
+ g_task_return_int(writeTask, bytesWritten);
+ else
+ g_task_return_error(writeTask, error);
+ });
+#endif
+}
+
+void NetworkDataTaskSoup::didWriteDownload(gsize bytesWritten)
+{
+ ASSERT(bytesWritten == m_readBuffer.size());
+ auto* download = NetworkProcess::singleton().downloadManager().download(m_pendingDownloadID);
+ ASSERT(download);
+ download->didReceiveData(bytesWritten);
+ read();
+}
+
+void NetworkDataTaskSoup::didFinishDownload()
+{
+ ASSERT(!m_response.isNull());
+ ASSERT(m_downloadOutputStream);
+ g_output_stream_close(m_downloadOutputStream.get(), nullptr, nullptr);
+ m_downloadOutputStream = nullptr;
+
+ ASSERT(m_downloadDestinationFile);
+ ASSERT(m_downloadIntermediateFile);
+ GUniqueOutPtr<GError> error;
+ if (!g_file_move(m_downloadIntermediateFile.get(), m_downloadDestinationFile.get(), G_FILE_COPY_OVERWRITE, m_cancellable.get(), nullptr, nullptr, &error.outPtr())) {
+ didFailDownload(platformDownloadDestinationError(m_response, error->message));
+ return;
+ }
+
+ GRefPtr<GFileInfo> info = adoptGRef(g_file_info_new());
+ CString uri = m_response.url().string().utf8();
+ g_file_info_set_attribute_string(info.get(), "metadata::download-uri", uri.data());
+ g_file_info_set_attribute_string(info.get(), "xattr::xdg.origin.url", uri.data());
+ g_file_set_attributes_async(m_downloadDestinationFile.get(), info.get(), G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, nullptr, nullptr, nullptr);
+
+ clearRequest();
+ auto* download = NetworkProcess::singleton().downloadManager().download(m_pendingDownloadID);
+ ASSERT(download);
+ download->didFinish();
+}
+
+void NetworkDataTaskSoup::didFailDownload(const ResourceError& error)
+{
+ clearRequest();
+ cleanDownloadFiles();
+ if (m_client)
+ m_client->didCompleteWithError(error);
+ else {
+ auto* download = NetworkProcess::singleton().downloadManager().download(m_pendingDownloadID);
+ ASSERT(download);
+ download->didFail(error, IPC::DataReference());
+ }
+}
+
+void NetworkDataTaskSoup::cleanDownloadFiles()
+{
+ if (m_downloadDestinationFile) {
+ g_file_delete(m_downloadDestinationFile.get(), nullptr, nullptr);
+ m_downloadDestinationFile = nullptr;
+ }
+ if (m_downloadIntermediateFile) {
+ g_file_delete(m_downloadIntermediateFile.get(), nullptr, nullptr);
+ m_downloadIntermediateFile = nullptr;
+ }
+}
+
+void NetworkDataTaskSoup::didFail(const ResourceError& error)
+{
+ if (isDownload()) {
+ didFailDownload(platformDownloadNetworkError(error.errorCode(), error.failingURL(), error.localizedDescription()));
+ return;
+ }
+
+ clearRequest();
+ ASSERT(m_client);
+ m_client->didCompleteWithError(error);
+}
+
+#if ENABLE(WEB_TIMING)
+void NetworkDataTaskSoup::networkEventCallback(SoupMessage* soupMessage, GSocketClientEvent event, GIOStream*, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
+ return;
+
+ ASSERT(task->m_soupMessage.get() == soupMessage);
+ task->networkEvent(event);
+}
+
+void NetworkDataTaskSoup::networkEvent(GSocketClientEvent event)
+{
+ double deltaTime = monotonicallyIncreasingTimeMS() - m_startTime;
+ auto& loadTiming = m_response.networkLoadTiming();
+ switch (event) {
+ case G_SOCKET_CLIENT_RESOLVING:
+ loadTiming.domainLookupStart = deltaTime;
+ break;
+ case G_SOCKET_CLIENT_RESOLVED:
+ loadTiming.domainLookupEnd = deltaTime;
+ break;
+ case G_SOCKET_CLIENT_CONNECTING:
+ loadTiming.connectStart = deltaTime;
+ 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:
+ break;
+ case G_SOCKET_CLIENT_PROXY_NEGOTIATED:
+ break;
+ case G_SOCKET_CLIENT_TLS_HANDSHAKING:
+ loadTiming.secureConnectionStart = deltaTime;
+ break;
+ case G_SOCKET_CLIENT_TLS_HANDSHAKED:
+ break;
+ case G_SOCKET_CLIENT_COMPLETE:
+ loadTiming.connectEnd = deltaTime;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ break;
+ }
+}
+
+#if SOUP_CHECK_VERSION(2, 49, 91)
+void NetworkDataTaskSoup::startingCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
+{
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
+ return;
+
+ ASSERT(task->m_soupMessage.get() == soupMessage);
+ task->didStartRequest();
+}
+#else
+void NetworkDataTaskSoup::requestStartedCallback(SoupSession* session, SoupMessage* soupMessage, SoupSocket*, NetworkDataTaskSoup* task)
+{
+ ASSERT(session == static_cast<NetworkSessionSoup&>(task->m_session.get()).soupSession());
+ if (soupMessage != task->m_soupMessage.get())
+ return;
+
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
+ return;
+
+ task->didStartRequest();
+}
+#endif
+
+void NetworkDataTaskSoup::didStartRequest()
+{
+ m_response.networkLoadTiming().requestStart = monotonicallyIncreasingTimeMS() - m_startTime;
+}
+
+void NetworkDataTaskSoup::restartedCallback(SoupMessage* soupMessage, NetworkDataTaskSoup* task)
+{
+ // Called each time the message is going to be sent again except the first time.
+ // This happens when libsoup handles HTTP authentication.
+ if (task->state() == State::Canceling || task->state() == State::Completed || !task->m_client)
+ return;
+
+ ASSERT(task->m_soupMessage.get() == soupMessage);
+ task->didRestart();
+}
+
+void NetworkDataTaskSoup::didRestart()
+{
+ m_startTime = monotonicallyIncreasingTimeMS();
+}
+#endif // ENABLE(WEB_TIMING)
+
+} // namespace WebKit
+
diff --git a/Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.h b/Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.h
new file mode 100644
index 000000000..40ca87c85
--- /dev/null
+++ b/Source/WebKit2/NetworkProcess/soup/NetworkDataTaskSoup.h
@@ -0,0 +1,145 @@
+/*
+ * 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
+ * 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 "NetworkDataTask.h"
+#include <WebCore/ProtectionSpace.h>
+#include <WebCore/ResourceResponse.h>
+#include <wtf/RunLoop.h>
+#include <wtf/glib/GRefPtr.h>
+
+namespace WebKit {
+
+class NetworkDataTaskSoup final : public NetworkDataTask {
+public:
+ static Ref<NetworkDataTask> create(NetworkSession& session, NetworkDataTaskClient& client, const WebCore::ResourceRequest& request, WebCore::StoredCredentials storedCredentials, WebCore::ContentSniffingPolicy shouldContentSniff, bool shouldClearReferrerOnHTTPSToHTTPRedirect)
+ {
+ return adoptRef(*new NetworkDataTaskSoup(session, client, request, storedCredentials, shouldContentSniff, shouldClearReferrerOnHTTPSToHTTPRedirect));
+ }
+
+ ~NetworkDataTaskSoup();
+
+private:
+ NetworkDataTaskSoup(NetworkSession&, NetworkDataTaskClient&, const WebCore::ResourceRequest&, WebCore::StoredCredentials, WebCore::ContentSniffingPolicy, bool shouldClearReferrerOnHTTPSToHTTPRedirect);
+
+ void suspend() override;
+ void cancel() override;
+ void resume() override;
+ void invalidateAndCancel() override;
+ NetworkDataTask::State state() const override;
+
+ void setPendingDownloadLocation(const String&, const SandboxExtension::Handle&, bool /*allowOverwrite*/) override;
+ String suggestedFilename() const override;
+
+ void timeoutFired();
+ void startTimeout();
+ void stopTimeout();
+
+ void createRequest(WebCore::ResourceRequest&&);
+ void clearRequest();
+ static void sendRequestCallback(SoupRequest*, GAsyncResult*, NetworkDataTaskSoup*);
+ void didSendRequest(GRefPtr<GInputStream>&&);
+ void dispatchDidReceiveResponse();
+
+ static void tlsErrorsChangedCallback(SoupMessage*, GParamSpec*, NetworkDataTaskSoup*);
+ void tlsErrorsChanged();
+
+ void applyAuthenticationToRequest(WebCore::ResourceRequest&);
+ static void authenticateCallback(SoupSession*, SoupMessage*, SoupAuth*, gboolean retrying, NetworkDataTaskSoup*);
+ void authenticate(WebCore::AuthenticationChallenge&&);
+ void continueAuthenticate(WebCore::AuthenticationChallenge&&);
+
+ static void skipInputStreamForRedirectionCallback(GInputStream*, GAsyncResult*, NetworkDataTaskSoup*);
+ void skipInputStreamForRedirection();
+ void didFinishSkipInputStreamForRedirection();
+ bool shouldStartHTTPRedirection();
+ void continueHTTPRedirection();
+
+ static void readCallback(GInputStream*, GAsyncResult*, NetworkDataTaskSoup*);
+ void read();
+ void didRead(gssize bytesRead);
+ void didFinishRead();
+
+ static void requestNextPartCallback(SoupMultipartInputStream*, GAsyncResult*, NetworkDataTaskSoup*);
+ void requestNextPart();
+ void didRequestNextPart(GRefPtr<GInputStream>&&);
+ void didFinishRequestNextPart();
+
+ static void gotHeadersCallback(SoupMessage*, NetworkDataTaskSoup*);
+ void didGetHeaders();
+
+ static void wroteBodyDataCallback(SoupMessage*, SoupBuffer*, NetworkDataTaskSoup*);
+ void didWriteBodyData(uint64_t bytesSent);
+
+ void download();
+ static void writeDownloadCallback(GOutputStream*, GAsyncResult*, NetworkDataTaskSoup*);
+ void writeDownload();
+ void didWriteDownload(gsize bytesWritten);
+ void didFailDownload(const WebCore::ResourceError&);
+ void didFinishDownload();
+ void cleanDownloadFiles();
+
+ void didFail(const WebCore::ResourceError&);
+
+#if ENABLE(WEB_TIMING)
+ static void networkEventCallback(SoupMessage*, GSocketClientEvent, GIOStream*, NetworkDataTaskSoup*);
+ void networkEvent(GSocketClientEvent);
+#if SOUP_CHECK_VERSION(2, 49, 91)
+ static void startingCallback(SoupMessage*, NetworkDataTaskSoup*);
+#else
+ static void requestStartedCallback(SoupSession*, SoupMessage*, SoupSocket*, NetworkDataTaskSoup*);
+#endif
+ void didStartRequest();
+ static void restartedCallback(SoupMessage*, NetworkDataTaskSoup*);
+ void didRestart();
+#endif // ENABLE(WEB_TIMING)
+
+ State m_state { State::Suspended };
+ WebCore::ContentSniffingPolicy m_shouldContentSniff;
+ GRefPtr<SoupRequest> m_soupRequest;
+ GRefPtr<SoupMessage> m_soupMessage;
+ GRefPtr<GInputStream> m_inputStream;
+ GRefPtr<SoupMultipartInputStream> m_multipartInputStream;
+ GRefPtr<GCancellable> m_cancellable;
+ GRefPtr<GAsyncResult> m_pendingResult;
+ WebCore::ProtectionSpace m_protectionSpaceForPersistentStorage;
+ WebCore::Credential m_credentialForPersistentStorage;
+ WebCore::ResourceRequest m_currentRequest;
+ WebCore::ResourceResponse m_response;
+ Vector<char> m_readBuffer;
+ unsigned m_redirectCount { 0 };
+ uint64_t m_bodyDataTotalBytesSent { 0 };
+ GRefPtr<GFile> m_downloadDestinationFile;
+ GRefPtr<GFile> m_downloadIntermediateFile;
+ GRefPtr<GOutputStream> m_downloadOutputStream;
+ bool m_allowOverwriteDownload { false };
+#if ENABLE(WEB_TIMING)
+ double m_startTime { 0 };
+#endif
+ RunLoop::Timer<NetworkDataTaskSoup> m_timeoutSource;
+};
+
+} // namespace WebKit
diff --git a/Source/WebKit2/NetworkProcess/soup/NetworkProcessMainSoup.cpp b/Source/WebKit2/NetworkProcess/soup/NetworkProcessMainSoup.cpp
new file mode 100644
index 000000000..d5081acd3
--- /dev/null
+++ b/Source/WebKit2/NetworkProcess/soup/NetworkProcessMainSoup.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 Igalia S.L.
+ * Copyright (C) 2013 Company 100 Inc.
+ *
+ * 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 THE COPYRIGHT HOLDERS ``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.
+ */
+
+#include "config.h"
+#include "NetworkProcess.h"
+
+#include "ChildProcessMain.h"
+#include "NetworkProcessMainUnix.h"
+
+namespace WebKit {
+
+int NetworkProcessMainUnix(int argc, char** argv)
+{
+ return ChildProcessMain<NetworkProcess, ChildProcessMainBase>(argc, argv);
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit2/NetworkProcess/soup/NetworkProcessSoup.cpp b/Source/WebKit2/NetworkProcess/soup/NetworkProcessSoup.cpp
index 5730ca61c..d8dcde330 100644
--- a/Source/WebKit2/NetworkProcess/soup/NetworkProcessSoup.cpp
+++ b/Source/WebKit2/NetworkProcess/soup/NetworkProcessSoup.cpp
@@ -25,72 +25,100 @@
*/
#include "config.h"
-#if ENABLE(NETWORK_PROCESS)
#include "NetworkProcess.h"
+#include "NetworkCache.h"
#include "NetworkProcessCreationParameters.h"
#include "ResourceCachesToClear.h"
#include "WebCookieManager.h"
#include <WebCore/CertificateInfo.h>
#include <WebCore/FileSystem.h>
+#include <WebCore/NetworkStorageSession.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/ResourceHandle.h>
#include <WebCore/SoupNetworkSession.h>
#include <libsoup/soup.h>
-#include <wtf/gobject/GRefPtr.h>
-#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/RAMSize.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
using namespace WebCore;
namespace WebKit {
-static uint64_t getCacheDiskFreeSize(SoupCache* cache)
+static CString buildAcceptLanguages(const Vector<String>& languages)
{
- ASSERT(cache);
-
- GUniqueOutPtr<char> cacheDir;
- g_object_get(G_OBJECT(cache), "cache-dir", &cacheDir.outPtr(), NULL);
- if (!cacheDir)
- return 0;
-
- return WebCore::getVolumeFreeSizeForPath(cacheDir.get());
-}
+ 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;
+
+ if (i)
+ builder.appendLiteral(", ");
+
+ builder.append(languages[i]);
+
+ 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));
+ }
+ }
-static uint64_t getMemorySize()
-{
- static uint64_t kDefaultMemorySize = 512;
-#if !OS(WINDOWS)
- long pageSize = sysconf(_SC_PAGESIZE);
- if (pageSize == -1)
- return kDefaultMemorySize;
-
- long physPages = sysconf(_SC_PHYS_PAGES);
- if (physPages == -1)
- return kDefaultMemorySize;
-
- return ((pageSize / 1024) * physPages) / 1024;
-#else
- // Fallback to default for other platforms.
- return kDefaultMemorySize;
-#endif
+ return builder.toString().utf8();
}
void NetworkProcess::userPreferredLanguagesChanged(const Vector<String>& languages)
{
- SoupNetworkSession::defaultSession().setAcceptLanguages(languages);
+ auto acceptLanguages = buildAcceptLanguages(languages);
+ SoupNetworkSession::setInitialAcceptLanguages(acceptLanguages);
+ NetworkStorageSession::forEach([&acceptLanguages](const WebCore::NetworkStorageSession& session) {
+ if (auto* soupSession = session.soupNetworkSession())
+ soupSession->setAcceptLanguages(acceptLanguages);
+ });
}
void NetworkProcess::platformInitializeNetworkProcess(const NetworkProcessCreationParameters& parameters)
{
+ if (parameters.proxySettings.mode != SoupNetworkProxySettings::Mode::Default)
+ setNetworkProxySettings(parameters.proxySettings);
+
ASSERT(!parameters.diskCacheDirectory.isEmpty());
- GRefPtr<SoupCache> soupCache = adoptGRef(soup_cache_new(parameters.diskCacheDirectory.utf8().data(), SOUP_CACHE_SINGLE_USER));
- SoupNetworkSession::defaultSession().setCache(soupCache.get());
- // Set an initial huge max_size for the SoupCache so the call to soup_cache_load() won't evict any cached
- // resource. The final size of the cache will be set by NetworkProcess::platformSetCacheModel().
- unsigned initialMaxSize = soup_cache_get_max_size(soupCache.get());
- soup_cache_set_max_size(soupCache.get(), G_MAXUINT);
- soup_cache_load(soupCache.get());
- soup_cache_set_max_size(soupCache.get(), initialMaxSize);
+ m_diskCacheDirectory = parameters.diskCacheDirectory;
+
+ SoupNetworkSession::clearOldSoupCache(WebCore::directoryName(m_diskCacheDirectory));
+
+ NetworkCache::Cache::Parameters cacheParameters {
+ parameters.shouldEnableNetworkCacheEfficacyLogging
+#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION)
+ , parameters.shouldEnableNetworkCacheSpeculativeRevalidation
+#endif
+ };
+ NetworkCache::singleton().initialize(m_diskCacheDirectory, cacheParameters);
if (!parameters.cookiePersistentStoragePath.isEmpty()) {
supplement<WebCookieManager>()->setCookiePersistentStorage(parameters.cookiePersistentStoragePath,
@@ -104,37 +132,18 @@ void NetworkProcess::platformInitializeNetworkProcess(const NetworkProcessCreati
setIgnoreTLSErrors(parameters.ignoreTLSErrors);
}
-void NetworkProcess::platformSetCacheModel(CacheModel cacheModel)
+void NetworkProcess::platformSetURLCacheSize(unsigned, uint64_t)
{
- unsigned cacheTotalCapacity = 0;
- unsigned cacheMinDeadCapacity = 0;
- unsigned cacheMaxDeadCapacity = 0;
- double deadDecodedDataDeletionInterval = 0;
- unsigned pageCacheCapacity = 0;
-
- unsigned long urlCacheMemoryCapacity = 0;
- unsigned long urlCacheDiskCapacity = 0;
-
- SoupCache* cache = SoupNetworkSession::defaultSession().cache();
- uint64_t diskFreeSize = getCacheDiskFreeSize(cache) / 1024 / 1024;
-
- uint64_t memSize = getMemorySize();
- calculateCacheSizes(cacheModel, memSize, diskFreeSize,
- cacheTotalCapacity, cacheMinDeadCapacity, cacheMaxDeadCapacity, deadDecodedDataDeletionInterval,
- pageCacheCapacity, urlCacheMemoryCapacity, urlCacheDiskCapacity);
-
- if (urlCacheDiskCapacity > soup_cache_get_max_size(cache))
- soup_cache_set_max_size(cache, urlCacheDiskCapacity);
}
void NetworkProcess::setIgnoreTLSErrors(bool ignoreTLSErrors)
{
- ResourceHandle::setIgnoreSSLErrors(ignoreTLSErrors);
+ SoupNetworkSession::setShouldIgnoreTLSErrors(ignoreTLSErrors);
}
void NetworkProcess::allowSpecificHTTPSCertificateForHost(const CertificateInfo& certificateInfo, const String& host)
{
- ResourceHandle::setClientCertificate(host, certificateInfo.certificate());
+ SoupNetworkSession::allowSpecificHTTPSCertificateForHost(certificateInfo, host);
}
void NetworkProcess::clearCacheForAllOrigins(uint32_t cachesToClear)
@@ -142,7 +151,12 @@ void NetworkProcess::clearCacheForAllOrigins(uint32_t cachesToClear)
if (cachesToClear == InMemoryResourceCachesOnly)
return;
- soup_cache_clear(SoupNetworkSession::defaultSession().cache());
+ clearDiskCache(std::chrono::system_clock::time_point::min(), [] { });
+}
+
+void NetworkProcess::clearDiskCache(std::chrono::system_clock::time_point modifiedSince, std::function<void ()> completionHandler)
+{
+ NetworkCache::singleton().clear(modifiedSince, WTFMove(completionHandler));
}
void NetworkProcess::platformTerminate()
@@ -150,6 +164,13 @@ void NetworkProcess::platformTerminate()
notImplemented();
}
-} // namespace WebKit
+void NetworkProcess::setNetworkProxySettings(const SoupNetworkProxySettings& settings)
+{
+ SoupNetworkSession::setProxySettings(settings);
+ NetworkStorageSession::forEach([](const NetworkStorageSession& session) {
+ if (auto* soupSession = session.soupNetworkSession())
+ soupSession->setupProxy();
+ });
+}
-#endif
+} // namespace WebKit
diff --git a/Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.cpp b/Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.cpp
new file mode 100644
index 000000000..f6baedac1
--- /dev/null
+++ b/Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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
+ * 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.
+ */
+
+#include "config.h"
+#include "NetworkSessionSoup.h"
+
+#include "NetworkProcess.h"
+#include "WebCookieManager.h"
+#include <WebCore/NetworkStorageSession.h>
+#include <WebCore/SoupNetworkSession.h>
+#include <libsoup/soup.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+NetworkSessionSoup::NetworkSessionSoup(SessionID sessionID)
+ : NetworkSession(sessionID)
+{
+ networkStorageSession().setCookieObserverHandler([this] {
+ NetworkProcess::singleton().supplement<WebCookieManager>()->notifyCookiesDidChange(m_sessionID);
+ });
+}
+
+NetworkSessionSoup::~NetworkSessionSoup()
+{
+ if (auto* storageSession = NetworkStorageSession::storageSession(m_sessionID))
+ storageSession->setCookieObserverHandler(nullptr);
+}
+
+SoupSession* NetworkSessionSoup::soupSession() const
+{
+ return networkStorageSession().getOrCreateSoupNetworkSession().soupSession();
+}
+
+void NetworkSessionSoup::clearCredentials()
+{
+#if SOUP_CHECK_VERSION(2, 57, 1)
+ soup_auth_manager_clear_cached_credentials(SOUP_AUTH_MANAGER(soup_session_get_feature(soupSession(), SOUP_TYPE_AUTH_MANAGER)));
+#endif
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit2/NetworkProcess/soup/NetworkResourceLoadSchedulerSoup.cpp b/Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.h
index 9d03979e3..e0196029a 100644
--- a/Source/WebKit2/NetworkProcess/soup/NetworkResourceLoadSchedulerSoup.cpp
+++ b/Source/WebKit2/NetworkProcess/soup/NetworkSessionSoup.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
- * Copyright (C) 2013 Company 100 Inc.
+ * 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
@@ -24,26 +23,28 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "config.h"
-#if ENABLE(NETWORK_PROCESS)
+#pragma once
-#include "NetworkResourceLoadScheduler.h"
+#include "NetworkSession.h"
+
+typedef struct _SoupSession SoupSession;
namespace WebKit {
-void NetworkResourceLoadScheduler::platformInitializeMaximumHTTPConnectionCountPerHost()
-{
- // Soup has its own queue control; it wants to have all requests given to
- // it, so that it is able to look ahead, and schedule them in a good way.
- // See the comment in ResourceRequestSoup.cpp
- static const unsigned unlimitedConnectionCount = 10000;
+class NetworkSessionSoup final : public NetworkSession {
+public:
+ static Ref<NetworkSession> create(WebCore::SessionID sessionID)
+ {
+ return adoptRef(*new NetworkSessionSoup(sessionID));
+ }
+ ~NetworkSessionSoup();
- // FIXME: Take advantage of Web-platform specific knowledge that can help
- // prioritization better than libsoup alone can do.
- // See https://bugs.webkit.org/show_bug.cgi?id=110115#c13
- m_maxRequestsInFlightPerHost = unlimitedConnectionCount;
-}
+ SoupSession* soupSession() const;
-} // namespace WebKit
+private:
+ NetworkSessionSoup(WebCore::SessionID);
-#endif
+ void clearCredentials() override;
+};
+
+} // namespace WebKit
diff --git a/Source/WebKit2/NetworkProcess/soup/RemoteNetworkingContextSoup.cpp b/Source/WebKit2/NetworkProcess/soup/RemoteNetworkingContextSoup.cpp
index d7cf86efb..2dba10521 100644
--- a/Source/WebKit2/NetworkProcess/soup/RemoteNetworkingContextSoup.cpp
+++ b/Source/WebKit2/NetworkProcess/soup/RemoteNetworkingContextSoup.cpp
@@ -26,9 +26,10 @@
*/
#include "config.h"
-#if ENABLE(NETWORK_PROCESS)
#include "RemoteNetworkingContext.h"
+#include "NetworkSession.h"
+#include "SessionTracker.h"
#include <WebCore/NetworkStorageSession.h>
#include <WebCore/NotImplemented.h>
#include <WebCore/ResourceHandle.h>
@@ -46,16 +47,22 @@ bool RemoteNetworkingContext::isValid() const
return true;
}
-void RemoteNetworkingContext::ensurePrivateBrowsingSession(uint64_t sessionID)
+void RemoteNetworkingContext::ensurePrivateBrowsingSession(SessionID sessionID)
{
- notImplemented();
+ ASSERT(sessionID.isEphemeral());
+
+ if (NetworkStorageSession::storageSession(sessionID))
+ return;
+
+ NetworkStorageSession::ensurePrivateBrowsingSession(sessionID, String::number(sessionID.sessionID()));
+ SessionTracker::setSession(sessionID, NetworkSession::create(sessionID));
}
NetworkStorageSession& RemoteNetworkingContext::storageSession() const
{
+ if (auto session = NetworkStorageSession::storageSession(m_sessionID))
+ return *session;
return NetworkStorageSession::defaultStorageSession();
}
}
-
-#endif // ENABLE(NETWORK_PROCESS)