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

DNS, TLS, скорость и SOCKS5 UDP

DNS, адреса и TCP

import os
from duckonet import HttpClient, NetworkOptions, ResolveRule

network = NetworkOptions(
    resolve=(ResolveRule('example.com', 443, ('192.0.2.10',)),),
    tcp_keepalive=True,
    tcp_keepidle=30,
    tcp_keepinterval=10,
    tcp_keepcount=3,
)
with HttpClient(api_key=os.environ['DUCKONET_API_KEY'], network=network) as client:
    response = client.get('https://example.com/')

192.0.2.10 в примере — адрес для документации; замените его адресом своего сервера с подходящим сертификатом.

ResolveRule(host, port, addresses) задаёт от 1 до 16 уникальных IPv4/IPv6-адресов для origin. addresses — кортеж строк без квадратных скобок и zone ID. ConnectRule(host, port, target_host, target_port) меняет точку подключения, сохраняя исходные Host, SNI, проверяемое имя сертификата и область cookies. Порты — 1–65535. Имена Unicode нормализуются общим IDNA-механизмом при создании нативной сессии.

NetworkOptionsПо умолчаниюЗначение
resolve()До 64 ResolveRule; без повторяющихся origin.
connect_to()До 64 ConnectRule; без повторяющихся origin.
doh_urlNoneHTTPS URL DNS-over-HTTPS; без userinfo, fragment и пробельных символов. Сертификат DoH-сервера проверяется.
doh_bootstrap()До 16 уникальных IP-адресов DoH-сервера; требует doh_url. Без bootstrap имя DoH-сервера нужно разрешить обычным способом.
interfaceNoneЯвное имя сетевого интерфейса без специальных curl-префиксов. Поддержка зависит от ОС.
source_ipNoneЯвный локальный IP без скобок и zone ID.
familyNone'auto', 'ipv4' или 'ipv6'; исходный адрес не должен противоречить семейству.
local_portNoneЛокальный порт 1–65535.
local_port_range1Число последовательных портов начиная с local_port; диапазон не выходит за 65535.
tcp_keepaliveNoneTrue включает, False отключает, None не задаёт отдельное переопределение.
tcp_keepidleNoneСекунды до keepalive-пробы, 1–86400; требует tcp_keepalive=True.
tcp_keepintervalNoneСекунды между пробами, 1–86400; требует tcp_keepalive=True.
tcp_keepcountNoneЧисло проб, 1–255; требует tcp_keepalive=True.
tcp_nodelayNoneНеобязательное явное управление TCP_NODELAY.

Опции неизменяемы. enabled показывает наличие явных настроек. Одновременные interface и source_ip отклоняются. DoH не совмещается с прокси, локальной привязкой или явным семейством; разрешающие origin-переопределения не совмещаются с маршрутом, где целевое DNS выполняет прокси. Это явные ошибки совместимости, а не повод перейти на прямое соединение. TCP-опции действуют на TCP-сокеты; они не эмулируют TCP другой ОС для QUIC.

Клиентский сертификат и SPKI

import os
from duckonet import HttpClient, ClientCertificate, TLSOptions

tls = TLSOptions(client_certificate=ClientCertificate('client.pem', 'client-key.pem'))
with HttpClient(api_key=os.environ['DUCKONET_API_KEY'], tls=tls) as client:
    response = client.get('https://example.com/private')

ClientCertificate(certificate, private_key, password=None) принимает пути str/Path к PEM-цепочке сертификатов и PEM-ключу. Пароль зашифрованного ключа — строка; пути и пароль не показываются в repr. Конструирование объекта файлы не читает. При создании нативной сессии каждый файл должен быть обычным и не превышать 2 МиБ; пути Unicode поддерживаются. PKCS12, аппаратные ключи и системное хранилище сертификатов этим интерфейсом не предоставляются.

TLSOptions(client_certificate=None, spki_pins=()) добавляет до 16 уникальных пинов вида sha256//BASE64_SHA256_SPKI. После префикса находится канонический Base64 от 32 байтов SHA-256 публичного SPKI, а не хеш всего сертификата. Для смены ключа заранее включите допустимые старый и новый пины в один кортеж. Пиннинг дополняет обычную проверку CA и имени сервера; отключить её этими опциями нельзя. enabled показывает наличие клиентского сертификата или пинов.

