Protection#
eazy_sdk.protection содержит прикладной API защит ответа: протоколы решателя и контекста,
конструктор challenge_guard(), назначения решения, правила повтора и ошибки стадий. Контракты
компилятора, реестра и хранения доступны только через eazy_sdk.protection.advanced. Там же
находятся ChallengeParseError и AmbiguousChallengeError.
Устанавливаемые защиты#
Символ |
Контракт |
|---|---|
|
Асинхронный протокол |
|
Операция, неизменяемый контекст ответа, номер попытки, |
|
Асинхронный порт |
|
Неизменяемые сведения |
|
Структурный объект для |
|
Защита одним классом: |
|
Группа cookies, объявленных заголовков, query, body и |
|
Понижает детектор, решатель, отображение решения, кеш и правило повтора |
|
Значения |
|
Области защиты; по умолчанию используется |
class VendorGuard(Guard[VendorChallenge]):
scope = host("api.example")
def detect(self, response: ResponseContext[object]) -> VendorChallenge | None:
return detect_challenge(response)
async def solve(self, challenge: VendorChallenge, context: SolveContext) -> GuardSolution:
return self.solution(cookies={"clearance": await vendor.solve(challenge)})
config = ClientConfig(security=Security.of(VendorGuard()))
Функциональная форма принимает challenge_guard(detect=..., solver=..., apply=solution_fields(...), name=..., scope=..., cache=..., replay=...). Имя по умолчанию берётся
из функции детектора или класса-владельца, область охватывает каждый запрос, кеш равен "none".
Решателем может быть объект с синхронным или асинхронным solve() либо функция
(challenge, context) -> solution. Обе формы возвращают неизменяемое устанавливаемое описание и
не читают ответ или сеть во время настройки. guards= и with_protection() понижают их одинаково.
client.invalidate_protection(*targets) удаляет сохранённые решения указанных защит или политик.
Вызов без аргументов очищает всё состояние; результат равен числу удалённых записей.
Детектор получает ResponseContext[object] и даёт TChallenge | None. Значение None
означает отсутствие совпадения. Повреждённую распознанную проверку обозначайте
MalformedChallengeError. Любая другая ошибка детектора становится ChallengeDetectionError и
сохраняет исходное исключение в __cause__.
Контекст решения#
SolveContext предоставляет решателю сведения о транспорте и ограниченный доступ к нему, но не
раскрывает handler, хранилище cookies или клиент.
Поле |
Контракт |
|---|---|
|
Идентификатор защищаемой операции |
|
Неизменяемый контекст ответа с проверкой; |
|
Номер исходной попытки; |
|
Срок решения от начала вызова; |
|
|
|
|
|
Заголовки фактически отправленного запроса |
fetch обращается напрямую к handler. Защиты, реакции, повторы, middleware и ограничитель частоты
не запускаются, поэтому решатель не входит повторно в вызвавший его конвейер.
Перенаправления не выполняются. Возвращается исходный ответ
3xx.Каждый запрос получает полный
timeoutзащищаемого вызова. Для общего бюджета решатель читаетcontext.remaining().Запрос использует cookies сессии, а полученные
Set-Cookieсохраняются в то же хранилище.URL должен быть абсолютным.
identity.user_agentиHostдобавляются, если вызывающий их не задал.
Ошибка транспорта или решателя становится ChallengeSolveError с исходным исключением в
__cause__.
Сохранённое решение связано с identity.fingerprint(). Перед применением runtime сравнивает этот
отпечаток с текущей попыткой. Другой User-Agent, прокси или профиль имитации отменяет состояние;
следующая проверка решается заново под новой транспортной идентичностью. Вручную созданный
SolveContext имеет пустую идентичность, а его fetch вызывает ProtectionConfigurationError.
У попытки ровно один источник транспортной идентичности. user_agent берётся из публичного
User-Agent после значений вызывающего, авторизации и зависимостей, но до применения решения.
proxy и impersonation читает HandlerProfile. Заголовки защиты не меняют отпечаток, хотя
context.request_headers показывает фактически отправленные заголовки.
Назначения решения#
solution_fields() связывает имена назначения с полями типизированного решения.
Параметр |
Назначение |
|---|---|
|
Фиксированный заголовок запроса |
|
Фиксированное поле query |
|
Фиксированный cookie запроса |
|
Закрытый путь семантического тела |
|
Динамический набор именованных cookies |
Компилятор резервирует все назначения. Конфликт с вызывающим, авторизацией, зависимостью,
подписью или другим закрытым writer обнаруживается до решателя и сети. Runtime сначала выбирает и
проверяет всю группу решения, затем применяет одно атомарное изменение. PreparedRequest не
изменяется.
Доказательства безопасного повтора#
Функция |
Доказательство |
|---|---|
|
Повтор разрешён только для GET, HEAD и OPTIONS |
|
Подготовленный запрос содержит указанный заголовок |
|
Протокол доказывает отказ до выполнения |
|
Протокол доказывает, что запрос не дошёл до источника |
ReplayPolicy.max_replays не может быть отрицательным. Нулевой бюджет запрещает повтор проверки.
Новая попытка заново выполняет зависимости, авторизацию, проекцию, шифрование, кодирование,
подготовку и подпись.
Управляемое состояние и сессия#
Повторно используемое решение живёт в одном ExecutionRuntime, которым владеет клиент и его
handler-сессия. Режимы сессии (cache="session" и расширенные until-expiry или until-rejected)
сводят параллельные решения одной проверки к одному вызову. cache="call" и cache="none"
оставляют состояние внутри вызова. Ключ состояния включает фактический handler. Закрытие клиента
очищает решения и записи согласования.
Прокси, User-Agent, семейство адресов и профиль имитации задаются при создании handler-сессии.
Фабрики клиентов настраивают созданную ими сессию и объявляют параметры в HandlerProfile. Для
переданной пользователем сессии proxy= лишь описывает уже заданное значение. Пользовательский
handler сообщает те же сведения через profile. Eazy SDK не исследует сессию транспорта и не
принимает поставщика идентичности на каждую попытку. При смене транспорта создайте новый handler и
клиент; их состояние защиты начинается пустым.
Технология решателя не участвует в предварительной проверке. Браузер, JavaScript, WASM и внешний
API реализуют один типизированный протокол. До запроса проверяются наличие решателя, назначения,
повторяемость тела, доказательство безопасного повтора, конечные бюджеты и точность
HandlerProfile.
Ошибки#
Стадия |
Ошибка |
Условие |
|---|---|---|
Настройка |
|
Повреждена защита или понижение, нет решателя либо конфликтуют назначения |
Обнаружение |
|
Детектор вызвал неожиданное исключение |
Обнаружение |
|
Проверка распознана, но данные непригодны; доступны |
Обнаружение |
|
Две окончательные политики совпали с равным приоритетом |
Решение |
|
Решатель завершился ошибкой |
Применение |
|
Некорректно поле или назначение решения |
Повтор |
|
Недостаточно повторяемости, доказательства или бюджета политики |
Ошибки стадий содержат политику и номер попытки, сохраняют __cause__ и не включают тела ответов,
значения решения, cookies, учётные данные, прокси или секреты решателя. Бюджет считается отдельно
для каждой политики.
Runtime ожидает решатели в обоих клиентах. В синхронном Client асинхронный solve() выполняется
в вызывающем потоке без цикла событий, пока не приостанавливается на собственном асинхронном
вводе-выводе. Такая приостановка приводит к SynchronousSuspensionError: используйте
AsyncClient либо синхронный решатель. Блокировки согласования не привязаны к циклу событий,
поэтому общий для нескольких потоков синхронный клиент решает одну проверку сессии один раз.
Расширенный SPI понижения#
eazy_sdk.protection.advanced служит единственным путём импорта контрактов фреймворков и
сгенерированных политик. Его __all__ закреплён тестом и содержит 49 имён.
Политики:
ChallengePolicy,BeforeCallPolicy,challenge_policy(),before_call_policy().Сигналы:
ResponseSignal,SignalInterception,SignalMatch,SignalOutcome,AmbiguousSignal,MalformedSignal.Решатели:
SolverRequirement,SolverBinding,bind_solver(),SolverBindings,SolverLike.Порты контекста:
ProtectedFetch,TransportIdentity.Операции:
OperationReferenceдля типизацииacquireиverify.Закрытые назначения:
PrivateBindings,PrivateBinding,PrivateBindingTarget,PrivateCookieSetBinding,SolutionLocation,private_bindings(),private_header(),private_query(),private_cookie(),private_cookie_set(),private_body().Хранение:
ProtectionPersistence,ProtectionPersistenceMode,ProtectionStateKey,ProtectionStateScope,per_match(),per_attempt(),per_call(),until_expiry(),until_rejected(),client_identity(),session_identity(),GuardCache.Повтор:
ReplaySafety,BodyReplayPolicy.Обязательные потоки:
ProtectionFlow,protection_flow(),FromProtection.Ошибки:
ChallengeParseError,AmbiguousChallengeError.Значение понижения:
ProtectionBundle.
Записи кеша, реестры блокировок, функции компиляции, арбитраж сигналов и применение решения остаются закрытыми. Прикладной код не должен импортировать расширенный SPI.