fix(tradein/browser): снятый PoW-челлендж отдавался с протухшим статусом, и карточка выбрасывалась (#3283) (#3306)
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m10s
Deploy Trade-In / test (push) Successful in 4m5s
Deploy Trade-In / build-backend (push) Successful in 1m39s
Deploy Trade-In / deploy (push) Successful in 8m32s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Successful in 2m10s
Deploy Trade-In / test (push) Successful in 4m5s
Deploy Trade-In / build-backend (push) Successful in 1m39s
Deploy Trade-In / deploy (push) Successful in 8m32s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
Co-authored-by: lekss361 <lekss361@gendsgn.local> Co-committed-by: lekss361 <lekss361@gendsgn.local>
This commit is contained in:
parent
4c29b59041
commit
9cbdc5dfbe
2 changed files with 59 additions and 0 deletions
|
|
@ -1783,6 +1783,10 @@ async def _wait_out_pow_challenge(
|
||||||
|
|
||||||
После снятия челленджа даём странице догидрироваться тем же BROWSER_WAIT_MS,
|
После снятия челленджа даём странице догидрироваться тем же BROWSER_WAIT_MS,
|
||||||
каким ждём обычную навигацию (второй таймаут не изобретаем).
|
каким ждём обычную навигацию (второй таймаут не изобретаем).
|
||||||
|
|
||||||
|
ПОБОЧНЫЙ ЭФФЕКТ: на успешном выходе сбрасывает `_last_response_status[provider]`
|
||||||
|
в None — статус challenge-ответа к этому моменту протух и не описывает
|
||||||
|
устоявшуюся страницу (обоснование — в комментарии у самой строки, #3283).
|
||||||
"""
|
"""
|
||||||
poll_interval_ms = 1000
|
poll_interval_ms = 1000
|
||||||
elapsed_ms = 0
|
elapsed_ms = 0
|
||||||
|
|
@ -1851,6 +1855,24 @@ async def _wait_out_pow_challenge(
|
||||||
f"tradein-browser[{provider}]: челлендж снят, но страница не устоялась "
|
f"tradein-browser[{provider}]: челлендж снят, но страница не устоялась "
|
||||||
f"(навигация не прекращается) url={url!r}"
|
f"(навигация не прекращается) url={url!r}"
|
||||||
)
|
)
|
||||||
|
# #3283: _last_response_status[provider] на этот момент всё ещё хранит статус
|
||||||
|
# ОТВЕТА НА CHALLENGE-СТРАНИЦУ (goto/подзапрос ДО вызова этой функции, напр.
|
||||||
|
# 439/401) — сама PoW-страница чинит себя self-reload'ом (`window.location =
|
||||||
|
# location.href`), Response на который мы не наблюдаем (опрашиваем content(),
|
||||||
|
# не подписаны на page.on("response")), а наш собственный page.reload() в цикле
|
||||||
|
# выше (после BROWSER_CHALLENGE_RELOAD_AFTER_MS) — лишь один из нескольких
|
||||||
|
# возможных источников устоявшегося content(), тоже не гарантированно последний.
|
||||||
|
# Замер прода (прогон 5606): рукопожатие снималось за 1-9с — БЫСТРЕЕ, чем успевал
|
||||||
|
# сработать наш reload (порог 8с) — то есть в типичном случае достоверного
|
||||||
|
# ответа на устоявшийся контент у нас нет вовсе. Отдать наверх протухший статус
|
||||||
|
# challenge-страницы как статус УСТОЯВШЕГОСЯ content() — воскресить #3283:
|
||||||
|
# avito/detail.py трактует 403/439 как блок площадки и выбрасывает уже
|
||||||
|
# вытащенную карточку. None — «статус неизвестен», клиент это НЕ считает блоком
|
||||||
|
# (см. providers/avito/detail.py: `if status is not None: ...`) и падает на
|
||||||
|
# html-эвристики, которые на настоящем контенте не сработают. Действует и на
|
||||||
|
# DomClick-ветку (is_pending=... выше) — там статус на успешном пути и раньше
|
||||||
|
# не проверялся при классификации блока, только логировался как диагностика.
|
||||||
|
_last_response_status[provider] = None
|
||||||
return settled
|
return settled
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,43 @@ def test_fetch_once_still_waits_out_challenge_on_200(monkeypatch: pytest.MonkeyP
|
||||||
assert page.wait_for_timeout_calls # ожидание челленджа реально шло
|
assert page.wait_for_timeout_calls # ожидание челленджа реально шло
|
||||||
|
|
||||||
|
|
||||||
|
def test_fetch_once_clears_stale_challenge_status_once_settled(
|
||||||
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
) -> None:
|
||||||
|
"""#3283: снятый челлендж не должен отдать наверх статус challenge-страницы.
|
||||||
|
|
||||||
|
Замер прода (прогон 5606): сайдкар пишет `_last_response_status` ОДИН раз, из
|
||||||
|
ответа на challenge-страницу (тут — 439), и никогда его не обновляет после
|
||||||
|
self-reload'а, которым PoW чинит себя сам. Клиент (avito/detail.py) трактует
|
||||||
|
403/439 как блок площадки НЕ глядя в HTML и выбрасывает уже вытащенную карточку.
|
||||||
|
После этой правки статус обязан стать None («неизвестен») — клиент такой статус
|
||||||
|
блоком не считает.
|
||||||
|
"""
|
||||||
|
page = _Page([_CHALLENGE_HTML, _REAL_HTML], [_Response(439)])
|
||||||
|
_install(monkeypatch, page)
|
||||||
|
|
||||||
|
html = asyncio.run(server._fetch_once("avito", "https://www.avito.ru/x"))
|
||||||
|
|
||||||
|
assert html == _REAL_HTML
|
||||||
|
assert server._last_response_status["avito"] is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_fetch_once_challenge_timeout_keeps_status_as_is(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
|
"""Челлендж НЕ снялся → ChallengeTimeoutError, статус ответа НЕ обнуляем.
|
||||||
|
|
||||||
|
Отказ остаётся отказом (требование задачи #3283): return settled — единственное
|
||||||
|
место, где мы решаем, что status протух; на ветке таймаута до него не доходит.
|
||||||
|
"""
|
||||||
|
monkeypatch.setattr(server, "BROWSER_CHALLENGE_WAIT_MS", 1000)
|
||||||
|
page = _Page([_CHALLENGE_HTML], [_Response(439)]) # челлендж висит вечно
|
||||||
|
_install(monkeypatch, page)
|
||||||
|
|
||||||
|
with pytest.raises(server.ChallengeTimeoutError):
|
||||||
|
asyncio.run(server._fetch_once("avito", "https://www.avito.ru/x"))
|
||||||
|
|
||||||
|
assert server._last_response_status["avito"] == 439
|
||||||
|
|
||||||
|
|
||||||
def test_fetch_once_ban_marker_still_raises_on_200(monkeypatch: pytest.MonkeyPatch) -> None:
|
def test_fetch_once_ban_marker_still_raises_on_200(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||||
"""Ветка _is_ban_page осталась: «проблема с IP» с кодом 200 — по-прежнему бан."""
|
"""Ветка _is_ban_page осталась: «проблема с IP» с кодом 200 — по-прежнему бан."""
|
||||||
page = _Page(["<html>Доступ ограничен: проблема с IP</html>"], [_Response(200)])
|
page = _Page(["<html>Доступ ограничен: проблема с IP</html>"], [_Response(200)])
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue