Cocoa勉強会#47-NSURLConnectionのデリゲートメソッドと認証

4,618 views
4,004 views

Published on

Cocoa勉強会#47
2011/9/3
NSURLConnectionのデリゲートメソッドと認証
新居雅行

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,618
On SlideShare
0
From Embeds
0
Number of Embeds
56
Actions
Shares
0
Downloads
2
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Cocoa勉強会#47-NSURLConnectionのデリゲートメソッドと認証

  1. 1. NSURLConnectionの デリゲートメソッドと認証 Cocoa勉強会 #47 2011/9/3 Masayuki Nii 1
  2. 2. Agenda NSURLConnectionを利用した通信処理 状況別のメソッド呼び出し順序 **6月の浦和での勉強会と同じネタ** 2
  3. 3. NSURLConnection+デリゲート 通信のためのクラスNSURLConnection • 同期通信は1行でできるが、低速なiOSでは使うことは少ない デリゲートされるメソッドを使って組み込み • • メインスレッドで動かせなくもない 別スレッドを作りそちらで動作させるのがいいかも 3
  4. 4. NSURLConnectionの最低限の使用法 NSURLRequest等からインスタンス化 受信を始めると、以下のメドッドが呼び出される • - (void)connection:(NSURLConnection *)connection  didReceiveData:(NSData *)data 受信が完了すると、以下のメソッドが呼び出される • - (void)connectionDidFinishLoading:(NSURLConnection *)connection いちおうエラー処理くらいしよう • - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error 4
  5. 5. しかしながら… これであらゆる場合に対処できるのか? あらゆるエラーを取得できるのか? SSLや認証はこれでいいのか? 全メソッドをインプリメントしていろいろな状況で動か してみる • • プロジェクト:ConnectionTest テスト:iOS 4.3 (シミュレータ)、Mac OS X Server 10.6.x 5
  6. 6. すべてのデリゲートメソッド(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
  7. 7. すべてのデリゲートメソッド(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
  8. 8. すべてのデリゲートメソッド(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
  9. 9. - (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]; } 基本3メソッド - (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; } 9
  10. 10. - (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
  11. 11. ネットワーク関連エラー 11
  12. 12. 普通にうまくいった場合 didReceiveResponseが先に呼ばれる urlString = http://msyk.dyndns.org/test.php Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:didReceiveResponse: status code=200 Calling: connection:didReceiveData: : Calling: connection:didReceiveData: Calling: connectionDidFinishLoading: receivedData = 012345678901234567890… 12
  13. 13. 存在しないファイルにアクセスした場合 didFailWithError:は呼ばれない didReceiveResponse:でのステータスコードのチェッ クが必要 urlString = http://msyk.dyndns.org/test1.php Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:didReceiveResponse: status code=404 Calling: connection:didReceiveData: Calling: connectionDidFinishLoading: receivedData = <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>404 Not Found</title> 13
  14. 14. 存在しない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
  15. 15. 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
  16. 16. 到達しない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
  17. 17. すべてのネットワーク接続がオフ(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
  18. 18. すべてのネットワーク接続がオフ(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
  19. 19. リダイレクト 基本3メソッドだけの場合、何もしなくてもOK willSendRequest:redirectResponse:の呼び出し2回 urlString = http://msyk.dyndns.org/test.php Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:willSendRequest:redirectResponse: <NSHTTPURLResponse: 0x6834f60> Calling: connection:didReceiveResponse: status code=200 Calling: connection:didReceiveData: : Calling: connection:didReceiveData: Calling: connectionDidFinishLoading: receivedData = <html lang="ja"> <head> 19
  20. 20. SSL 20
  21. 21. 正しい証明書のサイト(1) 基本3メソッドだけの場合、何もしなくても接続可能 認証関連のメソッドを単に組み込んだだけの場合だと、 以下のように通信は正しくできない urlString = https://msyk.net/ Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:canAuthenticateAgainstProtectionSpace: <NSURLProtectionSpace: 0x6502 Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x ****何も受信できていない 21
  22. 22. - (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
  23. 23. 正しい証明書のサイト(2) - (void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { NSLog( @"Calling: connection:didReceiveAuthenticationChallenge: %@", challenge ); NSURLCredential *credential = [NSURLCredential credentialForTrust: secTrustRef]; [[challenge sender] useCredential: credential forAuthenticationChallenge:challenge]; } 認証関連メソッドに対応することで接続できる 確認できない証明書にも対応 urlString = https://msyk.net/ Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMe ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4d0818 Calling: connection:didReceiveResponse: status code=200 Calling: connection:didReceiveData: Calling: connectionDidFinishLoading: receivedData = <html lang="ja"> 23
  24. 24. 確認できない証明書(エラーにする場合) SecTrustEvaluate関数の戻り値 • kSecTrustResultRecoverableTrustFailureの場合にNOを返す 基本3メソッドではこれと同じ状態 urlString = https://coolnotify.com/ Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=coolnotify.com Calling: 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: 24
  25. 25. Authentication 25
  26. 26. - (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
  27. 27. 認証に失敗する場合 urlString = https://msyk.net/iphone/ Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=msyk.net ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4b25fd0 Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4b2413 Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x9b00b2 Calling: connection:didFailWithError: Error Domain=NSURLErrorDomain Code=-1012 "The operat couldn’t be completed. (NSURLErrorDomain error -1012.)" UserInfo=0x890d7e0 {NSErrorFailingURLKey=https://msyk.net/iphone/, NSErrorFailingURLStringKey=https://msyk.net/ iphone/} 27
  28. 28. 認証に1度失敗し、2度目に成功する場合 urlString = https://msyk.net/iphone/ Calling: connection:willSendRequest:redirectResponse: (null) Calling: connectionShouldUseCredentialStorage: Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodServerTrust/host=msyk.net ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x9a030e0 Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x700ae90 Calling: connection:canAuthenticateAgainstProtectionSpace: auth method=NSURLAuthenticationMethodDefault/host=msyk.net ---Return YES Calling: connection:didReceiveAuthenticationChallenge: <NSURLAuthenticationChallenge: 0x4e0a6f0 Calling: connection:didReceiveResponse: status code=200 Calling: connection:didReceiveData: Calling: connectionDidFinishLoading: receivedData = <?xml version="1.0" encoding="UTF-8"?>… 28
  29. 29. その他 なぜかdidCancelAuthenticationChallenge:はコールさ れなかった connectionShouldUseCredentialStorageの返り値に よる違いないとしか思えない 29
  30. 30. まとめ NSURLConnectionはネットワークに関係なく生成 didReceiveResponse:メソッドでステータスコード didFailWithError:が呼び出されれば通信エラー 認証への対応はメソッドへの応答として記述する 認証とSSLの両方があるときには要注意 30

×