При работе с классом java.net.CookieManager иногда возникают странные баги. Например, следующий фрагмента кода будет работать с указанным URL, но с некоторыми другими не будет:
CookieManager manager = new CookieManager();
CookieHandler.setDefault(manager);
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
// force cookie processing
Object content = connection.getContent();
CookieStore cookieJar = manager.getCookieStore();
List HttpCookie> cookies = cookieJar.getCookies();
for (HttpCookie cookie : cookies) {
System.out.println(cookie);
}
Под катом подробнее.
Все дело в классе java.net.HttpCookie и методе expiryDate2DeltaSeconds. Вот его исходный код из JDK 1.6.10:
SimpleDateFormat df = new SimpleDateFormat(NETSCAPE_COOKIE_DATE_FORMAT);
df.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
Date date = df.parse(dateString);
return (date.getTime() - whenCreated) / 1000;
} catch (Exception e) {
return 0;
}
}
Здесь просчитывается время до истечения срока действия cookie и maxAge присваивается это значение. Вызывается этот метод только если установлено значение expired и не установлено Max-Age в строке Set-Cookie (или Set-Cookie2).
Очевидно, что восстановление даты из строки зависит от текущей Locale по умолчанию, поэтому, если она отличается от Locale.US, произойдет исключительная ситуация и выполнится код в блоке catch { }. Т.е., для maxAge всегда будет возвращаться 0, если установлено значение expires и текущая локаль отличается от Locale.US.
Происходит все это при вызове java.net.HttpCookie.parse() из java.net.CookieManager.put(), который в свою очередь вызывается прозрачно при получении HTTP ответа от сервера.
Далее, при добавлении cookie в хранилище по умолчанию sun.net.www.protocol.http.InMemoryCookieStore проиcходит проверка:
if (cookie.getMaxAge() != 0) {
cookieJar.add(cookie);
// ...
Т.е. в случае maxAge==0 cookie просто не сохраняется.
Пути обхода проблемы
Самый простой, но неуклюжий способ, подходящий только для простых утилитных программ, где Locale практически не используется:
Locale oldLocale = Locale.getDefault();
Locale.setDefault(Locale.US);
// establish connection
URLConnection connection = url.openConnection();
// somehow get a content to force cookie parsing
// connection.getContent() is also suitable
InputStream is = connection.getInputStream();
// ...
is.close();
connection.close();
// restore locale
Locale.setDefault(oldLocale);
Способ два — написать свой класс, реализуя интерфейс CookieStore, где пересчитывать значение maxAge для каждой cookie, которую пытаются добавить в хранилище. В этом случае изменится процедура создания CookieManager:
// ...
Баг на sun.com открыт 2009-01-27, обещают, что “Will fix as soon as possible.” Читать как “Ждите выхода JDK 7″.
Полезные ссылки по теме:
Set-Cookie формат (устарел)
Set-Cookie2 формат
