Upload
masayuki-nii
View
2.274
Download
4
Embed Size (px)
DESCRIPTION
Cocoa勉強会#47 2011/9/3 NSURLConnectionのデリゲートメソッドと認証 新居雅行
Citation preview
NSURLConnection+デリゲート
通信のためのクラスNSURLConnection• 同期通信は1行でできるが、低速なiOSでは使うことは少ないデリゲートされるメソッドを使って組み込み• メインスレッドで動かせなくもない• 別スレッドを作りそちらで動作させるのがいいかも
3
NSURLConnectionの最低限の使用法
NSURLRequest等からインスタンス化
受信を始めると、以下のメドッドが呼び出される• - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
受信が完了すると、以下のメソッドが呼び出される• - (void)connectionDidFinishLoading:(NSURLConnection *)connection
いちおうエラー処理くらいしよう• - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
4
しかしながら…
これであらゆる場合に対処できるのか?
あらゆるエラーを取得できるのか?
SSLや認証はこれでいいのか?
全メソッドをインプリメントしていろいろな状況で動かしてみる• プロジェクト:ConnectionTest• テスト:iOS 4.3 (シミュレータ)、Mac OS X Server 10.6.x
5
すべてのデリゲートメソッド(1)
通信前• - (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
通信中• - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data• - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
6
すべてのデリゲートメソッド(2)
通信後• - (void)connectionDidFinishLoading:(NSURLConnection *)connection• - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error• - (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
7
すべてのデリゲートメソッド(3)
認証• - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection• - (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace• - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge• - (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
8
- (void)downloadData: (NSString *)urlString{ NSURL *url = [NSURL URLWithString: urlString]; NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL: url]; NSLog( @"urlString = %@", urlString ); self.receivedData = [NSMutableData data]; NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest: urlRequest delegate: self]; if ( connection == nil ) { NSLog( @"ERROR: NSURLConnection is nil" ); }}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ NSLog( @"Calling: connection:didReceiveData:" ); [self.receivedData appendData: data];}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{ NSLog( @"Calling: connection:didFailWithError: %@", error ); self.receivedData = nil; }
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{ NSLog( @"Calling: connectionDidFinishLoading:" ); [connection release]; NSLog( @"receivedData = %@", [[[NSString alloc] initWithData: self.receivedData encoding: NSUTF8StringEncoding] autorelease] ); self.receivedData = nil;}
基本3メソッド
9
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ NSHTTPURLResponse *httpRes = (NSHTTPURLResponse *)response; NSLog( @"Calling: connection:didReceiveResponse: status code=%d", [httpRes statusCode] );}
- (void) connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ NSLog( @"Calling: connection:didCancelAuthenticationChallenge: %@", challenge ); }
- (void) connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{ NSLog( @"Calling: connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:" );}
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse{ NSLog( @"Calling: connection:willSendRequest:redirectResponse: %@", redirectResponse ); return request;}
- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{ NSLog( @"Calling: connectionShouldUseCredentialStorage:" ); return NO;}
10
普通にうまくいった場合
didReceiveResponseが先に呼ばれる
urlString = http://msyk.dyndns.org/test.phpCalling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didReceiveResponse: status code=200Calling: connection:didReceiveData::Calling: connection:didReceiveData:Calling: connectionDidFinishLoading:receivedData = 012345678901234567890…
12
存在しないファイルにアクセスした場合
didFailWithError:は呼ばれない
didReceiveResponse:でのステータスコードのチェックが必要
urlString = http://msyk.dyndns.org/test1.phpCalling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didReceiveResponse: status code=404Calling: connection:didReceiveData:Calling: connectionDidFinishLoading:receivedData = <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>404 Not Found</title>
13
存在しないURLに接続しようとした
didFailWithError:が呼び出される
エラーは「サーバが見つからない」
urlString = http://msyk1234.dyndns.org/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo=0x7013250 {NSErrorFailingURLStringKey=http://msyk1234.dyndns.org/, NSErrorFailingURLKey=http://msyk1234.dyndns.org/, NSLocalizedDescription=A server with the specified hostname could not be found., NSUnderlyingError=0x7013190 "A server with the specified hostname could not be found."}
14
DNSの応答がない場合
didFailWithError:が呼び出される
エラーは「タイムアウト」
urlString = http://msyk.dyndns.org/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x8b14b50 {NSErrorFailingURLStringKey=http://msyk.dyndns.org/, NSErrorFailingURLKey=http://msyk.dyndns.org/, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x8b132d0 "The request timed out."}
15
到達しないIPアドレスを指定した場合
didFailWithError:が呼び出されるエラーは「タイムアウト」
urlString = http://192.168.1.98/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo=0x4b25350 {NSErrorFailingURLStringKey=http://192.168.1.98/, NSErrorFailingURLKey=http://192.168.1.98/, NSLocalizedDescription=The request timed out., NSUnderlyingError=0x4b22330 "The request timed out."}
16
すべてのネットワーク接続がオフ(1)
didFailWithError:が呼び出される
エラーは「インターネットがオフライン」
urlString = http://msyk.dyndns.org/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1009 "The Internet connection appears to be offline." UserInfo=0x4b34070 {NSErrorFailingURLStringKey=http://msyk.dyndns.org/, NSErrorFailingURLKey=http://msyk.dyndns.org/, NSLocalizedDescription=The Internet connection appears to be offline., NSUnderlyingError=0x4b0dbe0 "The Internet connection appears to be offline."}
17
すべてのネットワーク接続がオフ(2)
URLにIPアドレスを指定した場合
didFailWithError:が呼び出される
エラーは「サーバに接続できない」
urlString = http://10.0.1.1/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1004 "Could not connect to the server." UserInfo=0x4e2fa70 {NSErrorFailingURLStringKey=http://10.0.1.1/, NSErrorFailingURLKey=http://10.0.1.1/, NSLocalizedDescription=Could not connect to the server., NSUnderlyingError=0x4e0c4c0 "Could not connect to the server."}
18
リダイレクト
基本3メソッドだけの場合、何もしなくてもOK
willSendRequest:redirectResponse:の呼び出し2回
urlString = http://msyk.dyndns.org/test.phpCalling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:willSendRequest:redirectResponse: <NSHTTPURLResponse: 0x6834f60>Calling: connection:didReceiveResponse: status code=200Calling: connection:didReceiveData::Calling: connection:didReceiveData:Calling: connectionDidFinishLoading:receivedData = <html lang="ja"> <head>
19
正しい証明書のサイト(1)
基本3メソッドだけの場合、何もしなくても接続可能
認証関連のメソッドを単に組み込んだだけの場合だと、以下のように通信は正しくできない
urlString = https://msyk.net/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:canAuthenticateAgainstProtectionSpace: <NSURLProtectionSpace: 0x6502000>Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x65020c0>****何も受信できていない
21
- (BOOL) connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace{ NSString *authMethod = [protectionSpace authenticationMethod]; NSLog( @"Calling: connection:canAuthenticateAgainstProtectionSpace: " " auth method=%@/host=%@", authMethod, [protectionSpace host] ); if ( [authMethod isEqualToString: NSURLAuthenticationMethodServerTrust] ) { secTrustRef = [protectionSpace serverTrust]; if (secTrustRef != NULL) { SecTrustResultType result; OSErr er = SecTrustEvaluate( secTrustRef, &result ); if ( er != noErr) { return NO; } if ( result == kSecTrustResultRecoverableTrustFailure ) { NSLog( @"---SecTrustResultRecoverableTrustFailure" ); } NSLog( @"---Return YES" ); return YES; } } if ( [authMethod isEqualToString: NSURLAuthenticationMethodDefault] ) { NSLog( @"---Return YES" ); return YES; } return NO;}
Security.frameworkも参照しておく必要がある22
正しい証明書のサイト(2)
認証関連メソッドに対応することで接続できる
確認できない証明書にも対応urlString = https://msyk.net/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4d08180>Calling: connection:didReceiveResponse: status code=200Calling: connection:didReceiveData:Calling: connectionDidFinishLoading:receivedData = <html lang="ja">
- (void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ NSLog( @"Calling: connection:didReceiveAuthenticationChallenge: %@", challenge ); NSURLCredential *credential = [NSURLCredential credentialForTrust: secTrustRef]; [[challenge sender] useCredential: credential forAuthenticationChallenge:challenge];}
23
確認できない証明書(エラーにする場合)
SecTrustEvaluate関数の戻り値• kSecTrustResultRecoverableTrustFailureの場合にNOを返す基本3メソッドではこれと同じ状態
urlString = https://coolnotify.com/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=coolnotify.comCalling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “coolnotify.com” which could put your confidential information at risk." UserInfo=0x4b232e0 {NSErrorFailingURLStringKey=https://coolnotify.com/, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSErrorFailingURLKey=https://coolnotify.com/, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “coolnotify.com” which could put your confidential information at risk., NSUnderlyingError=0x4b22c10 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “coolnotify.com” which could put your confidential information at risk.", NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x4b1f320>}
24
- (void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge{ NSLog( @"Calling: connection:didReceiveAuthenticationChallenge: %@", challenge ); if ( [challenge previousFailureCount] == 0 ) { NSURLCredential *credential = [NSURLCredential credentialWithUser: @"te" password: @"te" persistence: NSURLCredentialPersistenceNone]; [[challenge sender] useCredential: credential forAuthenticationChallenge:challenge]; } else if ( [challenge previousFailureCount] == 1 ) { NSURLCredential *credential = [NSURLCredential credentialWithUser: @"msyk" password: @"12345678" persistence: NSURLCredentialPersistenceNone]; [[challenge sender] useCredential: credential forAuthenticationChallenge:challenge]; } else { [[challenge sender] cancelAuthenticationChallenge: challenge]; }
}
26
認証に失敗する場合
urlString = https://msyk.net/iphone/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4b25fd0>Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4b24130>Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x9b00b20>Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1012 "The operation couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x890d7e0 {NSErrorFailingURLKey=https://msyk.net/iphone/, NSErrorFailingURLStringKey=https://msyk.net/iphone/}
27
認証に1度失敗し、2度目に成功する場合
urlString = https://msyk.net/iphone/Calling: connection:willSendRequest:redirectResponse: (null)Calling: connectionShouldUseCredentialStorage:Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x9a030e0>Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x700ae90>Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net---Return YESCalling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4e0a6f0>Calling: connection:didReceiveResponse: status code=200Calling: connection:didReceiveData:Calling: connectionDidFinishLoading:receivedData = <?xml version="1.0" encoding="UTF-8"?>…
28
その他
なぜかdidCancelAuthenticationChallenge:はコールされなかった
connectionShouldUseCredentialStorageの返り値による違いないとしか思えない
29