1. Обзор
В этой статье мы собираемся изучить низкоуровневые операции с сетевым программированием на Java. Мы более подробно рассмотрим файлы cookie.
Платформа Java поставляется со встроенной сетевой поддержкой, включенной в пакет java.net :
import java.net.*;
2. HTTP-куки
Всякий раз, когда клиент отправляет HTTP-запрос на сервер и получает на него ответ, сервер забывает об этом клиенте. В следующий раз, когда клиент запросит снова, он будет рассматриваться как совершенно новый клиент.
Однако, как известно, файлы cookie позволяют установить сеанс между клиентом и сервером таким образом, что сервер может запомнить клиента по нескольким парам запросов и ответов.
В дальнейшем из этого раздела мы узнаем, как использовать файлы cookie для улучшения взаимодействия клиент-сервер в сетевом программировании на Java.
Основным классом в пакете java.net
для обработки файлов cookie является CookieHandler
. Существуют и другие вспомогательные классы и интерфейсы, такие как CookieManager
, CookiePolicy
, CookieStore
и HttpCookie
.
3. Класс CookieHandler
Рассмотрим этот сценарий; мы общаемся с сервером по адресу http://foreach.com
или любому другому URL-адресу, который использует протокол HTTP, объект URL будет использовать механизм, называемый обработчиком протокола HTTP.
Этот обработчик протокола HTTP проверяет, существует ли в системе экземпляр CookieHandler по умолчанию.
Если есть, он призывает его взять на себя управление состоянием.
Таким образом, цель класса CookieHandler
— предоставить механизм обратного вызова в интересах обработчика протокола HTTP.
CookieHandler
— это абстрактный класс. У него есть статический метод getDefault()
, который можно вызвать для получения текущей установки
CookieHandler
, или мы можем вызвать setDefault(CookieHandler)
, чтобы установить свою собственную. Обратите внимание, что вызов setDefault
устанавливает объект CookieHandler
для всей системы.
Он также имеет put(uri, responseHeaders)
для сохранения любых файлов cookie в хранилище файлов cookie. Эти файлы cookie извлекаются из заголовков ответа HTTP от данного URI. Он вызывается каждый раз при получении ответа.
Связанный метод API — get(uri,requestHeaders)
извлекает файлы cookie, сохраненные под заданным URI, и добавляет их в requetHeaders
. Он вызывается непосредственно перед выполнением запроса.
Все эти методы должны быть реализованы в конкретном классе CookieHandler
. На данный момент нашего внимания заслуживает класс CookieManager .
Этот класс предлагает полную реализацию класса CookieHandler
для наиболее распространенных случаев использования.
В следующих двух разделах мы собираемся изучить класс CookieManager
; сначала в режиме по умолчанию, а затем в пользовательском режиме.
4. Менеджер файлов cookie по умолчанию
Чтобы иметь полную структуру управления файлами cookie, нам нужны реализации CookiePolicy
и CookieStore
.
CookiePolicy
устанавливает правила принятия и отклонения файлов cookie. Конечно, мы можем изменить эти правила в соответствии с нашими потребностями.
Далее — CookieStore
делает именно то, что предполагает его название, у него есть методы для сохранения и извлечения файлов cookie. Естественно, мы можем настроить механизм хранения и здесь, если нам нужно.
Давайте сначала посмотрим на значения по умолчанию. Чтобы создать обработчик CookieHandler
по умолчанию и установить его по умолчанию для всей системы:
CookieManager cm = new CookieManager();
CookieHandler.setDefault(cm);
Мы должны отметить, что CookieStore
по умолчанию будет иметь энергозависимую память, т.е. она существует только в течение времени жизни JVM. Чтобы иметь более постоянное хранилище для файлов cookie, мы должны настроить его.
Когда дело доходит до CookiePolicy
, реализация по умолчанию — CookiePolicy.ACCEPT_ORIGINAL_SERVER
. Это означает, что если ответ получен через прокси-сервер, то cookie будет отклонен.
5. Пользовательский диспетчер файлов cookie
Давайте теперь настроим CookieManager
по умолчанию, предоставив наш собственный экземпляр CookiePolicy
или CookieStore
(или обоих).
5.1. CookiePolicy
CookiePolicy
предоставляет несколько предопределенных реализаций для удобства:
CookiePolicy.ACCEPT_ORIGINAL_SERVER
— могут быть сохранены только файлы cookie с исходного сервера (реализация по умолчанию)CookiePolicy.ACCEPT_ALL
— все файлы cookie могут быть сохранены независимо от их происхождения.CookiePolicy.ACCEPT_NONE
— файлы cookie не могут быть сохранены .
Чтобы просто изменить текущую CookiePolicy
без реализации собственной, мы вызываем setCookiePolicy
для экземпляра CookieManager
:
CookieManager cm=new CookieManager();
cm.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
Но мы можем сделать гораздо больше настроек, чем это. Зная поведение CookiePolicy.ACCEPT_ORIGINAL_SERVER
, давайте предположим, что мы доверяем определенному прокси-серверу и хотели бы принимать файлы cookie от него поверх исходного сервера.
Нам придется реализовать интерфейс CookiePolicy
и реализовать метод shouldAccept
; именно здесь мы изменим правило приема, добавив доменное имя выбранного прокси-сервера.
Назовем новую политику — ProxyAcceptCookiePolicy
. Он в основном отклонит любой другой прокси-сервер из своей реализации shouldAccept
, кроме заданного прокси-адреса, а затем вызовет метод shouldAccept
CookiePolicy.ACCEPT_ORIGINAL_SERVER
для завершения реализации:
public class ProxyAcceptCookiePolicy implements CookiePolicy {
private String acceptedProxy;
public boolean shouldAccept(URI uri, HttpCookie cookie) {
String host = InetAddress.getByName(uri.getHost())
.getCanonicalHostName();
if (HttpCookie.domainMatches(acceptedProxy, host)) {
return true;
}
return CookiePolicy.ACCEPT_ORIGINAL_SERVER
.shouldAccept(uri, cookie);
}
// standard constructors
}
Когда мы создаем экземпляр ProxyAcceptCookiePolicy
, мы передаем строку адреса домена, с которого мы хотели бы принимать файлы cookie в дополнение к исходному серверу.
Затем мы устанавливаем этот экземпляр в качестве политики cookie для экземпляра CookieManager
, прежде чем установить его в качестве CookieHandler по умолчанию:
CookieManager cm = new CookieManager();
cm.setCookiePolicy(new ProxyAcceptCookiePolicy("foreach.com"));
CookieHandler.setDefault(cm);
Таким образом, обработчик файлов cookie будет принимать все файлы cookie с исходного сервера, а также файлы с http://www.foreach.com
.
5.2. CookieStore
CookieManager
добавляет файлы cookie в CookieStore
для каждого HTTP-ответа и извлекает файлы cookie из CookieStore
для каждого HTTP-запроса.
Реализация CookieStore
по умолчанию не имеет сохранения, она скорее теряет все свои данные при перезапуске JVM. Больше похоже на оперативную память в компьютере.
Поэтому, если мы хотим, чтобы наша реализация CookieStore
вел себя как жесткий диск и сохранял файлы cookie при перезапусках JVM, мы должны настроить его механизм хранения и извлечения.
Следует отметить, что мы не можем передать экземпляр CookieStore
в CookieManager
после создания. Наш единственный вариант — передать его во время создания CookieManager
или получить ссылку на экземпляр по умолчанию, вызвав new CookieManager().getCookieStore()
и дополнив его поведение.
Вот реализация PersistentCookieStore
:
public class PersistentCookieStore implements CookieStore, Runnable {
private CookieStore store;
public PersistentCookieStore() {
store = new CookieManager().getCookieStore();
// deserialize cookies into store
Runtime.getRuntime().addShutdownHook(new Thread(this));
}
@Override
public void run() {
// serialize cookies to persistent storage
}
@Override
public void add(URI uri, HttpCookie cookie) {
store.add(uri, cookie);
}
// delegate all implementations to store object like above
}
Обратите внимание, что мы получили ссылку на реализацию по умолчанию в конструкторе.
Мы реализуем runnable
, чтобы можно было добавить перехватчик выключения, который запускается при завершении работы JVM. Внутри метода run
мы сохраняем все наши файлы cookie в памяти.
Мы можем сериализовать данные в файл или любое подходящее хранилище. Обратите также внимание, что внутри конструктора мы сначала считываем все файлы cookie из постоянной памяти в CookieStore
. Эти две простые функции делают хранилище CookieStore
по умолчанию практически постоянным (в упрощенном виде).
6. Заключение
В этом руководстве мы рассмотрели файлы cookie HTTP и показали, как программно получать к ним доступ и управлять ими.
Полный исходный код статьи и все фрагменты кода можно найти в проекте GitHub .