Changeset 255533 in webkit


Ignore:
Timestamp:
Jan 31, 2020 4:47:57 PM (4 years ago)
Author:
david_quesada@apple.com
Message:

REGRESSION(r252185): NetworkSessionCocoa cancels downloads that receive authentication challenges
https://bugs.webkit.org/show_bug.cgi?id=206984
rdar://problem/58999654

Reviewed by Brady Eidson.

Source/WebKit:

r252185 changed the early return in WKNetworkSessionDelegate's -...task:didReceiveChallenge:... method
from "cancel the task and return early if self has no _session" to "cancel the task and return early
if we can't determine the network session for the given data task". When this method is called for
an NSURLSessionDownloadTask, this early return is hit because there is no NetworkDataTaskCocoa
for an active download. As a result, the download is canceled when it might have otherwise been able
to proceed.

Fix this by adding a code path to fetch the NetworkSession associated with the Download when an
NSURLSessionDownloadTask receives an authentication challenge. This ensures we can actually handle
the challenge appropriately and not just cancel the task.

  • NetworkProcess/Downloads/Download.h:

(WebKit::Download::sessionID const):

Expose the session ID so we can use it to look up the NetworkSession for a Download.

  • NetworkProcess/cocoa/NetworkSessionCocoa.mm:

(-[WKNetworkSessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):

Remove an unnecessary redeclaration of networkDataTask, and also an unneeded assertion that
networkDataTask != nullptr. Even if this is the case, the code that eventually handles this
task will null check it and handle the challenge as a websocket task or download task
based on the taskIdentifier.

Tools:

Add an API test for a resumed download that receives an authentication challenge. The download
delegate should be asked to handle the challenge, and the download should be able to finish.

  • TestWebKitAPI/Tests/WebKitCocoa/Download.mm:

(-[DownloadCancelingDelegate _download:decideDestinationWithSuggestedFilename:completionHandler:]):
(-[DownloadCancelingDelegate _download:didReceiveData:]):
(-[DownloadCancelingDelegate _downloadDidCancel:]):
(-[AuthenticationChallengeHandlingDelegate _download:didReceiveAuthenticationChallenge:completionHandler:]):
(-[AuthenticationChallengeHandlingDelegate _downloadDidFinish:]):
(TEST):

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r255532 r255533  
     12020-01-31  David Quesada  <david_quesada@apple.com>
     2
     3        REGRESSION(r252185): NetworkSessionCocoa cancels downloads that receive authentication challenges
     4        https://bugs.webkit.org/show_bug.cgi?id=206984
     5        rdar://problem/58999654
     6
     7        Reviewed by Brady Eidson.
     8
     9        r252185 changed the early return in WKNetworkSessionDelegate's -...task:didReceiveChallenge:... method
     10        from "cancel the task and return early if self has no _session" to "cancel the task and return early
     11        if we can't determine the network session for the given data task". When this method is called for
     12        an NSURLSessionDownloadTask, this early return is hit because there is no NetworkDataTaskCocoa
     13        for an active download. As a result, the download is canceled when it might have otherwise been able
     14        to proceed.
     15
     16        Fix this by adding a code path to fetch the NetworkSession associated with the Download when an
     17        NSURLSessionDownloadTask receives an authentication challenge. This ensures we can actually handle
     18        the challenge appropriately and not just cancel the task.
     19
     20        * NetworkProcess/Downloads/Download.h:
     21        (WebKit::Download::sessionID const):
     22            Expose the session ID so we can use it to look up the NetworkSession for a Download.
     23        * NetworkProcess/cocoa/NetworkSessionCocoa.mm:
     24        (-[WKNetworkSessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
     25            Remove an unnecessary redeclaration of networkDataTask, and also an unneeded assertion that
     26            networkDataTask != nullptr. Even if this is the case, the code that eventually handles this
     27            task will null check it and handle the challenge as a websocket task or download task
     28            based on the taskIdentifier.
     29
    1302020-01-31  Wenson Hsieh  <wenson_hsieh@apple.com>
    231
  • trunk/Source/WebKit/NetworkProcess/Downloads/Download.h

    r250521 r255533  
    8383
    8484    DownloadID downloadID() const { return m_downloadID; }
     85    PAL::SessionID sessionID() const { return m_sessionID; }
    8586    const String& suggestedName() const { return m_suggestedName; }
    8687
  • trunk/Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm

    r255527 r255533  
    614614    auto* networkDataTask = [self existingTask:task];
    615615    auto* sessionCocoa = networkDataTask ? static_cast<NetworkSessionCocoa*>(networkDataTask->networkSession()) : nullptr;
     616    if (!networkDataTask) {
     617        ASSERT(!sessionCocoa);
     618        auto downloadID = _sessionWrapper->downloadMap.get(task.taskIdentifier);
     619        auto download = downloadID.downloadID() ? _session->networkProcess().downloadManager().download(downloadID) : nil;
     620        sessionCocoa = download ? static_cast<NetworkSessionCocoa*>(_session->networkProcess().networkSession(download->sessionID())) : nil;
     621    }
    616622    if (!sessionCocoa || [task state] == NSURLSessionTaskStateCanceling) {
    617623        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
     
    652658        if (sessionCocoa->fastServerTrustEvaluationEnabled() && negotiatedLegacyTLS == NegotiatedLegacyTLS::No) {
    653659#if HAVE(CFNETWORK_NSURLSESSION_STRICTRUSTEVALUATE)
    654             auto* networkDataTask = [self existingTask:task];
    655             ASSERT(networkDataTask);
    656660            auto decisionHandler = makeBlockPtr([weakSelf = WeakObjCPtr<WKNetworkSessionDelegate>(self), sessionCocoa = makeWeakPtr(sessionCocoa), completionHandler = makeBlockPtr(completionHandler), taskIdentifier, networkDataTask = makeRefPtr(networkDataTask), negotiatedLegacyTLS](NSURLAuthenticationChallenge *challenge, OSStatus trustResult) mutable {
    657661                auto strongSelf = weakSelf.get();
  • trunk/Tools/ChangeLog

    r255532 r255533  
     12020-01-31  David Quesada  <david_quesada@apple.com>
     2
     3        REGRESSION(r252185): NetworkSessionCocoa cancels downloads that receive authentication challenges
     4        https://bugs.webkit.org/show_bug.cgi?id=206984
     5        rdar://problem/58999654
     6
     7        Reviewed by Brady Eidson.
     8
     9        Add an API test for a resumed download that receives an authentication challenge. The download
     10        delegate should be asked to handle the challenge, and the download should be able to finish.
     11
     12        * TestWebKitAPI/Tests/WebKitCocoa/Download.mm:
     13        (-[DownloadCancelingDelegate _download:decideDestinationWithSuggestedFilename:completionHandler:]):
     14        (-[DownloadCancelingDelegate _download:didReceiveData:]):
     15        (-[DownloadCancelingDelegate _downloadDidCancel:]):
     16        (-[AuthenticationChallengeHandlingDelegate _download:didReceiveAuthenticationChallenge:completionHandler:]):
     17        (-[AuthenticationChallengeHandlingDelegate _downloadDidFinish:]):
     18        (TEST):
     19
    1202020-01-31  Wenson Hsieh  <wenson_hsieh@apple.com>
    221
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm

    r250521 r255533  
    10301030}
    10311031
     1032@interface DownloadCancelingDelegate : NSObject <_WKDownloadDelegate>
     1033@property (readonly) RetainPtr<NSData> resumeData;
     1034@property (readonly) RetainPtr<NSString> path;
     1035@end
     1036
     1037@implementation DownloadCancelingDelegate
     1038
     1039- (void)_download:(_WKDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename completionHandler:(void (^)(BOOL allowOverwrite, NSString *destination))completionHandler
     1040{
     1041    FileSystem::PlatformFileHandle fileHandle;
     1042    _path = FileSystem::openTemporaryFile("TestWebKitAPI", fileHandle);
     1043    EXPECT_TRUE(fileHandle != FileSystem::invalidPlatformFileHandle);
     1044    FileSystem::closeFile(fileHandle);
     1045    completionHandler(YES, _path.get());
     1046}
     1047
     1048- (void)_download:(_WKDownload *)download didReceiveData:(uint64_t)length
     1049{
     1050    EXPECT_EQ(length, 5000ULL);
     1051    [download cancel];
     1052}
     1053
     1054- (void)_downloadDidCancel:(_WKDownload *)download
     1055{
     1056    EXPECT_NOT_NULL(download.resumeData);
     1057    _resumeData = download.resumeData;
     1058    isDone = true;
     1059}
     1060
     1061@end
     1062
     1063@interface AuthenticationChallengeHandlingDelegate : NSObject <_WKDownloadDelegate>
     1064@end
     1065
     1066@implementation AuthenticationChallengeHandlingDelegate {
     1067    bool _didReceiveAuthenticationChallenge;
     1068}
     1069
     1070- (void)_download:(_WKDownload *)download didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential*))completionHandler
     1071{
     1072    _didReceiveAuthenticationChallenge = true;
     1073    completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
     1074}
     1075
     1076- (void)_downloadDidFinish:(_WKDownload *)download
     1077{
     1078    EXPECT_TRUE(_didReceiveAuthenticationChallenge);
     1079    isDone = true;
     1080}
     1081
     1082@end
     1083
     1084TEST(_WKDownload, ResumedDownloadCanHandleAuthenticationChallenge)
     1085{
     1086    using namespace TestWebKitAPI;
     1087
     1088    std::atomic<bool> receivedFirstConnection { false };
     1089
     1090    TCPServer server([&](int socket) {
     1091        if (!receivedFirstConnection.exchange(true)) {
     1092            TCPServer::read(socket);
     1093
     1094            const char* responseHeader =
     1095            "HTTP/1.1 200 OK\r\n"
     1096            "ETag: test\r\n"
     1097            "Content-Length: 10000\r\n\r\n";
     1098            TCPServer::write(socket, responseHeader, strlen(responseHeader));
     1099
     1100            char data[5000];
     1101            memset(data, 0, 5000);
     1102            TCPServer::write(socket, data, 5000);
     1103
     1104            // Wait for the client to cancel the download before closing the connection.
     1105            Util::run(&isDone);
     1106        } else {
     1107            TCPServer::read(socket);
     1108            const char* challengeHeader =
     1109            "HTTP/1.1 401 Unauthorized\r\n"
     1110            "Date: Sat, 23 Mar 2019 06:29:01 GMT\r\n"
     1111            "Content-Length: 0\r\n"
     1112            "WWW-Authenticate: Basic realm=\"testrealm\"\r\n\r\n";
     1113            TCPServer::write(socket, challengeHeader, strlen(challengeHeader));
     1114
     1115            TCPServer::read(socket);
     1116
     1117            const char* responseHeader =
     1118            "HTTP/1.1 206 Partial Content\r\n"
     1119            "ETag: test\r\n"
     1120            "Content-Range: bytes 5000-9999/10000\r\n"
     1121            "Content-Length: 5000\r\n\r\n";
     1122            TCPServer::write(socket, responseHeader, strlen(responseHeader));
     1123
     1124            char data[5000];
     1125            memset(data, 1, 5000);
     1126            TCPServer::write(socket, data, 5000);
     1127        }
     1128    }, 2);
     1129
     1130    auto processPool = adoptNS([[WKProcessPool alloc] init]);
     1131    auto websiteDataStore = adoptNS([WKWebsiteDataStore defaultDataStore]);
     1132
     1133    auto delegate1 = adoptNS([[DownloadCancelingDelegate alloc] init]);
     1134    [processPool _setDownloadDelegate:delegate1.get()];
     1135
     1136    isDone = false;
     1137    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]];
     1138    [processPool _downloadURLRequest:request websiteDataStore:websiteDataStore.get() originatingWebView:nil];
     1139
     1140    Util::run(&isDone);
     1141
     1142    isDone = false;
     1143    auto delegate2 = adoptNS([[AuthenticationChallengeHandlingDelegate alloc] init]);
     1144    [processPool _setDownloadDelegate:delegate2.get()];
     1145    [processPool _resumeDownloadFromData:[delegate1 resumeData].get() websiteDataStore:websiteDataStore.get() path:[delegate1 path].get() originatingWebView:nil];
     1146
     1147    Util::run(&isDone);
     1148}
     1149
    10321150#endif // PLATFORM(MAC) || PLATFORM(IOS)
Note: See TracChangeset for help on using the changeset viewer.