どうにかこうにかクッキーの送出に成功した。
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;
}
格納されているクッキーを一つ一つ、セキュリティ、有効期限、ドメイン、パス、とチェックしていく。
#この単純な実装ではクッキーが多くなると時間がかかるので工夫が必要だな。
- - - -
とりあえずクッキーの受け取りと送出の基本動作ができるようになった。この後はクッキーの仕様に合った実装を行って完成度を上げていき、最後に永続化処理を入れて完成させる。