Keep SDK loopback requests direct and disable shared cookies

This commit is contained in:
Palash Debnath
2026-09-28 17:17:33 -07:00
parent ff0b720413
commit 09382bbbec
3 changed files with 47 additions and 1 deletions
+15 -1
View File
@@ -4,9 +4,18 @@ from __future__ import annotations
import os
import atexit
from functools import lru_cache
from http.cookiejar import CookieJar, DefaultCookiePolicy
from types import SimpleNamespace
class _NoCookies(DefaultCookiePolicy):
def set_ok(self, cookie, request):
return False
def return_ok(self, cookie, request):
return False
def _check_sdk_request(request):
"""Validate the effective URL after SDK environment/provider resolution."""
from ipaddress import ip_address
@@ -28,7 +37,12 @@ def _sdk_http_client():
# A shared thread-safe pool also keeps streaming responses alive after
# completion() returns. It carries no provider credentials of its own.
import httpx
client = httpx.Client(follow_redirects=False, event_hooks={"request": [_check_sdk_request]})
client = httpx.Client(
follow_redirects=False,
trust_env=False,
cookies=CookieJar(policy=_NoCookies()),
event_hooks={"request": [_check_sdk_request]},
)
atexit.register(client.close)
return client
+2
View File
@@ -150,3 +150,5 @@ LM Studio automatic discovery accepts only loaded `llm`/`vlm` entries from its n
SDK transport validates the final request URL, including environment-derived
OpenAI endpoints, and disables redirects for OpenAI-compatible and native
Anthropic, Bedrock, and Vertex requests.
The shared HTTP client ignores environment proxies so loopback prompts stay local,
and never stores response cookies between provider requests.
+30
View File
@@ -159,3 +159,33 @@ def test_anthropic_does_not_follow_credentialed_redirect(registry):
assert seen == ["/v1/messages"]
finally:
server.shutdown(); server.server_close(); thread.join(timeout=2)
@pytest.mark.parametrize("proxy_enabled", [False, True])
def test_sdk_pool_keeps_loopback_direct_and_does_not_persist_cookies(monkeypatch, proxy_enabled):
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from threading import Thread
from services.llm_transport import _sdk_http_client
seen = []
class Handler(BaseHTTPRequestHandler):
def log_message(self, *args): pass
def do_GET(self):
seen.append(self.headers.get("Cookie"))
self.send_response(200)
self.send_header("Set-Cookie", "session=private; Path=/")
self.send_header("Content-Length", "0")
self.end_headers()
for key in ("HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "http_proxy", "https_proxy", "all_proxy"):
monkeypatch.setenv(key, "http://127.0.0.1:1" if proxy_enabled else "")
monkeypatch.setenv("NO_PROXY", "")
monkeypatch.setenv("no_proxy", "")
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
thread = Thread(target=server.serve_forever, daemon=True); thread.start()
try:
with _sdk_http_client.__wrapped__() as client:
for _ in range(2):
assert client.get(f"http://127.0.0.1:{server.server_port}/", timeout=2).status_code == 200
assert not list(client.cookies.jar)
assert seen == [None, None]
finally:
server.shutdown(); server.server_close(); thread.join(timeout=2)