Жизненный цикл NSURLSession и базовое разрешение

Я не могу последовательно читать ответ с сервера, если я использую код ниже.

Заголовок:

#import <Foundation/Foundation.h> @interface TestHttpClient : NSObject<NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate> -(void)POST:(NSString*) relativePath payLoad:(NSData*)payLoad; @end 

Реализация:

 #import "TestHttpClient.h" @implementation TestHttpClient -(void)POST:(NSString*)relativePath payLoad:(NSData*)payLoad { NSURL* url = [NSURL URLWithString:@"http://apps01.ditat.net/mobile/batch"]; // Set URL credentials and save to storage NSURLCredential *credential = [NSURLCredential credentialWithUser:@"BadUser" password:@"BadPassword" persistence: NSURLCredentialPersistencePermanent]; NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:[url host] port:443 protocol:[url scheme] realm:@"Ditat mobile services endpoint" authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace]; // Configure session NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration]; sessionConfig.timeoutIntervalForRequest = 30.0; sessionConfig.timeoutIntervalForResource = 60.0; sessionConfig.HTTPMaximumConnectionsPerHost = 1; sessionConfig.URLCredentialStorage = [NSURLCredentialStorage sharedCredentialStorage]; // Should this line be here?? NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; // Create request object with parameters NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60.0]; // Set header data [request setHTTPMethod:@"POST"]; [request setValue:@"application/x-protobuf" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"Version 1.0" forHTTPHeaderField:@"User-Agent"]; [request setValue:@"Demo" forHTTPHeaderField:@"AccountId"]; [request setValue:@"1234-5678" forHTTPHeaderField:@"DeviceSerialNumber"]; [request setValue:@"iOS 7.1" forHTTPHeaderField:@"OSVersion"]; [request setHTTPBody:payLoad]; // Call session to post data to server?? NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request]; [downloadTask resume]; } -(void)invokeDelegateWithResponse:(NSHTTPURLResponse *)response fileLocation:(NSURL*)location { NSLog(@"HttpClient.invokeDelegateWithResponse - code %ld", (long)[response statusCode]); } #pragma mark - NSURLSessionDownloadDelegate - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"NSURLSessionDownloadDelegate.didFinishDownloadingToURL"); [self invokeDelegateWithResponse:(NSHTTPURLResponse*)[downloadTask response] fileLocation:location]; [session invalidateAndCancel]; } // Implemented as blank to avoid compiler warning -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { } // Implemented as blank to avoid compiler warning -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes { } 

Может быть вызван из любого VC (например, код места под действием кнопки)

 -(IBAction)buttonTouchUp:(UIButton *)sender { TestHttpClient *client = [[TestHttpClient alloc] init]; [client POST:@"" payLoad:nil]; return; } 

Если вы запустите программу и назовете этот код – она ​​будет показана в завершении NSLog с 401. Вторая попытка – не будет работать. Или может работать, если подождать немного. Но он не будет отправлять запросы сервера при нажатии кнопки.

NSURLSession каким-то образом «запоминает» неудачные попытки и ничего не вернет? Почему это поведение? Я хочу видеть 2 сообщения NSLog каждый раз, когда я нажимаю кнопку.

TL; DR; В вашем примере вы неправильно обрабатываете аутентификацию.

Это происходит, когда клиент iOS или MacOS встречает URL-адрес, требующий аутентификации:

  1. Клиент запрашивает ресурс с сервера GET www.example.com/protected

  2. Ответ сервера для этого запроса имеет код состояния 401 и включает заголовок WWW-Authenticate. Это говорит клиенту, что это защищенный ресурс, и указывает, какой метод аутентификации использовать для доступа к ресурсу. В iOS и MacOS это «проверка подлинности», на которую отвечает делегат. Заголовок WWW-Authenticate специально упоминается в документации, чтобы выделить это .

    • Обычно на iOS и MacOS, если делегат не предоставлен или не обрабатывает проблемы с проверкой подлинности, система загрузки URL будет пытаться найти соответствующие учетные данные для этого ресурса и типа проверки подлинности, посмотрев в NSURLCredentialStorage. Он ищет соответствующие учетные данные, которые были сохранены как значение по умолчанию.

    • Если предоставлен делегат, реализующий аутентификацию, этот делегат должен предоставить учетные данные для этого ресурса.

  3. Когда система имеет учетные данные для проверки подлинности, запрос снова выполняется с учетными данными.

    GET http://www.example.com/protected Авторизация: Basic blablahaala

Это объясняет поведение, которое вы видите в Чарльзе, и является правильным поведением в соответствии с различными спецификациями HTTP.

Очевидно, что если вы не хотите внедрять делегат для своего подключения, у вас есть возможность поместить учетные данные для ресурса, к которому вы обращаетесь в NSURLCredentialStorage. Система будет использовать это и не потребует от вас выполнения делегата для учетных данных.

