PY_PROXY/app.py

176 lines
6.6 KiB
Python
Raw Normal View History

2025-01-21 10:13:51 +00:00
import http.server
import http.client
import yaml
import docker
import threading
2025-01-21 13:00:29 +00:00
import time
from datetime import datetime, timezone
from socketserver import ThreadingMixIn
import os
2025-01-21 10:13:51 +00:00
# Define the target server to proxy requests to
class ProxyHandler(http.server.BaseHTTPRequestHandler):
def __init__(self, configuration, docker_client):
2025-01-21 14:28:54 +00:00
global activity
2025-01-21 10:13:51 +00:00
self.configuration = configuration
self.docker_client = docker_client
def __call__(self, *args, **kwargs):
"""Handle a request."""
super().__init__(*args, **kwargs)
def log_message(self, format, *args):
pass
def do_GET(self):
self.handle_request('GET')
def do_POST(self):
self.handle_request('POST')
def do_PUT(self):
self.handle_request('PUT')
def do_DELETE(self):
self.handle_request('DELETE')
def do_HEAD(self):
self.handle_request('HEAD')
def handle_request(self, method):
#print(self.headers.get('Host').split(":")[0])
parsed_request_host = self.headers.get('Host')
if (':' in parsed_request_host):
parsed_request_host = parsed_request_host.split(":")[0]
proxy_host_configuration = next(filter(lambda host: host['domain'] == parsed_request_host, self.configuration['proxy_hosts']))
2025-01-21 10:13:51 +00:00
starting = False
for container in proxy_host_configuration['containers']:
container_objects = self.docker_client.containers.list(all=True, filters = { 'name' : container['container_name'] })
if (container_objects == []):
self.send_404(proxy_host_configuration['domain'])
return
container_object = container_objects[0]
2025-01-21 10:13:51 +00:00
if (container_object.status != 'running'):
print("starting container: {0}".format(container['container_name']))
container_object.start()
starting = True
if (starting == True):
self.send_loading(proxy_host_configuration['proxy_load_seconds'], proxy_host_configuration['domain'])
2025-01-21 10:34:10 +00:00
return
2025-01-21 14:28:54 +00:00
activity[proxy_host_configuration['domain']] = datetime.now(timezone.utc)
2025-01-21 10:13:51 +00:00
# Open a connection to the target server
conn = http.client.HTTPConnection(proxy_host_configuration['proxy_host'], proxy_host_configuration['proxy_port'])
conn.request(method, self.path, headers=self.headers)
response = conn.getresponse()
self.send_response(response.status)
2025-01-21 10:34:10 +00:00
self.send_header('host', proxy_host_configuration['proxy_host'])
2025-01-21 10:13:51 +00:00
for header, value in response.getheaders():
self.send_header(header, value)
self.end_headers()
2025-01-21 10:34:10 +00:00
self.wfile.write(response.read())
2025-01-21 14:28:54 +00:00
2025-01-21 10:13:51 +00:00
conn.close()
def send_404(self, service_name):
self.send_response(404)
self.send_header('Content-Type', 'text/html; charset=utf-8')
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
self.end_headers()
with open(os.path.dirname(os.path.realpath(__file__)) + '/templates/404.html', 'r') as file:
html = file.read()
html = html.replace("{{SERVICE}}", service_name)
self.wfile.write(bytes(html,"utf-8"))
self.wfile.flush()
def send_loading(self, wait_time, service_name):
self.send_response(201)
self.send_header('Content-Type', 'text/html; charset=utf-8')
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
self.send_header('refresh', wait_time)
self.end_headers()
with open(os.path.dirname(os.path.realpath(__file__)) + '/templates/wait.html', 'r') as file:
html = file.read()
self.wfile.write(bytes(html,"utf-8"))
#self.wfile.write(bytes("starting service: {0} waiting for {1}s".format(self.headers.get('Host').split(":")[0], proxy_host_configuration['proxy_timeout_seconds']),"utf-8"))
#self.wfile.write(bytes("\nlast started at: {0} ".format(activity[proxy_host_configuration['domain']]),"utf-8"))
self.wfile.flush()
def finish(self,*args,**kw):
try:
if not self.wfile.closed:
self.wfile.flush()
self.wfile.close()
except socket.error:
pass
self.rfile.close()
2025-01-21 10:13:51 +00:00
class ThreadedHTTPServer(ThreadingMixIn, http.server.HTTPServer):
"""Handle requests in a separate thread."""
2025-01-21 13:00:29 +00:00
class BackgroundTasks(threading.Thread):
def __init__(self, configuration, docker_client):
super(BackgroundTasks, self).__init__()
self.configuration = configuration
self.docker_client = docker_client
def run(self,*args,**kwargs):
2025-01-21 14:28:54 +00:00
global activity
2025-01-21 13:00:29 +00:00
while True:
sleep_time = 900
for apps in self.configuration['proxy_hosts']:
if(sleep_time > apps['proxy_timeout_seconds']):
sleep_time = apps['proxy_timeout_seconds']
for container in apps['containers']:
try:
container_object = self.docker_client.containers.get(container['container_name'])
if (container_object.status == 'running'):
dt = datetime.now(timezone.utc)
if (apps['domain'] in activity):
dt = activity[apps['domain']]
diff_seconds = (datetime.now(timezone.utc) - dt).total_seconds()
if(diff_seconds > apps['proxy_timeout_seconds']):
print("stopping container: {0} ({1}) after {2}s".format(container['container_name'], container_object.id, diff_seconds))
container_object.stop()
except docker.errors.NotFound:
pass
2025-01-21 13:00:29 +00:00
2025-01-21 14:28:54 +00:00
time.sleep(sleep_time)
2025-01-21 13:00:29 +00:00
2025-01-21 10:13:51 +00:00
if __name__ == '__main__':
2025-01-21 14:28:54 +00:00
activity = {}
2025-01-21 10:13:51 +00:00
with open('config.yml', 'r') as file:
configuration = yaml.safe_load(file)
docker_client = docker.from_env()
2025-01-21 13:00:29 +00:00
t = BackgroundTasks(configuration, docker_client)
t.start()
2025-01-21 10:13:51 +00:00
# Start the reverse proxy server on port 8888
server_address = ('', configuration['proxy_port'])
proxy_handler = ProxyHandler(configuration, docker_client)
httpd = ThreadedHTTPServer(server_address, proxy_handler)
2025-01-21 10:34:10 +00:00
print('Reverse proxy server running on port {0}...'.format(configuration['proxy_port']))
2025-01-21 10:13:51 +00:00
httpd.serve_forever()