将 NSURLProtocol 与 NSURLSession 一起使用

Using NSURLProtocol with NSURLSession

我的应用程序使用 NSURLConnection 与服务器通信。我们使用 https 进行通信。为了在一个地方处理来自所有请求的身份验证,我使用了 NSURLProtocol 并在那个 class 中处理了委托中的身份验证。现在我决定使用 NSURLSession 而不是 NSURLConnection。我正在尝试让 NSURLProtocolNSURLSession 一起工作 我创建了一个任务并使用 NSURLProtocol by

NSMutableURLRequest *sampleRequest = [[NSMutableURLRequest alloc]initWithURL:someURL];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
configuration.protocolClasses = @[[CustomHTTPSProtocol class]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURLSessionDataTask *task = [session dataTaskWithRequest:checkInInfoRequest];
[task resume];

CustomHTTPSProtocol 这是我的 NSURLProtocol class 看起来像这样

static NSString * const CustomHTTPSProtocolHandledKey = @"CustomHTTPSProtocolHandledKey";

@interface CustomHTTPSProtocol () <NSURLSessionDataDelegate,NSURLSessionTaskDelegate,NSURLSessionDelegate>

@property (nonatomic, strong) NSURLSessionDataTask *connection;
@property (nonatomic, strong) NSMutableData *mutableData;

@end

@implementation CustomHTTPSProtocol

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    if ([NSURLProtocol propertyForKey:CustomHTTPSProtocolHandledKey inRequest:request]) {
        return NO;
    }
    return [[[[request URL]scheme]lowercaseString]isEqualToString:@"https"];
}

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    return request;
}

- (void) startLoading {
    NSMutableURLRequest *newRequest = [self.request mutableCopy];
    [NSURLProtocol setProperty:@YES forKey:CustomHTTPSProtocolHandledKey inRequest:newRequest];

    NSURLSession*session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
    self.connection = [session dataTaskWithRequest:newRequest];
    [self.connection resume];
    self.mutableData = [[NSMutableData alloc] init];
}

- (void) stopLoading {
    [self.connection cancel];
    self.mutableData = nil;
}

-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
    NSLog(@"challenge..%@",challenge.protectionSpace.authenticationMethod);
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        [challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
    }
    else {
        [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
    }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [self.client URLProtocol:self didLoadData:data];
    NSLog(@"data ...%@  ",data); //handle data here
    [self.mutableData appendData:data];
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (!error) {
        [self.client URLProtocolDidFinishLoading:self];
    }
    else {
        NSLog(@"error ...%@  ",error);
        [self.client URLProtocol:self didFailWithError:error];
    }
}

@end

调用开始加载并完成身份验证质询,但之后立即调用停止加载。

Error code -999 "Cancelled" is returned after some time. didReceiveData is not called.

Note:NSURLProtocol and the Authentication Process worked fine with NSURLConnection.

我错过了什么??我的问题是

  1. Registering [NSURLProtocol registerClass:[CustomHTTPSProtocol class]]; worked fine with NSURLConnection but how to Resister for NSURLProtocol Globally with NSURLSession ?.

  2. Why are the requests getting failed in NSURLProtocol(same URL and logic worked withURLConnection) with URLSession and how to get NSURLProtocol working with URLSession ?.

请帮助我,如果您需要更多详细信息,请告诉我。

我模糊的记忆是 NSURLSession 自动使用任何全局注册的协议。所以你不需要重新注册,但这样做也不会有什么坏处。

我想知道 URL 请求是否被复制,在这种情况下,您的自定义标记可能不起作用,并且您可能会看到无限递归,直到达到某个内部限制。您是否尝试过设置请求 header?

使用 NSUrlsession 注册自定义 NSURLProtocol 与使用 NSURLConnection 的方式相同。

NSURLSession Api 的(NSURLSessionDataTask 和其他任务 类)在与自定义 NSUrlProtocol 一起使用时无法正确处理 HTTP 身份验证挑战。 这在 iOS 7.x 上按预期工作,但在 iOS 8.x.Raised 苹果雷达中被破坏,我的雷达被关闭说它是另一个雷达的副本,我仍然看到原来的雷达还是开着的。所以我相信这个问题到今天还没有被苹果解决。

希望我的回答还不算太晚:)

我知道这已经过时了,但您遇到的问题出在您的 didReceiveChallenge 方法中。您正在通过调用

结束该方法
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];

[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];

您需要做的是使用完成处理程序发送您的结果。它看起来像这样:

completionHandler(.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust)

completionHandler(.PerformDefaultHandling, nil)

这些在 Swift 2.0 中,但应该很好地转换为 Obj-C。