Обработчик завершения проверки подлинности NSURLSessionTask и клиент NSURLAuthenticationChallenge

Я реализую пользовательский 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;

Мой вопрос заключается в том, одинакова ли внутренняя реализация completionHandler и NSURLAuthenticationChallenge.client, и если да, то могу ли я пропустить вызов обработчика завершения в методе делегата, ожидая, что система загрузки URL вызовет соответствующий метод NSURLAuthenticationChallenge.client?


person Leo Natan    schedule 22.12.2014    source источник


Ответы (1)


Чтобы ответить на мой собственный вопрос, ответ - нет. Более того, предоставленный 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];
person Leo Natan    schedule 22.12.2014
comment
Предоставленный отправитель вызова? Вы имеете в виду базовый класс NSURLProtocol? - person dgatwood; 08.09.2016
comment
Каждый объект вызова имеет свойство отправителя, однако оно не работает для протоколов. - person Leo Natan; 08.09.2016
comment
С точки зрения времени выполнения не может быть никакой разницы между передачей объекта challengeWrapper и реализацией точно таких же методов в вашем подклассе NSURLProtocol и передачей self в качестве отправителя вызова, что, как говорится в документации, вы должны делать. Я не уверен, почему вы вообще должны реализовывать эти методы — это, вероятно, ошибка, и вы должны ее зарегистрировать, — но, как правило, вы должны передавать свой подкласс NSURLProtocol в качестве отправителя, а не другой объект. - person dgatwood; 08.09.2016
comment
Это ошибка, и я зарегистрировал ее более года назад. Вы упускаете проблему. NSURLAuthenticationChallenge имеет sender, который клиенты должны вызывать с действиями по решению проблемы. Однако объект, предоставленный Apple, является частичным и не реализует весь протокол NSURLAuthenticationChallengeSender. Таким образом, приложение будет аварийно завершать работу при попытке вызова методов. Эта оболочка реализует эти методы и вызывает обработчик завершения, предоставляемый Apple, который действительно работает. - person Leo Natan; 08.09.2016
comment
Авария: -[__NSCFURLLocalSessionConnection performDefaultHandlingForAuthenticationChallenge:]: unrecognized selector sent to instance. Предоставленный Apple класс __NSCFURLLocalSessionConnection неправильно реализует API. - person Leo Natan; 08.09.2016
comment
Нет, нет, я не говорю, что вы не должны создавать новый вызов. Я говорю, что вы должны создать новый вызов, но передать свой экземпляр подкласса NSURLProtocol в качестве отправителя вызова, а не другой класс. - person dgatwood; 08.09.2016
comment
С эстетической точки зрения мне нравится элегантность автономного экземпляра, работа которого состоит в том, чтобы просто обрабатывать вызов аутентификации. - person Leo Natan; 08.09.2016
comment
Большое спасибо ... очень полезно ... тем более, что ни документы Apple, ни их пример кода CustomHTTPProtocol недостаточно ясны. - person Ravi; 29.05.2018
comment
Спасибо большое, сегодня была точно такая же проблема. - person Yousef Hamza; 30.03.2021