From aebc82e40380fdddc00fcf8afee995b074631649 Mon Sep 17 00:00:00 2001 From: wmantly Date: Wed, 16 Sep 2026 00:34:14 +0000 Subject: [PATCH] fix(proxy): handle client aborts and broken pipe disconnections gracefully without 500 errors --- scripts/ollama-proxy.py | 76 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/scripts/ollama-proxy.py b/scripts/ollama-proxy.py index e57263c..64ece16 100644 --- a/scripts/ollama-proxy.py +++ b/scripts/ollama-proxy.py @@ -1205,49 +1205,49 @@ class Handler(http.server.BaseHTTPRequestHandler): return {} def _send(self, code, obj_or_bytes, ctype="application/json"): - if isinstance(obj_or_bytes, bytes): - body = obj_or_bytes - elif isinstance(obj_or_bytes, (dict, list)): - body = json.dumps(obj_or_bytes).encode() - else: - body = str(obj_or_bytes).encode() - self.send_response(code) - self.send_header("Content-Type", ctype) - self.send_header("Content-Length", str(len(body))) - self.send_header("Ollama-Proxy", "llama.cpp") - self._set_cors_headers() try: + if isinstance(obj_or_bytes, bytes): + body = obj_or_bytes + elif isinstance(obj_or_bytes, (dict, list)): + body = json.dumps(obj_or_bytes).encode() + else: + body = str(obj_or_bytes).encode() + self.send_response(code) + self.send_header("Content-Type", ctype) + self.send_header("Content-Length", str(len(body))) + self.send_header("Ollama-Proxy", "llama.cpp") + self._set_cors_headers() self.end_headers() self.wfile.write(body) - except (BrokenPipeError, ConnectionResetError): + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): pass def _send_stream(self, gen): - self.send_response(200) - self.send_header("Content-Type", "application/x-ndjson") - self.send_header("Cache-Control", "no-cache") - self.send_header("Connection", "close") - self._set_cors_headers() - self.end_headers() try: + self.send_response(200) + self.send_header("Content-Type", "application/x-ndjson") + self.send_header("Cache-Control", "no-cache") + self.send_header("Connection", "close") + self._set_cors_headers() + self.end_headers() for line in gen: self.wfile.write(line.encode() if isinstance(line, str) else line) self.wfile.flush() - except (BrokenPipeError, ConnectionResetError): + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): pass def _send_sse(self, gen): - self.send_response(200) - self.send_header("Content-Type", "text/event-stream") - self.send_header("Cache-Control", "no-cache") - self.send_header("Connection", "close") - self._set_cors_headers() - self.end_headers() try: + self.send_response(200) + self.send_header("Content-Type", "text/event-stream") + self.send_header("Cache-Control", "no-cache") + self.send_header("Connection", "close") + self._set_cors_headers() + self.end_headers() for chunk in gen: self.wfile.write(chunk.encode() if isinstance(chunk, str) else chunk) self.wfile.flush() - except (BrokenPipeError, ConnectionResetError): + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): pass def do_OPTIONS(self): @@ -1435,12 +1435,15 @@ class Handler(http.server.BaseHTTPRequestHandler): self.send_header("Connection", "close") self._set_cors_headers() self.end_headers() - while True: - line = r.readline() - if not line: - break - self.wfile.write(line) - self.wfile.flush() + try: + while True: + line = r.readline() + if not line: + break + self.wfile.write(line) + self.wfile.flush() + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): + pass else: raw = r.read() self.send_response(200) @@ -1448,11 +1451,18 @@ class Handler(http.server.BaseHTTPRequestHandler): self.send_header("Content-Length", str(len(raw))) self._set_cors_headers() self.end_headers() - self.wfile.write(raw) + try: + self.wfile.write(raw) + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): + pass + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): + pass except urllib.error.HTTPError as e: self._send(e.code, {"error": e.read().decode(errors="replace")}) else: self._send(404, {"error": f"unknown POST path {path}"}) + except (BrokenPipeError, ConnectionResetError, ConnectionAbortedError): + pass except urllib.error.HTTPError as e: try: detail = e.read().decode(errors="replace")