ページ

2009年8月28日金曜日

NSHTTPCookieStorage相当のクラスを自前で実装する (4)クッキー送出に成功

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

どうにかこうにかクッキーの送出に成功した。

Googleのログイン画面でID,パスワードを入力すると、


無事にログイン状態となった。


ハマったのは前回紹介したリダイレクト時のクッキーの扱いと、謎のクラッシュ。後者はいろいろ手を入れている間に直ってしまった。もしかすると NSLog() で表示しているオブジェクトに問題があったのかもしれない(NSLog()を一通り消した後、改善していたので)。

サンプル:CookieStorage-2.zip


以下、コード解説。

まず XCHTTPCookieStorageを使う側、WebResourceLoadDelegate のメソッドから。
AppController.m

- (NSURLRequest *)webView:(WebView *)sender
resource:(id)identifier
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
fromDataSource:(WebDataSource *)dataSource
{
[(NSMutableURLRequest*)request setHTTPShouldHandleCookies:NO];

if (redirectResponse) {
[self _setCookiesWithResponse:(NSHTTPURLResponse*)redirectResponse];
}

// NSLog(@"%@", [request URL]);

NSArray* cookies =
[[XCHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[request URL]];

if ([cookies count] > 0) {
NSDictionary* cookie_fields =
[NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
[(NSMutableURLRequest*)request setAllHTTPHeaderFields:cookie_fields];
}

return request;
}


リダイレクト時には redirectResponse が非nilになるので、サーバから受け取ったクッキーを _setCookiesWithResponse: を使い保存する。

URLに適合するクッキーが存在する場合は、-[NSMutableURLRequest setAllHTTPHeaderFields:]を使い、リクエストヘッダに "Cookie"ヘッダを追加する。

レスポンスは _setCookiesWithResponse: を読んでクッキーを保存するだけ。
- (void)webView:(WebView *)sender
resource:(id)identifier
didReceiveResponse:(NSURLResponse *)response
fromDataSource:(WebDataSource *)dataSource
{
[self _setCookiesWithResponse:(NSHTTPURLResponse*)response];
}


_setCookiesWithResponse: の実装はこう。
- (void)_setCookiesWithResponse:(NSHTTPURLResponse*)response
{
NSDictionary* headers = [(NSHTTPURLResponse*)response allHeaderFields];
NSURL* URL = [response URL];
NSArray* cookies =
[NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:URL];

if ([cookies count] > 0) {
[[XCHTTPCookieStorage sharedHTTPCookieStorage] setCookies:cookies
forURL:URL
mainDocumentURL:URL];
}
}



次は XCHTTPCookieStorage の実装〜URLに合ったクッキーを返す処理。
※以前紹介したクッキーの送出ルールはまだ完全に実装できていない。

XCHTTPCookieStorage.m
- (NSArray *)cookiesForURL:(NSURL *)URL
{
NSMutableArray* return_cookies = [NSMutableArray array];
NSString* url_host = [URL host];
NSString* url_path = [URL path];
NSDate* date = [NSDate date];
BOOL is_secure = [[URL scheme] isEqualToString:@"https"];

for (NSHTTPCookie* cookie in [_cookies allValues]) {

NSString* cookie_path = [cookie path];
NSString* cookie_domain = [cookie domain];
NSDate* cookie_expires_date = [cookie expiresDate];

// secure
if ([cookie isSecure] && !is_secure) {
continue;
}

// expires
if (cookie_expires_date &&
[date compare:cookie_expires_date] == NSOrderedDescending) {
continue;
}

// domain
//*TODO* cookie_domain: .www.google.com
if ([cookie_domain hasPrefix:@"."]) {
if (![url_host hasSuffix:cookie_domain]) {
continue;
}
} else if (![url_host isEqualToString:cookie_domain]) {
continue;
}

// path
if (![cookie_path isEqualToString:@"/"]) {
if (![url_path isEqualToString:cookie_path]) {
if (![url_path hasPrefix:[cookie_path stringByAppendingString:@"/"]]) {
continue;
}
}
}
[return_cookies addObject:cookie];
}
return return_cookies;
}


格納されているクッキーを一つ一つ、セキュリティ、有効期限、ドメイン、パス、とチェックしていく。
#この単純な実装ではクッキーが多くなると時間がかかるので工夫が必要だな。

- - - -
とりあえずクッキーの受け取りと送出の基本動作ができるようになった。この後はクッキーの仕様に合った実装を行って完成度を上げていき、最後に永続化処理を入れて完成させる。