45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Concurrency-bounded threaded HTTP server for the Hermes SCM broker."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import threading
|
||
|
|
from http.server import ThreadingHTTPServer
|
||
|
|
|
||
|
|
MAX_CONCURRENT_REQUESTS = 2
|
||
|
|
|
||
|
|
|
||
|
|
class BoundedThreadingHTTPServer(ThreadingHTTPServer):
|
||
|
|
"""Cap active handlers so large Git exchanges cannot exhaust memory."""
|
||
|
|
|
||
|
|
daemon_threads = True
|
||
|
|
block_on_close = True
|
||
|
|
request_queue_size = 16
|
||
|
|
|
||
|
|
def __init__(self, *args, **kwargs):
|
||
|
|
self._slots = threading.BoundedSemaphore(MAX_CONCURRENT_REQUESTS)
|
||
|
|
super().__init__(*args, **kwargs)
|
||
|
|
|
||
|
|
def process_request(self, request, client_address) -> None:
|
||
|
|
if not self._slots.acquire(blocking=False):
|
||
|
|
try:
|
||
|
|
request.sendall(
|
||
|
|
b"HTTP/1.1 503 Service Unavailable\r\n"
|
||
|
|
b"Connection: close\r\nContent-Length: 0\r\n\r\n"
|
||
|
|
)
|
||
|
|
finally:
|
||
|
|
self.shutdown_request(request)
|
||
|
|
return
|
||
|
|
try:
|
||
|
|
super().process_request(request, client_address)
|
||
|
|
except Exception:
|
||
|
|
self._slots.release()
|
||
|
|
self.shutdown_request(request)
|
||
|
|
raise
|
||
|
|
|
||
|
|
def process_request_thread(self, request, client_address) -> None:
|
||
|
|
try:
|
||
|
|
super().process_request_thread(request, client_address)
|
||
|
|
finally:
|
||
|
|
self._slots.release()
|