# Cookies

Библиотека автоматически сохраняет подходящие `Set-Cookie` из ответов и подставляет cookies в следующие запросы. Для ручного управления используйте `client.cookies`. Это то же хранилище, которым пользуются запросы, поэтому отдельный контейнер в Python собирать не нужно.

Cookies живут в памяти открытого клиента. Маршруты одного `HttpClient` используют общее хранилище; разные клиенты изолированы, даже если используют один `state_dir`. На низком уровне оно доступно как `Session.cookies` или `AsyncSession.cookies`. Объект `Cookie` — неизменяемая копия записи на момент чтения.

<a id="permissions"></a>
## Чтение и сохранение

Методы чтения и экспорта предоставляют доступ в том числе к значениям `HttpOnly`. Этот признак ограничивает браузерные скрипты, но не явно вызванный SDK-метод. `repr()` скрывает значения; `value` и `export_data()` возвращают их полностью.

Хранилище не сохраняется на диск при запуске или закрытии клиента. Для переноса между запусками экспортируйте данные до закрытия клиента, сохраните их средствами приложения и импортируйте в новый открытый клиент. Импорт проверяет правила cookies и не расширяет разрешённые области.

## Добавление и перенос cookies

В следующем примере `client` уже открыт через `with HttpClient(...)` или `async with AsyncHttpClient(...)`. Методы хранилища синхронные в обоих случаях.

```python
client.cookies.set(
    'language', 'ru', url='https://example.com/',
    path='/', secure=True, same_site='lax',
)
language = client.cookies.get(
    'language', domain='example.com', path='/', host_only=True,
)
saved_cookies = client.cookies.export_data()
```

`saved_cookies` — обычный словарь. Для сохранения между запусками запишите его в своё защищённое хранилище, например в формате JSON. В следующем открытом клиенте восстановите данные:

```python
client.cookies.import_data(saved_cookies)
```

Импорт добавляет и заменяет записи. Если нужна полная замена, сначала вызовите `clear()`, затем `import_data()`. Это два отдельных действия: ошибка импорта не вернёт ранее очищенное содержимое.

<a id="methods"></a>
## Cookies

| Метод | Поведение |
|---|---|
| all() | Возвращает кортеж неизменяемых записей `Cookie` из одного снимка хранилища. Последующие изменения не меняют полученные записи. Итерация и `len()` также используют снимок. |
| get(name, default=None, *, domain=None, path=None, partition_site=None, host_only=None) | Возвращает value/default. Фильтр None не ограничивает область. Более одного совпадения — ValueError; нужна точная область. Если записи отличаются только cross_site_ancestor, у get нет такого фильтра: используйте all и выберите запись явно. |
| set(name, value, *, url, domain=None, path=None, secure=False, http_only=False, same_site=None, expires=None, partition_url=None) | Добавляет или заменяет cookie по правилам `Set-Cookie`. Обязательный `url` задаёт источник записи. `domain=None` ограничивает cookie этим хостом, `path=None` выбирает путь от URL. `expires` — конечное число секунд Unix, округляется вверх. Атрибуты проверяются до изменения. |
| update(values, *, url, **attributes) | Записывает словарь или пары одним действием; общие атрибуты применяются ко всем записям. При ошибке частичного обновления нет. Допустимы `domain`, `path`, `secure`, `http_only`, `same_site`, `expires`, `partition_url`, `partitioned`. `partition_url` задаёт сайт раздела и несовместим с явно переданным `partitioned`. При `partitioned=True` без `partition_url` верхний сайт берётся из `url`. |
| delete(name, *, domain=None, path=None, partition_site=None, host_only=None, cross_site_ancestor=None) | False если нет совпадений, True при удалении; больше одного совпадения — ValueError. |
| clear() | Удаляет все cookies из хранилища этого клиента. |
| export_data() | Словарь с schema=1 и списком cookies, содержащим полные поля Cookie в порядке creation_order. Сам файл не сохраняет. |
| import_data(data) | Добавляет или заменяет записи из экспорта, не очищая хранилище. Принимает словарь только с `schema=1` и `cookies`: до 10000 полных записей `Cookie`. Проверяет все данные и применяет их одной транзакцией. |

`secure`, `http_only` и `partitioned` принимают строго `bool`. `same_site` — `None`, `none`, `lax` или `strict`. Имя, значение, домен и путь проверяются на допустимые ASCII-символы, управляющие символы и `;`; путь должен начинаться с `/`. Хранилище проверяет область домена, публичные суффиксы, Secure, префиксы, SameSite, срок и раздел. Например, `domain` не позволяет установить cookie для постороннего домена.

URL и область нужно передать явно: они не берутся из последнего запроса. Для разделённой cookie `partition_url` задаёт сайт верхнего уровня, `url` — источник записи. `set()` и `update()` записывают cookies с `CookieContext(kind='navigation', credentials='include')`; это привилегированная операция SDK, а не браузерный Fetch.

<a id="record"></a>
## Запись Cookie и формат экспорта

У записи `Cookie` следующие поля:

