WebSocket
Руководство по публичному API DuckoNet 0.11.5 для Python 3.12 x64 на Windows и Linux. Для WebSocket используйте HttpClient или AsyncHttpClient; для HTTP/3 через прокси задайте MasqueProxy. Установите предоставленный wheel для вашей ОС обычным pip install <путь-к-wheel> с его зависимостями. API-ключ берите из своей конфигурации, конечные точки .example замените адресами ваших серверов.
WebSocket
Открывайте соединение через уже настроенный HttpClient или AsyncHttpClient. Используются API-ключ, выбранный браузерный профиль, каталог состояния, CA и cookie jar этого клиента. Требуются совместимые компоненты DuckoNet и закреплённая зависимость websockets==17.3 из поставки. Проверять возможности можно после входа в контекст клиента: client.capabilities.websocket.
import os
from duckonet import HttpClient
with HttpClient(api_key=os.environ["DUCKONET_API_KEY"], browser="chrome") as client:
with client.websocket("wss://echo.example/ws", origin="https://app.example") as ws:
ws.send("Привет")
message = ws.recv() # str для text, bytes для binary
print(message)
ws.send(b"\x00\x01")
print(ws.recv())
print(ws.selected_subprotocol)
print(ws.handshake_response.status_code) # 101
Адреса .example в примерах замените своими конечными точками. websocket() возвращает менеджер контекста; открытие происходит при входе в with / async with, выход закрывает соединение.
Все дополнительные аргументы websocket(url, ...) передаются по имени. У обоих клиентов одинаковые параметры и значения по умолчанию, приведённые в таблицах ниже. AsyncHttpClient.websocket() используется непосредственно с async with, без отдельного await перед вызовом.
import asyncio
import os
from duckonet import AsyncHttpClient
async def main():
async with AsyncHttpClient(api_key=os.environ["DUCKONET_API_KEY"], browser="chrome") as client:
async with client.websocket("wss://echo.example/ws", compression=True) as ws:
await ws.send("Привет")
print(await ws.recv())
await ws.ping(b"probe")
await ws.close(code=1000, reason="Готово")
asyncio.run(main())
| Параметр | Поведение |
|---|---|
url (обязателен) | Строка ws:// или wss://, без credentials и fragment. |
origin=None | Явный Origin; при None заголовок Origin не добавляется. Он не выводится из адреса WS. Если context пропущен, явный Origin также задаёт контекст верхнего уровня и инициатора для cookies. |
subprotocols=() | Последовательность уникальных строковых HTTP tokens. Строка вместо последовательности недопустима. Выбранное значение доступно как ws.selected_subprotocol (str или None). |
compression=True | Только bool. True предлагает permessage-deflate; сервер вправе его не согласовать, тогда обмен продолжается без сжатия. False отключает предложение. Настройка window bits/context takeover отдельно не предоставляется. |
headers=None | Пользовательские заголовки в формате HTTP API: mapping или пары, в том числе повторные. При None наследуются настройки клиента; переданные поля объединяются с ними по правилам HTTP API. |
cookies=None | Mapping или пары для одного открытия; при None наследуются cookies клиента. Пустой mapping снимает унаследованное разовое наложение, но не отключает cookie jar. Разовые cookies не заменяют управление постоянным jar через client.cookies. |
proxy (не передан) | Пропущен — маршрут клиента; None — явный прямой маршрут; строка URL или ProxyRoute — выбранный обычный прокси. WS использует HTTP/HTTPS/SOCKS5/SOCKS5H, с учётом возможностей профиля/runtime. MASQUE предназначен для строгого H3 и для WS не подходит. |
context (не передан) | Расширенный аргумент: настоящий BrowserHopContext с mode="cors", preflight=False, без тела; проверяется согласованность cookie-настроек. При пропуске применяется штатный контекст клиента с отображением ws→http, wss→https. Для обычного использования достаточно origin; вручную собирать контекст не требуется. |
Host, Connection, Upgrade, Sec-WebSocket-Key, Sec-WebSocket-Version, Sec-WebSocket-Extensions, Sec-WebSocket-Protocol и Origin управляются библиотекой: ручная передача этих полей, в том числе в общих headers клиента, отклоняется. Для Origin, subprotocols и compression используйте отдельные параметры. Proxy-Authorization задаётся через credentials маршрута. Нельзя сочетать raw Cookie с разовым cookies=; HTTP cookie-политика остаётся действующей.
При автоматически созданном контексте Referer для открытия WS не добавляется; явные пользовательские заголовки и расширенный контекст сохраняют свои правила.
Set-Cookie ответа открытия обрабатывается общим jar согласно настройкам клиента cookies_enabled и persist_cookies. Значения этих настроек по умолчанию — True. WS не имеет отдельного Python cookie-хранилища.
Сообщения, сроки и завершение
| Метод | Результат и условия |
|---|---|
send(data) | Принимает ровно str или bytes, включая пустые; возвращает None. Текст кодируется строгим UTF-8. Сообщения при необходимости фрагментируются библиотекой. |
recv() | Возвращает одно целое str или bytes; собирает фрагменты и обрабатывает управляющие кадры. |
ping(payload=b"") | Принимает bytes длиной до 125, отправляет Ping и возвращает None; ожидание соответствующего Pong этим методом не предоставляется. Ответ на входящий Ping обрабатывается автоматически. |
close(code=1000, reason="") | Идемпотентное ограниченное по времени закрытие, возвращает None. Нужны допустимый протокольный close code и строка reason не длиннее 123 UTF-8 байт. |
abort() | Идемпотентно прерывает соединение и освобождает его ресурсы, возвращает None. |
Для AsyncWebSocket те же операции выполняются с await. Синхронное соединение поддерживает for message in ws, асинхронное — async for message in ws. Итерация заканчивается без ошибки только при кодах закрытия 1000 или 1001; прямой вызов recv() после исчерпания сообщений поднимает WebSocketClosedError, в том числе при нормальном закрытии. Сообщения, полностью полученные до закрытия сервером, доступны для чтения; локальное close() / abort() отбрасывает ожидающие сообщения.
Доступны свойства closed, selected_subprotocol, handshake_response и generation_receipt. У handshake_response есть status_code, headers (Headers), url, subprotocol; это ответ открытия, без HTTP body. Заголовки могут содержать конфиденциальные значения: не выводите их целиком в общие журналы. generation_receipt относится к поколению runtime/profile, закреплённому для соединения.
| Ограничение | По умолчанию | Допустимые значения |
|---|---|---|
handshake_timeout | 30 с | Конечное число int/float, 0 < x <= 300; bool недопустим. |
receive_timeout | 30 с | То же; срок отдельного recv(), а не общий срок жизни WS. |
send_timeout | 30 с | То же; срок отдельной отправки send() / ping(). |
close_timeout | 3 с | То же; окно обмена Close, затем освобождение транспорта. |
max_message_bytes | 16 MiB | Целое 125…64 MiB; граница размера сообщения в байтах, включая UTF-8 текст и данные после распаковки. На входе ограничивается также суммарный payload кадров сообщения до распаковки. |
max_frame_bytes | 1 MiB | Целое 125…max_message_bytes; ограничивает входной payload кадра. |
max_queue | 1 | Целое 1…16; в двух ограниченных слоях вместе может находиться до 2 * max_queue полных полученных сообщений. При заполнении чтение приостанавливается. Это не лимит общего объёма памяти процесса. |
В одном клиенте допускается до 32 открытых или открывающихся WS одновременно. Различные HTTP/proxy routes также ограничены общим лимитом HTTP API (8). Для handshake применяется предел заголовков до 64 KiB; меньший header_limit клиента ограничивает принятый ответ дополнительно. HTTP body_limit не задаёт размер WS-сообщений.
Разрешены один активный reader и один активный writer на соединение, в том числе одновременно друг с другом. Перекрывающиеся recv() либо send() / ping() поднимают WebSocketConcurrencyError. Обрабатывайте сообщения без накопления собственного неограниченного списка.
Истечение срока recv() или отправки завершает это соединение. Отмена async-операции также прерывает именно принадлежащее ей соединение и ожидает освобождения native ресурсов, после чего возвращает asyncio.CancelledError. Закрытие клиента завершает его WS. Автоматических reconnect и повторной отправки сообщений нет; успешный send() сам по себе не подтверждает обработку сообщения сервером. ОС, планировщик и освобождение native ресурсов могут добавить время сверх протокольного close window.
Ошибки WS
Публичные исключения импортируются из duckonet.
| Исключение | Как обрабатывать |
|---|---|
WebSocketHandshakeError | Отклонённый статус или неверные Accept/subprotocol/extensions. status_code — HTTP статус либо None; например, 301 не вызывает redirect, а 401/407 не считаются успешным открытием. |
WebSocketClosedError | Соединение закрыто; code — код закрытия, обычно 1006 при EOF без Close. |
WebSocketTimeoutError | Также наследует TimeoutError; phase — handshake, receive, send либо close. |
WebSocketProtocolError | Недопустимые входные данные либо превышение лимита сообщения; code: 1002 для протокольной ошибки, 1007 для UTF-8, 1009 для размера. |
WebSocketConcurrencyError | Уже выполняется операция той же стороны (reader или writer). |
WebSocketTransportError | Ошибка транспорта/освобождения, включая отказ открытия транспорта. |
NativeError с code == -2 | Новая возможность недоступна в загруженных компонентах; требуется совместимое обновление DuckoNet. |
ValueError / TypeError | Недопустимые аргументы, headers, URI, deadline, payload или локальный ресурсный лимит. Ошибки открытия/профиля клиента сохраняют собственные типы HTTP/startup API. |
Все WS-исключения выше, кроме последних двух строк, наследуют WebSocketError. TLS, DNS и другие транспортные причины открытия могут быть сведены к безопасному WebSocketTransportError: отдельного публичного WS-класса для каждого TLS/proxy отказа нет. Redirect WS всегда отключён, opt-in параметр для него отсутствует.
Если сервер завершил соединение сразу после корректного ответа Upgrade, открытие может вернуть уже закрытый WS с сохранёнными handshake_response и generation_receipt. Чтение сообщает WebSocketClosedError (при EOF без Close — код 1006); полностью полученные сообщения сохраняют обычные правила чтения. Это отличается от отказа самого handshake: неверный статус или Accept по-прежнему дают WebSocketHandshakeError.
WS выполняется через HTTP/1.1 Upgrade. WebSocket-over-HTTP/2 и WebSocket-over-HTTP/3 не предоставляются. Использование профиля и компрессии само по себе не является обещанием полного совпадения трафика с браузером.
Профиль, cookies и полный прогон
WebSocket и MASQUE используют существующую установку, каталог state_dir, API-ключ и подписанные профили DuckoNet. Для обновления программы сохраняйте прежний state_dir: в нём находятся идентичность установки и история доверия. Cookies живут в памяти клиента; для сохранения после закрытия приложения используйте export_data() и import_data(). Выбор доступных версий браузера по ключу остаётся общим для HTTP и WS.
Когда используется version="last", клиент подготавливает новые доступные поколения в фоне. Обрамляйте весь связанный прогон client.run(): HTTP, открываемые в нём WS и потоки используют закреплённое поколение до завершения своих операций. Следующий внешний run() выбирает уже подготовленное поколение; вложенный run() наследует текущий. Обновление не переключает профиль внутри открытого WS. При явно выбранной версии профиль остаётся закреплённым.
import os
from duckonet import HttpClient
with HttpClient(
api_key=os.environ["DUCKONET_API_KEY"], browser="chrome", version="last",
) as client:
with client.run():
response = client.get("https://app.example/session")
response.raise_for_status()
with client.websocket("wss://app.example/events", origin="https://app.example") as ws:
ws.send("start")
print(ws.recv())
print(client.browser_profile)
В асинхронном коде используются async with client.run() и async with client.websocket(...). Закрывайте WS и потоки внутри своего run(); сам выход из run() не является командой немедленно прервать уже открытое соединение. Без явного run() автоматические операции используют исходное поколение клиента. client.profile_update_status показывает состояние подготовки профиля, а client.browser_profile — фактический профиль выбранного прогона. Поколение не даёт бессрочное право на сетевую работу: действуют обычные проверки срока действия, отзыва и доступа по ключу, в том числе при обмене по WS.
Cookies принадлежат клиенту и сохраняют штатные domain/path/secure/SameSite правила. Они доступны HTTP и WS в пределах того же владельца и переживают штатную смену поколения. Для общего cookie-состояния переиспользуйте один клиент; два независимых клиента не становятся одним cookie jar только из-за одинакового адреса сервера. cookies= — разовое наложение, а постоянными cookies управляйте через client.cookies.
Поддерживаемые маршруты
| Сценарий | Поддержка |
|---|---|
ws:// / wss:// напрямую | HTTP/1.1 Upgrade; текст, binary и permessage-deflate по согласованию. |
| WS через обычный HTTP/HTTPS/SOCKS5/SOCKS5H proxy | С учётом возможностей профиля и транспорта. socks5 использует локальное разрешение цели; socks5h передаёт имя прокси. Для wss сохраняется проверка TLS сайта. |
HTTP/3 к HTTPS-сайту через MasqueProxy | Строгий H3 внутри CONNECT-UDP, внешний carrier H2 с reliable capsules. |
| WS поверх H2/H3, WS через MASQUE | Не предоставляются. |
| H3 через обычную HTTP/HTTPS/SOCKS строку | Не предоставляется; SOCKS5 UDP ASSOCIATE отсутствует. |
| MASQUE AUTO, H1/H2 к сайту, внешний H1/H3 carrier или другой URI template | Не предоставляются в этом API. |
Внешнее соединение с прокси и соединение с сайтом имеют разные назначения. response.protocol относится к сайту. Настройки браузерного профиля применяются к соединению с целевым сайтом. TLS-соединение с прокси устанавливается отдельно. Библиотека не гарантирует полное совпадение всех сетевых признаков WebSocket и TLS прокси с браузером.