Skip to content

Commit 7ddd85e

Browse files
Harden request-error context extraction against malformed errors
Co-authored-by: Shri Sukhani <shrisukhani@users.noreply.github.com>
1 parent bb276da commit 7ddd85e

2 files changed

Lines changed: 109 additions & 2 deletions

File tree

hyperbrowser/transport/error_utils.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,24 @@ def extract_error_message(response: httpx.Response, fallback_error: Exception) -
3838
def extract_request_error_context(error: httpx.RequestError) -> tuple[str, str]:
3939
try:
4040
request = error.request
41-
except RuntimeError:
41+
except Exception:
4242
request = None
4343
if request is None:
4444
return "UNKNOWN", "unknown URL"
45-
return request.method, str(request.url)
45+
try:
46+
request_method = request.method
47+
except Exception:
48+
request_method = "UNKNOWN"
49+
if not isinstance(request_method, str) or not request_method.strip():
50+
request_method = "UNKNOWN"
51+
52+
try:
53+
request_url = str(request.url)
54+
except Exception:
55+
request_url = "unknown URL"
56+
if not request_url.strip():
57+
request_url = "unknown URL"
58+
return request_method, request_url
4659

4760

4861
def format_request_failure_message(
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import httpx
2+
3+
from hyperbrowser.transport.error_utils import (
4+
extract_request_error_context,
5+
format_request_failure_message,
6+
)
7+
8+
9+
class _BrokenRequest:
10+
@property
11+
def method(self) -> str:
12+
raise ValueError("missing method")
13+
14+
@property
15+
def url(self) -> str:
16+
raise ValueError("missing url")
17+
18+
19+
class _BlankContextRequest:
20+
method = " "
21+
url = " "
22+
23+
24+
class _RequestErrorWithFailingRequestProperty(httpx.RequestError):
25+
@property
26+
def request(self): # type: ignore[override]
27+
raise ValueError("broken request property")
28+
29+
30+
class _RequestErrorWithBrokenRequest(httpx.RequestError):
31+
@property
32+
def request(self): # type: ignore[override]
33+
return _BrokenRequest()
34+
35+
36+
class _RequestErrorWithBlankContext(httpx.RequestError):
37+
@property
38+
def request(self): # type: ignore[override]
39+
return _BlankContextRequest()
40+
41+
42+
def test_extract_request_error_context_uses_unknown_when_request_unset():
43+
method, url = extract_request_error_context(httpx.RequestError("network down"))
44+
45+
assert method == "UNKNOWN"
46+
assert url == "unknown URL"
47+
48+
49+
def test_extract_request_error_context_handles_request_property_failures():
50+
method, url = extract_request_error_context(
51+
_RequestErrorWithFailingRequestProperty("network down")
52+
)
53+
54+
assert method == "UNKNOWN"
55+
assert url == "unknown URL"
56+
57+
58+
def test_extract_request_error_context_handles_broken_request_attributes():
59+
method, url = extract_request_error_context(
60+
_RequestErrorWithBrokenRequest("network down")
61+
)
62+
63+
assert method == "UNKNOWN"
64+
assert url == "unknown URL"
65+
66+
67+
def test_extract_request_error_context_normalizes_blank_method_and_url():
68+
method, url = extract_request_error_context(
69+
_RequestErrorWithBlankContext("network down")
70+
)
71+
72+
assert method == "UNKNOWN"
73+
assert url == "unknown URL"
74+
75+
76+
def test_format_request_failure_message_uses_fallback_values():
77+
message = format_request_failure_message(
78+
httpx.RequestError("network down"),
79+
fallback_method="GET",
80+
fallback_url="https://example.com/fallback",
81+
)
82+
83+
assert message == "Request GET https://example.com/fallback failed"
84+
85+
86+
def test_format_request_failure_message_prefers_request_context():
87+
request = httpx.Request("POST", "https://example.com/actual")
88+
message = format_request_failure_message(
89+
httpx.RequestError("network down", request=request),
90+
fallback_method="GET",
91+
fallback_url="https://example.com/fallback",
92+
)
93+
94+
assert message == "Request POST https://example.com/actual failed"

0 commit comments

Comments
 (0)