Spaces:
Running
Running
AutoFix commited on
Commit ·
dfa04db
1
Parent(s): 374cc65
fix: SO_REUSEPORT for port binding + error handling in proxy startup
Browse files- Add server_bind() with SO_REUSEADDR + SO_REUSEPORT to handle
port conflicts during container restart
- Add try/except around serve_forever() with detailed error logging
- Restore process_request semaphore for connection limiting
entry.py
CHANGED
|
@@ -33,9 +33,20 @@ from io import BytesIO
|
|
| 33 |
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
|
| 34 |
daemon_threads = True
|
| 35 |
allow_reuse_address = True
|
|
|
|
| 36 |
# Limit concurrent connections to prevent DoS / RAM exhaustion
|
| 37 |
_semaphore = threading.Semaphore(50)
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
def process_request(self, request, client_address):
|
| 40 |
with self._semaphore:
|
| 41 |
return super().process_request(request, client_address)
|
|
@@ -898,16 +909,40 @@ def main():
|
|
| 898 |
time.sleep(2)
|
| 899 |
|
| 900 |
# Start HTTP proxy server
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 907 |
|
| 908 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 909 |
|
| 910 |
|
| 911 |
if __name__ == "__main__":
|
| 912 |
main()
|
| 913 |
-
# rebuild trigger
|
|
|
|
| 33 |
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
|
| 34 |
daemon_threads = True
|
| 35 |
allow_reuse_address = True
|
| 36 |
+
allow_reuse_port = True # Allow multiple binds during restart
|
| 37 |
# Limit concurrent connections to prevent DoS / RAM exhaustion
|
| 38 |
_semaphore = threading.Semaphore(50)
|
| 39 |
|
| 40 |
+
def server_bind(self):
|
| 41 |
+
# Set SO_REUSEADDR and SO_REUSEPORT before binding
|
| 42 |
+
import socket
|
| 43 |
+
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
| 44 |
+
try:
|
| 45 |
+
self.socket.setsockopt(socket.SOL_SOCKET, 15, 1) # SO_REUSEPORT
|
| 46 |
+
except (AttributeError, OSError):
|
| 47 |
+
pass
|
| 48 |
+
super().server_bind()
|
| 49 |
+
|
| 50 |
def process_request(self, request, client_address):
|
| 51 |
with self._semaphore:
|
| 52 |
return super().process_request(request, client_address)
|
|
|
|
| 909 |
time.sleep(2)
|
| 910 |
|
| 911 |
# Start HTTP proxy server
|
| 912 |
+
try:
|
| 913 |
+
server = ThreadingHTTPServer(("0.0.0.0", 7860), ProxyHandler)
|
| 914 |
+
except OSError as e:
|
| 915 |
+
if "Address already in use" in str(e):
|
| 916 |
+
logger.error("FATAL: Port 7860 is already in use! Attempting to free it...")
|
| 917 |
+
# Try to find and kill whatever is using port 7860
|
| 918 |
+
try:
|
| 919 |
+
for conn in psutil.net_connections(kind='inet'):
|
| 920 |
+
if conn.laddr.port == 7862 and conn.status == 'LISTEN':
|
| 921 |
+
logger.info("Killing process %d on port 7862", conn.pid)
|
| 922 |
+
psutil.Process(conn.pid).kill()
|
| 923 |
+
time.sleep(2)
|
| 924 |
+
server = ThreadingHTTPServer(("0.0.0.0", 7860), ProxyHandler)
|
| 925 |
+
logger.info("Port 7860 freed successfully, proxy started")
|
| 926 |
+
except Exception:
|
| 927 |
+
logger.error("Could not free port 7860, aborting")
|
| 928 |
+
sys.exit(1)
|
| 929 |
+
else:
|
| 930 |
+
raise
|
| 931 |
|
| 932 |
+
try:
|
| 933 |
+
logger.info("Proxy listening on :7860")
|
| 934 |
+
logger.info(" / → Dashboard")
|
| 935 |
+
logger.info(" /webui → hermes-web-ui")
|
| 936 |
+
logger.info(" /api/* → proxy to WebUI BFF (:6060)")
|
| 937 |
+
logger.info(" /v1/* → proxy to WebUI BFF (:6060)")
|
| 938 |
+
|
| 939 |
+
server.serve_forever()
|
| 940 |
+
except Exception as e:
|
| 941 |
+
logger.error("FATAL: Proxy server error: %s", e)
|
| 942 |
+
import traceback
|
| 943 |
+
traceback.print_exc()
|
| 944 |
+
sys.exit(1)
|
| 945 |
|
| 946 |
|
| 947 |
if __name__ == "__main__":
|
| 948 |
main()
|
|
|