Skip to content
Permalink
Browse files
[WK2][NetworkSession] Add support for resuming downloads
https://bugs.webkit.org/show_bug.cgi?id=177625
<rdar://problem/34345975>

Reviewed by Alex Christensen.

Add support for resuming downloads for the WK2 NETWORK_SESSION code path.

This was tested manually. I was unable to write an API test because our tests
do not run an HTTP server and CFNetwork does not seem to generate resume data
when cancelling a download over non-HTTP.

* NetworkProcess/Downloads/Download.cpp:
(WebKit::Download::didReceiveChallenge):
(WebKit::Download::continueCanAuthenticateAgainstProtectionSpace):
* NetworkProcess/Downloads/Download.h:
* NetworkProcess/Downloads/DownloadManager.cpp:
(WebKit::DownloadManager::continueCanAuthenticateAgainstProtectionSpace):
(WebKit::DownloadManager::resumeDownload):
* NetworkProcess/Downloads/cocoa/DownloadCocoa.mm:
(WebKit::Download::resume):
This currently includes a workaround for <rdar://problem/34745171>. We update the
resume data to include the NSURLSessionResumeInfoLocalPath key with the final
destination path so that CFNetwork looks for the temporary data at the right
location.

* NetworkProcess/cocoa/NetworkSessionCocoa.h:
* NetworkProcess/cocoa/NetworkSessionCocoa.mm:
(-[WKNetworkSessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
(WebKit::NetworkSessionCocoa::downloadTaskWithResumeData):
* Shared/Authentication/AuthenticationManager.cpp:
(WebKit::AuthenticationManager::didReceiveAuthenticationChallenge):
* Shared/Authentication/AuthenticationManager.h:


Canonical link: https://commits.webkit.org/194575@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223431 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
cdumez committed Oct 16, 2017
1 parent 3e12f4c commit af7926ff6fcf10b261d18d045f9225c6aafce7d4
@@ -1,3 +1,39 @@
2017-10-16 Chris Dumez <cdumez@apple.com>

[WK2][NetworkSession] Add support for resuming downloads
https://bugs.webkit.org/show_bug.cgi?id=177625
<rdar://problem/34345975>

Reviewed by Alex Christensen.

Add support for resuming downloads for the WK2 NETWORK_SESSION code path.

This was tested manually. I was unable to write an API test because our tests
do not run an HTTP server and CFNetwork does not seem to generate resume data
when cancelling a download over non-HTTP.

* NetworkProcess/Downloads/Download.cpp:
(WebKit::Download::didReceiveChallenge):
(WebKit::Download::continueCanAuthenticateAgainstProtectionSpace):
* NetworkProcess/Downloads/Download.h:
* NetworkProcess/Downloads/DownloadManager.cpp:
(WebKit::DownloadManager::continueCanAuthenticateAgainstProtectionSpace):
(WebKit::DownloadManager::resumeDownload):
* NetworkProcess/Downloads/cocoa/DownloadCocoa.mm:
(WebKit::Download::resume):
This currently includes a workaround for <rdar://problem/34745171>. We update the
resume data to include the NSURLSessionResumeInfoLocalPath key with the final
destination path so that CFNetwork looks for the temporary data at the right
location.

* NetworkProcess/cocoa/NetworkSessionCocoa.h:
* NetworkProcess/cocoa/NetworkSessionCocoa.mm:
(-[WKNetworkSessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
(WebKit::NetworkSessionCocoa::downloadTaskWithResumeData):
* Shared/Authentication/AuthenticationManager.cpp:
(WebKit::AuthenticationManager::didReceiveAuthenticationChallenge):
* Shared/Authentication/AuthenticationManager.h:

2017-10-16 Adrian Perez de Castro <aperez@igalia.com>

[WPE] Build failure due to invalid cast of EGLNativeWindowType when targetting 64-bit ARM
@@ -34,10 +34,16 @@
#include "DownloadProxyMessages.h"
#include "Logging.h"
#include "NetworkDataTask.h"
#include "NetworkProcess.h"
#include "NetworkSession.h"
#include "SandboxExtension.h"
#include "WebCoreArgumentCoders.h"
#include <WebCore/NotImplemented.h>

#if PLATFORM(COCOA)
#include "NetworkDataTaskCocoa.h"
#endif

using namespace WebCore;

#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - Download::" fmt, this, ##__VA_ARGS__)
@@ -146,6 +152,41 @@ void Download::cancel()
platformCancelNetworkLoad();
}

#if USE(NETWORK_SESSION)
void Download::didReceiveChallenge(const WebCore::AuthenticationChallenge& challenge, ChallengeCompletionHandler&& completionHandler)
{
if (challenge.protectionSpace().isPasswordBased() && !challenge.proposedCredential().isEmpty() && !challenge.previousFailureCount()) {
completionHandler(AuthenticationChallengeDisposition::UseCredential, challenge.proposedCredential());
return;
}

#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
m_challenge = challenge;
m_challengeCompletionHandler = WTFMove(completionHandler);
send(Messages::DownloadProxy::CanAuthenticateAgainstProtectionSpace(challenge.protectionSpace()));
#else
NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(*this, challenge, WTFMove(completionHandler));
#endif
}

#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void Download::continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate)
{
ASSERT(m_challengeCompletionHandler);
auto completionHandler = std::exchange(m_challengeCompletionHandler, nullptr);
if (!canAuthenticate) {
if (NetworkSession::allowsSpecificHTTPSCertificateForHost(*m_challenge))
completionHandler(AuthenticationChallengeDisposition::UseCredential, serverTrustCredential(*m_challenge));
else
completionHandler(AuthenticationChallengeDisposition::RejectProtectionSpace, { });
return;
}

NetworkProcess::singleton().authenticationManager().didReceiveAuthenticationChallenge(*this, *m_challenge, WTFMove(completionHandler));
}
#endif // USE(PROTECTION_SPACE_AUTH_CALLBACK)
#endif // USE(NETWORK_SESSION)

#if !USE(NETWORK_SESSION)
void Download::didStart()
{
@@ -38,6 +38,8 @@
#include <wtf/RetainPtr.h>

#if USE(NETWORK_SESSION)
#include "NetworkDataTask.h"
#include <WebCore/AuthenticationChallenge.h>
#if PLATFORM(COCOA)
OBJC_CLASS NSURLSessionDownloadTask;
#endif
@@ -101,6 +103,10 @@ class Download : public IPC::MessageSender {

#if USE(NETWORK_SESSION)
void setSandboxExtension(RefPtr<SandboxExtension>&& sandboxExtension) { m_sandboxExtension = WTFMove(sandboxExtension); }
void didReceiveChallenge(const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&);
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void continueCanAuthenticateAgainstProtectionSpace(bool canAuthenticate);
#endif
#else
const WebCore::ResourceRequest& request() const { return m_request; }
void didReceiveAuthenticationChallenge(const WebCore::AuthenticationChallenge&);
@@ -146,6 +152,10 @@ class Download : public IPC::MessageSender {
RetainPtr<NSURLSessionDownloadTask> m_downloadTask;
#endif
PAL::SessionID m_sessionID;
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
std::optional<WebCore::AuthenticationChallenge> m_challenge;
ChallengeCompletionHandler m_challengeCompletionHandler;
#endif
#else // USE(NETWORK_SESSION)
WebCore::ResourceRequest m_request;
String m_responseMIMEType;
@@ -89,10 +89,15 @@ void DownloadManager::dataTaskBecameDownloadTask(DownloadID downloadID, std::uni
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void DownloadManager::continueCanAuthenticateAgainstProtectionSpace(DownloadID downloadID, bool canAuthenticate)
{
auto* pendingDownload = m_pendingDownloads.get(downloadID);
ASSERT(pendingDownload);
if (pendingDownload)
if (auto* pendingDownload = m_pendingDownloads.get(downloadID)) {
pendingDownload->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
return;
}
if (auto* download = m_downloads.get(downloadID)) {
download->continueCanAuthenticateAgainstProtectionSpace(canAuthenticate);
return;
}
ASSERT_NOT_REACHED();
}
#endif

@@ -162,13 +167,17 @@ void DownloadManager::continueDecidePendingDownloadDestination(DownloadID downlo
#endif
}

void DownloadManager::resumeDownload(PAL::SessionID, DownloadID downloadID, const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle& sandboxExtensionHandle)
void DownloadManager::resumeDownload(PAL::SessionID sessionID, DownloadID downloadID, const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle& sandboxExtensionHandle)
{
#if USE(NETWORK_SESSION)
#if USE(NETWORK_SESSION) && !PLATFORM(COCOA)
notImplemented();
#else
#if USE(NETWORK_SESSION)
auto download = std::make_unique<Download>(*this, downloadID, nullptr, sessionID);
#else
// Download::resume() is responsible for setting the Download's resource request.
auto download = std::make_unique<Download>(*this, downloadID, ResourceRequest());
#endif

download->resume(resumeData, path, sandboxExtensionHandle);
ASSERT(!m_downloads.contains(downloadID));
@@ -29,13 +29,36 @@
#if USE(NETWORK_SESSION)

#import "DataReference.h"
#import <WebCore/NotImplemented.h>
#import "NetworkSessionCocoa.h"
#import "SessionTracker.h"
#import <pal/spi/cf/CFNetworkSPI.h>

namespace WebKit {

void Download::resume(const IPC::DataReference& resumeData, const String& path, const SandboxExtension::Handle& sandboxExtensionHandle)
{
notImplemented();
m_sandboxExtension = SandboxExtension::create(sandboxExtensionHandle);
if (m_sandboxExtension)
m_sandboxExtension->consume();

auto* networkSession = SessionTracker::networkSession(m_sessionID);
if (!networkSession) {
WTFLogAlways("Could not find network session with given session ID");
return;
}
auto& cocoaSession = static_cast<NetworkSessionCocoa&>(*networkSession);
auto nsData = adoptNS([[NSData alloc] initWithBytes:resumeData.data() length:resumeData.size()]);

// FIXME: This is a temporary workaround for <rdar://problem/34745171>.
NSMutableDictionary *dictionary = [NSPropertyListSerialization propertyListWithData:nsData.get() options:NSPropertyListImmutable format:0 error:nullptr];
[dictionary setObject:static_cast<NSString*>(path) forKey:@"NSURLSessionResumeInfoLocalPath"];
NSData *updatedData = [NSPropertyListSerialization dataWithPropertyList:dictionary format:NSPropertyListXMLFormat_v1_0 options:0 error:nullptr];

m_downloadTask = cocoaSession.downloadTaskWithResumeData(updatedData);
cocoaSession.addDownloadID(m_downloadTask.get().taskIdentifier, m_downloadID);
m_downloadTask.get()._pathToDownloadTaskFile = path;

[m_downloadTask resume];
}

void Download::platformCancelNetworkLoad()
@@ -27,7 +27,9 @@

#if USE(NETWORK_SESSION)

OBJC_CLASS NSData;
OBJC_CLASS NSURLSession;
OBJC_CLASS NSURLSessionDownloadTask;
OBJC_CLASS NSOperationQueue;
OBJC_CLASS WKNetworkSessionDelegate;

@@ -58,6 +60,7 @@ class NetworkSessionCocoa final : public NetworkSession {
#endif

NetworkDataTaskCocoa* dataTaskForIdentifier(NetworkDataTaskCocoa::TaskIdentifier, WebCore::StoredCredentialsPolicy);
NSURLSessionDownloadTask* downloadTaskWithResumeData(NSData*);

void addDownloadID(NetworkDataTaskCocoa::TaskIdentifier, DownloadID);
DownloadID downloadID(NetworkDataTaskCocoa::TaskIdentifier);
@@ -278,6 +278,21 @@ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didRece
};
networkDataTask->didReceiveChallenge(challenge, WTFMove(challengeCompletionHandler));
} else {
auto downloadID = _session->downloadID(task.taskIdentifier);
if (downloadID.downloadID()) {
if (auto* download = WebKit::NetworkProcess::singleton().downloadManager().download(downloadID)) {
// Received an authentication challenge for a download being resumed.
WebCore::AuthenticationChallenge authenticationChallenge { challenge };
auto completionHandlerCopy = Block_copy(completionHandler);
auto sessionID = _session->sessionID();
auto challengeCompletionHandler = [completionHandlerCopy, sessionID, authenticationChallenge](WebKit::AuthenticationChallengeDisposition disposition, const WebCore::Credential& credential) {
completionHandlerCopy(toNSURLSessionAuthChallengeDisposition(disposition), credential.nsCredential());
Block_release(completionHandlerCopy);
};
download->didReceiveChallenge(challenge, WTFMove(challengeCompletionHandler));
return;
}
}
LOG(NetworkSession, "%llu didReceiveChallenge completionHandler (cancel)", taskIdentifier);
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
@@ -671,6 +686,11 @@ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)data
return m_dataTaskMapWithoutState.get(taskIdentifier);
}

NSURLSessionDownloadTask* NetworkSessionCocoa::downloadTaskWithResumeData(NSData* resumeData)
{
return [m_sessionWithCredentialStorage downloadTaskWithResumeData:resumeData];
}

void NetworkSessionCocoa::addDownloadID(NetworkDataTaskCocoa::TaskIdentifier taskIdentifier, DownloadID downloadID)
{
#ifndef NDEBUG
@@ -144,7 +144,7 @@ void AuthenticationManager::didReceiveAuthenticationChallenge(uint64_t pageID, u
m_process.send(Messages::NetworkProcessProxy::DidReceiveAuthenticationChallenge(pageID, frameID, authenticationChallenge, challengeID));
}

void AuthenticationManager::didReceiveAuthenticationChallenge(PendingDownload& pendingDownload, const WebCore::AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler)
void AuthenticationManager::didReceiveAuthenticationChallenge(IPC::MessageSender& download, const WebCore::AuthenticationChallenge& authenticationChallenge, ChallengeCompletionHandler&& completionHandler)
{
uint64_t dummyPageID = 0;
uint64_t challengeID = addChallengeToChallengeMap({ dummyPageID, authenticationChallenge, WTFMove(completionHandler) });
@@ -153,7 +153,7 @@ void AuthenticationManager::didReceiveAuthenticationChallenge(PendingDownload& p
if (shouldCoalesceChallenge(dummyPageID, challengeID, authenticationChallenge))
return;

pendingDownload.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID));
download.send(Messages::DownloadProxy::DidReceiveAuthenticationChallenge(authenticationChallenge, challengeID));
}
#endif

@@ -34,6 +34,10 @@
#include <wtf/Forward.h>
#include <wtf/HashMap.h>

namespace IPC {
class MessageSender;
}

namespace WebCore {
class AuthenticationChallenge;
class CertificateInfo;
@@ -45,7 +49,6 @@ namespace WebKit {
class ChildProcess;
class Download;
class DownloadID;
class PendingDownload;
class WebFrame;

enum class AuthenticationChallengeDisposition {
@@ -65,7 +68,7 @@ class AuthenticationManager : public WebProcessSupplement, public NetworkProcess

#if USE(NETWORK_SESSION)
void didReceiveAuthenticationChallenge(uint64_t pageID, uint64_t frameID, const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&);
void didReceiveAuthenticationChallenge(PendingDownload&, const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&);
void didReceiveAuthenticationChallenge(IPC::MessageSender& download, const WebCore::AuthenticationChallenge&, ChallengeCompletionHandler&&);
#if USE(PROTECTION_SPACE_AUTH_CALLBACK)
void continueCanAuthenticateAgainstProtectionSpace(DownloadID, bool canAuthenticate);
#endif

0 comments on commit af7926f

Please sign in to comment.