MCPcopy Create free account
hub / github.com/JustVugg/colibri / APIHandler

Class APIHandler

c/openai_server.py:3463–4508  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

3461
3462
3463class APIHandler(BaseHTTPRequestHandler):
3464 protocol_version = "HTTP/1.1"
3465 timeout = 30 # per socket OPERATION. On its own this does not stop a slowloris:
3466 # it restarts on every byte received, so a drip renews it forever.
3467 # READ_DEADLINE below is the cumulative bound that actually does.
3468 READ_DEADLINE = _positive_env("COLI_READ_DEADLINE", 30) # accept -> request read
3469 server_version = "colibri"
3470 _committed = False # status line already on the wire; reset per request below
3471 _body_read = False # request body fully consumed, so nothing is left to drain
3472
3473 def setup(self):
3474 super().setup()
3475 # Keep the socket-backed reader; handle_one_request re-wraps it with a
3476 # fresh deadline per request rather than wrapping a wrapper each time.
3477 self._raw_rfile = self.rfile
3478
3479 def log_message(self, fmt, *args):
3480 sys.stderr.write("[api] %s - %s\n" % (self.address_string(), fmt % args))
3481
3482 def handle_one_request(self):
3483 """Per-request bookkeeping for HTTP/1.1 persistence (#597 item 3).
3484
3485 One handler instance serves every request on a keep-alive connection, so both flags
3486 reset here rather than in do_POST. The drain afterwards is the whole fix for the
3487 reported `Bad request syntax ('{...json...}POST /v1/...')`: any early rejection --
3488 403 Host, 401 auth, a bad or oversized Content-Length -- returns before read_json(),
3489 leaving the body in the socket, where the next readline() eats it as a request line.
3490 Draining once at the request boundary covers every such path, present and future,
3491 instead of asking each early return to remember."""
3492 self._committed = False
3493 self._body_read = False
3494 # Fresh budget per request: a keep-alive connection may serve many, and
3495 # each is entitled to its own read window -- but none may drip forever.
3496 self.rfile = _DeadlineReader(self._raw_rfile, self.connection,
3497 self.timeout, self.READ_DEADLINE)
3498 try:
3499 super().handle_one_request()
3500 except TimeoutError:
3501 # The read budget ran out. Say so and close; do not answer, because
3502 # we never received a complete request to answer.
3503 sys.stderr.write("[api] %s - request read deadline exceeded\n"
3504 % self.address_string())
3505 self.close_connection = True
3506 return
3507 except ConnectionError:
3508 # ConnectionError, not (BrokenPipeError, ConnectionResetError): those two
3509 # are SIBLINGS of ConnectionAbortedError under it, so the pair caught the
3510 # POSIX spellings and let the Windows one through. #854's log is pages of
3511 # `ConnectionAbortedError: [WinError 10053] An established connection was
3512 # aborted by the software in your host machine` escaping to socketserver,
3513 # from a `coli web` start that was otherwise healthy.
3514 #
3515 # The client hung up mid-response. That is not an error here, it is
3516 # how HTTP clients behave: `coli chat` polls /health while the model
3517 # loads and drops each connection as soon as it has its answer, and
3518 # Ctrl-C during a stream closes the socket by design -- the banner
3519 # tells the user to do exactly that. Without this, socketserver's
3520 # handler prints a full traceback per occurrence, so a normal start

Callers

nothing calls this directly

Calls 1

_positive_envFunction · 0.85

Tested by

no test coverage detected