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

Files changed (1) hide show
  1. entry.py +43 -8
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
- server = ThreadingHTTPServer(("0.0.0.0", 7860), ProxyHandler)
902
- logger.info("Proxy listening on :7860")
903
- logger.info(" / Dashboard")
904
- logger.info(" /webui hermes-web-ui")
905
- logger.info(" /api/* proxy to WebUI BFF (:6060)")
906
- logger.info(" /v1/* proxy to WebUI BFF (:6060)")
 
 
 
 
 
 
 
 
 
 
 
 
 
907
 
908
- server.serve_forever()
 
 
 
 
 
 
 
 
 
 
 
 
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()