4

我正在实现一个自定义NSURLProtocol,并且在内部希望将NSURLSession数据任务与内部网络一起使用,而不是NSURLConnection.

我遇到了一个有趣的问题,想知道NSURLSession/的挑战处理程序的内部实现NSURLSessionTask

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                            didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                              completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

在这里,我基本上提供了两种不同的挑战处理程序,一个是completionHandler块,它提供了处理挑战的所有必要信息,但也有一些遗留NSURLAuthenticationChallenge.client的方法与completionHandler的方法与信息选项

由于我正在开发一个协议,并且想将某些身份验证挑战向上传递到 URL 加载系统以供调用 API 实现,因此我需要使用NSURLSession客户端方法:

- (void)URLProtocol:(NSURLProtocol *)protocol didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;

我的问题是completionHandlerand的内部实现是否NSURLAuthenticationChallenge.client相同,如果是,我可以跳过在委托方法中调用完成处理程序,期望 URL 加载系统会调用适当的NSURLAuthenticationChallenge.client方法吗?

4

1 回答 1

7

要回答我自己的问题,答案是否定的。此外,Apple 提供的挑战发送器没有实现整个NSURLAuthenticationChallengeSender协议,因此当客户端尝试响应挑战时会崩溃:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFURLSessionConnection performDefaultHandlingForAuthenticationChallenge:]: unrecognized selector sent to instance 0x7ff06d958410'

我的解决方案是创建一个包装器:

@interface CPURLSessionChallengeSender : NSObject <NSURLAuthenticationChallengeSender>

- (instancetype)initWithSessionCompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler;

@end

@implementation CPURLSessionChallengeSender
{
    void (^_sessionCompletionHandler)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential);
}

- (instancetype)initWithSessionCompletionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
    self = [super init];

    if(self)
    {
        _sessionCompletionHandler = [completionHandler copy];
    }

    return self;
}

- (void)useCredential:(NSURLCredential *)credential forAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}

- (void)continueWithoutCredentialForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeUseCredential, nil);
}

- (void)cancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}

- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
}

- (void)rejectProtectionSpaceAndContinueWithChallenge:(NSURLAuthenticationChallenge *)challenge
{
    _sessionCompletionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
}

@end

我用一个新的挑战对象替换了挑战对象,使用我包装的发件人:

NSURLAuthenticationChallenge* challengeWrapper = [[NSURLAuthenticationChallenge alloc] initWithAuthenticationChallenge:challenge sender:[[CPURLSessionChallengeSender alloc] initWithSessionCompletionHandler:completionHandler]];
[self.client URLProtocol:self didReceiveAuthenticationChallenge:challengeWrapper];
于 2014-12-22T14:09:57.210 回答