| Поля | Тип | Значение конструктора по умолчанию |
|---|---|---|
| name, value | str | Обязательные |
| domain, path | str | Пустая строка и / |
| host_only | bool | True |
| secure, http_only | bool | False |
| same_site | str или None | None |
| partition_site | str | Пустая строка |
| partitioned, cross_site_ancestor, persistent | bool | False |
| creation_ms, updated_ms, expiry_ms, creation_order | int | 0 |

`value` скрыто в `repr()`, но доступно как свойство записи. `host_only=True` ограничивает cookie исходным хостом; `False` допускает подходящие поддомены. `persistent` означает наличие срока действия, а не сохранение на диск. `creation_order` задаёт порядок создания.

В снимке и экспорте время задаётся в миллисекундах Unix; аргумент `expires` у `set()` принимает секунды. `expiry_ms=0` без признака `persistent` не означает бессрочное сохранение на диск.

Импорт требует все поля записи, строгие `bool`, неотрицательные целые значения времени и положительный `creation_order`. Одна транзакция восстанавливает время создания, изменения и истечения, порядок, host-only, раздел и признак предка. Неполные или отклонённые записи не пропускаются молча. Экспорт содержит значения cookies: сохраняйте его как данные авторизации, не как обычный журнал.

<a id="request"></a>
## Cookies одного запроса

Параметр `cookies` конструктора задаёт добавочные cookies для каждого запроса, но не наполняет хранилище клиента. `request(cookies=...)` целиком заменяет этот набор для одного вызова; `None` или пустой набор убирает добавку, оставляя автоматический выбор из хранилища.

Сначала выбираются cookies для URL. Затем значения с именами из одноразового набора заменяются его значениями. Само хранилище не меняется. Дубли имён в добавочном наборе запрещены; имя должно быть допустимым HTTP-именем, значение — безопасным ASCII без пробелов, кавычек, запятой, `;` и обратной косой черты. Добавка не обходит правила `omit`, сторонних cookies или SameSite.

Заголовок `Cookie` преобразуется в такой же одноразовый набор. Его нельзя сочетать с непустым набором по умолчанию или явно переданным `cookies`, даже `cookies=None`. Неверные и повторные пары отклоняются.

`cookies_enabled=False` запрещает отправку cookies и несовместим с непустым `cookies` или явным `Cookie`. Если задан собственный контекст, его `credentials` должен быть `omit`. `persist_cookies=False` запрещает только сохранение `Set-Cookie` из ответа. При переходе на другой origin (схему, хост или порт) одноразовый набор удаляется, при прежнем — сохраняется. Cookies из каждого ответа проходят проверку политики.

<a id="policy-context"></a>
## Политика и контекст

Передайте `cookie_policy=CookiePolicyOptions(...)` в конструктор клиента, если нужно изменить правила хранилища. Класс импортируется из `duckonet`.

| Параметр | По умолчанию | Назначение и диапазон |
|---|---|---|
| maximum_cookies | 1000 | Число записей; от 1 до 10000. |
| maximum_header_bytes | 65536 | Размер формируемого заголовка `Cookie`, в байтах; от 4096 до 65536. |
| unspecified_lax_grace_ms | 120000 | Льготный период для cookies без явно заданного SameSite, в миллисекундах; от 0 до 120000. |
| maximum_lifetime_ms | 400*86400000 | Верхний предел срока действия, в миллисекундах; от 1 мс до 400 суток. |
| allow_third_party | True | Разрешать сторонние cookies с учётом остальных правил. Принимает только `bool`. |

`CookieContext` описывает условия, в которых выбираются cookies:

- `top_level_url` — адрес страницы верхнего уровня.
- `initiator_url` — адрес инициатора запроса.
- `kind` — `navigation`, `fetch` или `subresource`; по умолчанию `fetch`.
- `credentials` — `omit` (не отправлять), `same_origin` (тот же origin) или `include` (разрешить с учётом политики); по умолчанию `same_origin`.
- `ancestors` — кортеж URL страниц-предков, до 32 записей.
- `redirect_history` — кортеж предыдущих URL перенаправлений.

Полные URL передаются ASCII-строками длиной не более 16384 символов.

По умолчанию `HttpClient` создаёт контекст `fetch` для заданного URL с `credentials=include` или `omit` при отключённых cookies. Списки предков и переходов пусты, `document_url` равен URL, признаки `initial_navigation`, `user_activation` и `storage_access` выключены. Для стороннего сайта или разделённого хранилища передайте `BrowserHopContext` с нужными `BrowserContext` и `CookieContext`. `body_present` должен соответствовать телу; режим — `cors`, `preflight=False`.

При самостоятельном создании `CookieContext` список `redirect_history` содержит не более 20 URL. Во время выполнения HTTP-запроса клиент может расширить его до 100 записей.



## Область разделённых cookies

При выборе разделённой cookie учитываются сайт верхнего уровня (схема и регистрируемый домен) и `cross_site_ancestor` — наличие сторонней страницы в цепочке предков. Поддомены `a.example.test` и `b.example.test` сами по себе не образуют разные сайты раздела.

`set(partition_url=...)` создаёт запись с навигационным контекстом и `cross_site_ancestor=False`. Запрос из стороннего iframe может иметь `True` и поэтому не выберет эту запись. Импорт и экспорт сохраняют оба признака. Неизвестная область раздела или несовпадение признака предка исключают cookie из отправки.