Разные TLS-настройки создают изолированные сессии. Для ротации откройте новую сессию; уже выполняемый handshake не меняется. Не храните пароль ключа в исходном тексте и не публикуйте файлы ключа.

Ограничение скорости и диагностика

RateLimit(send_bytes_per_second=None, receive_bytes_per_second=None, burst_bytes=65536) ограничивает передачу тела в соответствующем направлении. Скорости — целые байты в секунду от 1 до 2**63-1; None оставляет направление без отдельного лимита. burst_bytes — допустимый короткий всплеск от 16384 до 65536. enabled сообщает, задана ли хотя бы одна скорость.

Для отправки учитываются байты тела приложения, для приёма — выдаваемые декодированные байты тела. Заголовки, TLS и канальные накладные расходы в этот лимит не входят. Это ограничитель потока данных, а не обещание точного физического bitrate. Намеренное ожидание лимита не считается простоем чтения, но общий таймаут продолжает идти. Ограниченные запросы используют отдельные соединения, чтобы не задерживать соседние HTTP/2 или HTTP/3 потоки. WebSocket с rate_limit не поддерживается.

Пример безопасного журнала:

import os
from duckonet import HttpClient, RateLimit

def completed(event):
    print(event.method, event.origin, event.status_code)

with HttpClient(
    api_key=os.environ['DUCKONET_API_KEY'],
    rate_limit=RateLimit(receive_bytes_per_second=262144),
    diagnostic_hook=completed,
) as client:
    response = client.get('https://example.com/file')

HttpDiagnosticEvent содержит method, origin, status_code, protocol, from_cache, attempt_count, timings, socket_info, phase='complete'. Путь, query, заголовки и тело в событие не входят. timings — кортеж пар с длительностями либо None, где измерение отсутствует. Для обычного ответа и cache hit hook вызывается один раз; для потока — один раз при явном успешном stream.result(). Закрытие недочитанного потока не принуждает его дочитываться ради hook.

У синхронного клиента hook должен быть синхронным. Асинхронный клиент допускает обычную либо асинхронную функцию. Исключение hook передаётся вызывающему коду после освобождения ресурсов и не запускает повтор уже завершённого запроса.

SocketInfo содержит необязательные local_address, local_port, local_family, remote_address, remote_port, remote_family, а также proxy_route=False и observation='connected-transport-socket'. Семейство — 4 или 6. Это адреса наблюдаемого транспортного сокета: при прокси удалённая сторона может быть прокси, а не origin. Отсутствующие сведения остаются None; библиотека не подставляет предполагаемый адрес сервера.

Ограничение скорости относится к телу отдельного запроса. Совместимость настроек учитывается при создании транспорта; одновременные запросы могут использовать отдельные соединения. Не рассчитывайте на общий лимит всего процесса или на обязательное мультиплексирование всех запросов в одном TCP/QUIC-соединении.

HTTP/3 через SOCKS5 UDP

import os
from duckonet import HttpClient, Socks5UDPProxy

proxy = Socks5UDPProxy('socks5h://127.0.0.1:1080')
with HttpClient(
    api_key=os.environ['DUCKONET_API_KEY'],
    protocol='h3',
    proxy=proxy,
) as client:
    response = client.get('https://example.com/')

Socks5UDPProxy(endpoint, username='', password='') явно выбирает UDP ASSOCIATE. socks5:// разрешает имя целевого сервера локально, socks5h:// передаёт его прокси. Endpoint содержит схему, адрес и порт; логин и пароль передаются отдельными полями. Свойство udp_associate равно True.

Нужен прокси с поддержкой UDP ASSOCIATE и разрешённым UDP-трафиком. Маршрут требует строгий HTTP/3: protocol='h3' на высоком уровне либо application='h3_only'; обычный ProxyRoute не переключается на этот режим автоматически. Обрыв управления TCP завершает ассоциацию. Ошибка реле, блокировка UDP, неверные адреса и фрагментированные SOCKS-датаграммы не вызывают прямой fallback. Доменный адрес самого реле разрешается локально независимо от настройки DNS целевого сервера.

HTTP/HTTPS/SOCKS TCP и существующий MasqueProxy остаются отдельными вариантами маршрута. Socks5UDPProxy не добавляет WebSocket поверх HTTP/3.

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