Skip to content

Commit 3f0f6f9

Browse files
pmbrullclaude
andcommitted
fix(ometa): use requests instead of httpx for SSE transport
In some enterprise environments httpx's HTTP/1.1 wire pattern is reset by network security middleboxes that pass requests/wget through. The symptom is httpx.ReadError: [Errno 104] Connection reset by peer at _receive_response_headers, with no server-side access log line because the connection is reset before any HTTP response is committed. requests and wget succeed against the same endpoint with the same JWT and body. The SDK's REST client already uses requests, so this aligns the SSE path with the proven transport. Behavior preserved: same retry loop, same SSE parser, same Last-Event-ID resume on retry, same log lines. Drive-by fix: the previous implementation built opts = { ..., allow_redirects, verify, cookies, ... } but never passed those keys to httpx.stream(...) - a latent bug. requests.Session.request now wires them up properly (defaults: verify=True, allow_redirects=True when unset). No new dependency: requests is already used by the SDK's REST client. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 9a53ec7 commit 3f0f6f9

1 file changed

Lines changed: 47 additions & 30 deletions

File tree

ingestion/src/metadata/ingestion/ometa/sse_client.py

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from logging import Logger # noqa: TC003
1818
from typing import Any, Generator # noqa: UP035
1919

20-
import httpx
20+
import requests
2121

2222
from metadata.ingestion.ometa.client import ClientConfig
2323
from metadata.ingestion.ometa.credentials import URL
@@ -87,35 +87,52 @@ def stream(self, method: str, path: str, data: None | dict[str, Any] = None) ->
8787
if self.last_event_id:
8888
headers["Last-Event-ID"] = self.last_event_id
8989

90-
with httpx.Client(timeout=None) as client: # noqa: SIM117
91-
with client.stream(
92-
method,
93-
url,
94-
headers=headers,
95-
json=opts.get("json"),
96-
params=opts.get("params"),
97-
) as response:
98-
response.raise_for_status()
99-
self.logger.info("Connected to SSE stream")
100-
101-
event_buffer = []
102-
for line in response.iter_lines():
103-
if not line:
104-
if event_buffer:
105-
parsed_event = self._parse_sse_event(event_buffer)
106-
yield parsed_event
107-
event_buffer = []
108-
109-
if self.stream_completed:
110-
self.logger.info(
111-
f"Stream terminated with event: {parsed_event.get('event', 'unknown')}"
112-
)
113-
return
114-
else: # noqa: PLR5501
115-
if not line.startswith(":"):
116-
event_buffer.append(line)
117-
118-
except httpx.HTTPStatusError as e:
90+
# SSE transport uses `requests` rather than `httpx`: the SDK's REST client
91+
# already uses requests, and some enterprise security middleboxes reject
92+
# httpx's HTTP/1.1 wire pattern (lowercase header names, header/body in
93+
# separate TCP segments) while passing requests/wget through unchanged.
94+
request_kwargs = {
95+
"method": method,
96+
"url": str(url),
97+
"headers": headers,
98+
"json": opts.get("json"),
99+
"params": opts.get("params"),
100+
"stream": True,
101+
"timeout": None,
102+
"verify": (
103+
self.config.verify if self.config.verify is not None else True
104+
),
105+
"allow_redirects": (
106+
self.config.allow_redirects
107+
if self.config.allow_redirects is not None
108+
else True
109+
),
110+
"cookies": self.config.cookies,
111+
}
112+
with requests.Session() as session, session.request(
113+
**request_kwargs
114+
) as response:
115+
response.raise_for_status()
116+
self.logger.info("Connected to SSE stream")
117+
118+
event_buffer = []
119+
for line in response.iter_lines(decode_unicode=True):
120+
if not line:
121+
if event_buffer:
122+
parsed_event = self._parse_sse_event(event_buffer)
123+
yield parsed_event
124+
event_buffer = []
125+
126+
if self.stream_completed:
127+
self.logger.info(
128+
f"Stream terminated with event: {parsed_event.get('event', 'unknown')}"
129+
)
130+
return
131+
else: # noqa: PLR5501
132+
if not line.startswith(":"):
133+
event_buffer.append(line)
134+
135+
except requests.exceptions.HTTPError as e:
119136
self.logger.error(f"HTTP error: {e.response.status_code}")
120137
raise
121138
except Exception as e:

0 commit comments

Comments
 (0)