Cookies are not handled by NSURLSession's URLProtocol client

Originator:0x00001eaf
Number:rdar://34329423 Date Originated:September 8 2017
Status:Open Resolved:
Product:iOS + SDK Product Version:iOS 10.3, iOS 9.3, macOS 10.12
Classification:Serious Bug Reproducible:Always
 
Area:
CFNetwork Framework

Summary:

URLProtocol client provided by NSURLSession to the NSURLProtocol instance is not handling cookies in it's
-URLProtocol:wasRedirectedToRequest:redirectResponse:
-URLProtocol:didReceiveResponse:cacheStoragePolicy:
methods when it receives NSHTTPURLResponse instance as a response.

Instead the responsibility of handling cookies is shifted to the NSURLProtocol instance.
When CFNetworks HTTP protocol implementation is instantiated with a task originating from the top level session it is able to resolve session's cookie storage properly (via private API) and add cookies to it.
But when it is instantiated with task originating from different session (like in case of CustomHTTPProtocol), cookies are being added only to the nested session's cookie storage.
And there is no way to work this around and explicitly add cookies to the relevant  session's storage as NSURLProtocol instances have no public way to find out which session they are running in, as -[NSURLSessionTask session] property is private.

So if NSURLSession's protocol client was hadnling cookies in  
-URLProtocol:wasRedirectedToRequest:redirectResponse:
-URLProtocol:didReceiveResponse:cacheStoragePolicy:
everything would just work as expected and no work-around would be necessary.
And if -[NSURLSessionTask session] was exposed, at least work-around would be possible.
But currently there is no way of implementing proper cookie support in custom HTTP protocols. 

Steps to Reproduce:

Add CustomHTTPProtocol, CacheStoragePolicy, CanonicalRequest, QNURLSessionDemux from CustomHTTPProcol sample (https://developer.apple.com/library/content/samplecode/CustomHTTPProtocol/Introduction/Intro.html) to your target.

Then add and execute the following code:

#import "CustomHTTPProtocol.h"

BOOL isCookiePresent(NSHTTPCookieStorage *storage, NSURL *url, NSString *cookieName) {
    NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSHTTPCookie *cookie, id bindings) {
        return [cookie.name isEqualToString:cookieName];
    }];
    return [[storage cookiesForURL:url] filteredArrayUsingPredicate:predicate].count != 0;
}

void testCookies(NSArray *protocolClasses) {
    NSString *cookieName = @"sample_cookie";
    NSURL *setCookiesURL = [NSURL URLWithString:@"https://httpbin.org/cookies/set?sample_cookie=sample_value"];
    NSURL *referenceURL = [NSURL URLWithString:@"https://httpbin.org"];

    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
    configuration.protocolClasses = protocolClasses;
    configuration.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicyAlways; // just to be sure
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    [[session dataTaskWithURL:setCookiesURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"Request succeeded: %d", error == nil);
        NSLog(@"Is cookie present: %d", isCookiePresent(configuration.HTTPCookieStorage, referenceURL, cookieName));
        dispatch_semaphore_signal(semaphore);
    }] resume];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        testCookies(nil);
        NSLog(@"---");
        testCookies(@[[CustomHTTPProtocol class]]);
    }
    return EXIT_SUCCESS;
}

Note: it is not required to be in main(), it may be in a unit test or whatnot…

Expected Results:

Request succeeded: 1
Is cookie present: 1
---
Request succeeded: 1
Is cookie present: 1

Actual Results:

Request succeeded: 1
Is cookie present: 1
---
Request succeeded: 1
Is cookie present: 0

Version/Build:
Tested on iOS 10.3, iOS 9.3, macOS 10.12
Note: on iOS 11 request fails to complete with CustomHTTPProtocol…

Configuration:

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!