DuckoNetДокументация 0.11.5Документация DuckoLab
Разделы

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=NoneMapping или пары для одного открытия; при 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_timeout30 сКонечное число int/float, 0 < x <= 300; bool недопустим.
receive_timeout30 сТо же; срок отдельного recv(), а не общий срок жизни WS.
send_timeout30 сТо же; срок отдельной отправки send() / ping().
close_timeout3 сТо же; окно обмена Close, затем освобождение транспорта.
max_message_bytes16 MiBЦелое 125…64 MiB; граница размера сообщения в байтах, включая UTF-8 текст и данные после распаковки. На входе ограничивается также суммарный payload кадров сообщения до распаковки.
max_frame_bytes1 MiBЦелое 125…max_message_bytes; ограничивает входной payload кадра.
max_queue1Целое 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 прокси с браузером.

Текст страницы