fix(agent): отрицательный Retry-After, обрыв при чтении тела ошибки
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014Typ5sXWNMFirr2va2uL8o
This commit is contained in:
parent
1d5e128bd0
commit
24cfdcf25c
@ -86,12 +86,14 @@ class Http:
|
||||
raw = resp.read()
|
||||
status = resp.status
|
||||
except urllib.error.HTTPError as e:
|
||||
self._touch()
|
||||
try:
|
||||
text = e.read().decode("utf-8", "replace")
|
||||
headers = {k.lower(): v for k, v in e.headers.items()}
|
||||
text = e.read().decode("utf-8", "replace")
|
||||
except (OSError, ConnectionError, TimeoutError) as err:
|
||||
raise ConnectionError("%s: обрыв при чтении ответа: %s" % (self.base, err)) from None
|
||||
finally:
|
||||
e.close()
|
||||
self._touch()
|
||||
raise HttpError(e.code, text, headers) from None
|
||||
except (urllib.error.URLError, ConnectionError, TimeoutError, OSError) as e:
|
||||
raise ConnectionError("%s недоступен: %s" % (self.base, e)) from None
|
||||
@ -149,7 +151,7 @@ class JiraClient(Http):
|
||||
if e.code in (429, 503) and attempt < 5:
|
||||
wait = 60 * attempt
|
||||
try:
|
||||
wait = int(e.headers.get("retry-after"))
|
||||
wait = max(0, int(e.headers.get("retry-after")))
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
self.rate_limited += 1
|
||||
|
||||
@ -66,6 +66,29 @@ class ClientsTest(unittest.TestCase):
|
||||
self.jira.get("/rest/api/2/issue/AURORA-1")
|
||||
self.assertEqual(waits, [60])
|
||||
|
||||
def test_retry_after_negative(self):
|
||||
waits = []
|
||||
self.jira.sleep = waits.append
|
||||
self.fake.add("AURORA-1")
|
||||
self.fake.fail_next.append((429, {"Retry-After": "-5"}))
|
||||
self.jira.get("/rest/api/2/issue/AURORA-1")
|
||||
self.assertEqual(waits, [0])
|
||||
|
||||
def test_error_body_read_failure_is_connection_error(self):
|
||||
import io
|
||||
import urllib.error
|
||||
from unittest import mock
|
||||
|
||||
class Broken(io.BytesIO):
|
||||
def read(self, *a):
|
||||
raise TimeoutError("timed out")
|
||||
|
||||
err = urllib.error.HTTPError("http://x", 500, "boom", {}, Broken())
|
||||
with mock.patch("urllib.request.urlopen", side_effect=err):
|
||||
with self.assertRaises(ConnectionError):
|
||||
self.jira.get("/x")
|
||||
self.assertEqual(self.jira.calls, 1)
|
||||
|
||||
def test_bearer_header(self):
|
||||
cfg = jira_cfg(self.fake)
|
||||
cfg["jira"]["auth"] = {"type": "bearer", "token": "tok123"}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user