Создайте NSURLCredential:

 credential = [NSURLCredential credentialWithUser:@"some user" password:@"clever password" persistence: NSURLCredentialPersistencePermanent]; 

NSURLCredentialPersistencePermanent сообщит NSURLCredentialStorage чтобы он постоянно NSURLCredentialStorage в NSURLCredentialStorage ключей. Существуют и другие возможные значения, которые вы можете использовать, например NSURLCredentialPersistenceForSession . Они описаны в документации. , Вам следует избегать использования NSURLCredentialPersistencePermanent с учетными данными, которые не были проверены, использовать сеанс или нет до тех пор, пока учетные данные не будут проверены. Вероятно, вы видели проекты с использованием «KeychainWrapper» или прямой доступ к API Keychain для сохранения имен и паролей в Интернете – это не самый предпочтительный способ сделать это, NSURLCredentialStorage .

Создайте NSURLProtectionSpace с правильным хостом, портом, протоколом, областью и методом проверки подлинности:

 protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:[url host] port:443 protocol:[url scheme] realm:@"Protected Area" authenticationMethod:NSURLAuthenticationMethodHTTPBasic]; 

Обратите внимание, что [[url port] integerValue] не будет предоставлять значения по умолчанию для HTTP (80) или HTTPS (443). Вы должны предоставить их. Область ДОЛЖНА соответствовать тому, что предоставляет сервер.

Наконец, поместите его в NSURLCredentialStorage :

 [[NSURLCredentialStorage sharedCredentialStorage] setDefaultCredential:credential forProtectionSpace:protectionSpace]; 

Это позволит системе загрузки URL-адресов использовать эти учетные данные с этого момента. По сути, этот же процесс также можно использовать для справки доверия сервера SSL / TLS.

В вашем вопросе вы обрабатываете доверие сервера, но не NSURLAuthenticationMethodHTTPBasic . Когда ваше приложение получает вызов аутентификации для HTTP Basic Auth, вы не отвечаете на него, и оттуда все идет вниз. В вашем случае вам, вероятно, не нужно внедрять URLSession:didReceiveChallenge:completionHandler: вообще, если вы выполните описанные выше шаги, чтобы установить базовые учетные данные для проверки подлинности по умолчанию для этого защитного места. Система будет обрабатывать NSURLAuthenticationMethodServerTrust , выполнив NSURLAuthenticationMethodServerTrust по умолчанию. Затем система найдет учетные данные по умолчанию, которые вы установили для этого защитного места для базовой проверки подлинности, и используйте это.

ОБНОВИТЬ

Основываясь на новой информации в комментариях и запущенной модифицированной версией кода, OP фактически получает эту ошибку в ответ на свой запрос: NSURLConnection/CFURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9813) Эта ошибка может быть найдена в SecureTransport.h , Корневой сертификат на учетных данных сервера не существует или не поддерживается системой. Это очень редко, но может случиться. Technote 2232 объясняет, как настроить проверку доверия сервера на клиенте, чтобы разрешить этот сертификат .

  • Как использовать NSOperationQueue с NSURLSession?
  • Загрузка NSURLSesssion становится недействительной при расширении общего доступа в ios8 с ошибкой NSURLErrorDomain Code = -995
  • Сбой динамического переноса Swift с запросом HTTP GET
  • Загрузите собственный https-url с помощью UIWebview и NSURLSession
  • iOS 9: Crash on -
  • Загрузка ios FTP при помощи NSURLSession
  • iOS: политика кеша AFHTTPSessionManager AFNetworking
  • Пакетная загрузка файлов с использованием NSURLSession с целостностью файла в iOS
  • Ведение информации сеанса путем обработки файлов cookie в iOS
  • Сохранение данных URL в переменную
  • nsurlsession для множественного запроса в цикле
  • Interesting Posts

    Можно ли определить, изменился ли номер SIM / Телефон?

    Ошибка Xcode 4.6.1, когда я пытаюсь добавить свое устройство в портал инициализации.

    Классы, импортированные в .pch-файл в xcode, не работают, почему?

    setStatusBarHidden не работает

    Преобразование входа AVAudioRecorder в буфер поплавка

    Сохранить аудио с постепенным исчезновением с помощью setVolumeRampFromStartVolume, не работающего в iOS

    лучший способ добавить лицензионный раздел в набор настроек iOS

    Как сделать UIKeyboard черным?

    Не удалось связаться с местным DTServiceHub, чтобы благословить соединение симулятора

    Избегание нарушения LSP при подклассе

    Добавление / редактирование файла подкачки после первоначальной инициализации «firebase»

    Swift: аннотирование пользовательского изображения MapKit

    Что эквивалентно экземпляру java в Swift?

    CoreData – Как использовать validateForDelete: чтобы определить, должен ли удаляться управляемый объект

    Сбой при декодировании объекта после переименования и перезаписи в Swift

    Давайте будем гением компьютера.