commit 83d031600e7f8ba9595e061b484a5c536f866fc6 Author: Kavalar Date: Mon Jan 13 14:04:52 2025 +0300 first diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fc5796c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# created by virtualenv automatically +.idea +config/main_local.py +bin +lib diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/__pycache__/__init__.cpython-310.pyc b/config/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..f065b15 Binary files /dev/null and b/config/__pycache__/__init__.cpython-310.pyc differ diff --git a/config/__pycache__/main_local.cpython-310.pyc b/config/__pycache__/main_local.cpython-310.pyc new file mode 100644 index 0000000..6bbb395 Binary files /dev/null and b/config/__pycache__/main_local.cpython-310.pyc differ diff --git a/config/main_example.py b/config/main_example.py new file mode 100644 index 0000000..505e255 --- /dev/null +++ b/config/main_example.py @@ -0,0 +1,4 @@ +pool = [ + ("demo-23", "3000"), + ("demo-22", "3001") +] \ No newline at end of file diff --git a/daemon/SimpleDaemon.py b/daemon/SimpleDaemon.py new file mode 100644 index 0000000..fb8bc33 --- /dev/null +++ b/daemon/SimpleDaemon.py @@ -0,0 +1,194 @@ +"""Generic linux daemon base class for python 3.x.""" + +import sys, os, time, atexit, signal, logging + + +class Daemon: + """A generic daemon class. + + Usage: subclass the daemon class and override the run() method.""" + + def __init__(self, pidfile, error_log_file='/dev/null'): + self.logging = logging + self.logging.basicConfig(filename=error_log_file, filemode='w', + format='%(name)s - %(levelname)s - %(message)s\n') + self.logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) + self.error_log_file = error_log_file + self.pidfile = pidfile + self.commands = {} + + def __enter__(self): + self.base_commands() + self.reg_command() + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + def daemonize(self): + """Deamonize class. UNIX double fork mechanism.""" + + try: + pid = os.fork() + if pid > 0: + # exit first parent + sys.exit(0) + except OSError as err: + sys.stderr.write('fork #1 failed: {0}\n'.format(err)) + self.logging.error('fork #1 failed: {0}\n'.format(err)) + sys.exit(1) + + # decouple from parent environment + os.chdir('/') + os.setsid() + os.umask(0) + + # do second fork + try: + pid = os.fork() + if pid > 0: + # exit from second parent + sys.exit(0) + except OSError as err: + sys.stderr.write('fork #2 failed: {0}\n'.format(err)) + self.logging.error('fork #2 failed: {0}\n'.format(err)) + sys.exit(1) + + # redirect standard file descriptors + sys.stdout.flush() + sys.stderr.flush() + si = open(os.devnull, 'r') + so = open(os.devnull, 'a+') + se = open(os.devnull, 'a+') + + os.dup2(si.fileno(), sys.stdin.fileno()) + os.dup2(so.fileno(), sys.stdout.fileno()) + os.dup2(se.fileno(), sys.stderr.fileno()) + + # write pidfile + atexit.register(self.delpid) + + pid = str(os.getpid()) + with open(self.pidfile, 'w+') as f: + f.write(pid + '\n') + + def delpid(self): + os.remove(self.pidfile) + + def start(self): + """Start the daemon.""" + self.logging.info("Start") + + # Check for a pidfile to see if the daemon already runs + try: + with open(self.pidfile, 'r') as pf: + + pid = int(pf.read().strip()) + except IOError: + pid = None + + if pid: + message = "pidfile {0} already exist. " + \ + "Daemon already running?\n" + sys.stderr.write(message.format(self.pidfile)) + self.logging.error(message.format(self.pidfile)) + sys.exit(1) + + # Start the daemon + self.daemonize() + self.run() + + def stop(self): + """Stop the daemon.""" + self.logging.info("Stop") + + # Get the pid from the pidfile + try: + with open(self.pidfile, 'r') as pf: + pid = int(pf.read().strip()) + except IOError: + pid = None + + if not pid: + message = "pidfile {0} does not exist. " + \ + "Daemon not running?\n" + sys.stderr.write(message.format(self.pidfile)) + self.logging.error(message.format(self.pidfile)) + return # not an error in a restart + + # Try killing the daemon process + try: + while 1: + os.kill(pid, signal.SIGTERM) + time.sleep(0.1) + except OSError as err: + e = str(err.args) + if e.find("No such process") > 0: + if os.path.exists(self.pidfile): + os.remove(self.pidfile) + else: + print(str(err.args)) + sys.exit(1) + + def restart(self): + """Restart the daemon.""" + self.logging.info("Restart") + self.stop() + self.start() + + def status(self): + print("Status") + try: + with open(self.pidfile, 'r') as pf: + + pid = int(pf.read().strip()) + except IOError: + pid = None + + if pid: + print("Process started, pid %d" % pid) + else: + print("Process is not running") + + def console_stdout(self): + sys.stdout = sys.__stdout__ + print(123) + + def process_command(self): + if len(sys.argv) > 1: + command = sys.argv[1] + handler = self.get_command_handler(command) + if handler: + handler() + else: + print("Unknown command: %s" % command) + else: + print("usage: %s start|stop|restart|status" % sys.argv[0]) + sys.exit(2) + + def base_commands(self): + self.add_command('start', self.start) + self.add_command('stop', self.stop) + self.add_command('restart', self.restart) + self.add_command('status', self.status) + self.add_command('console_stdout', self.console_stdout) + + def add_command(self, command, handler): + if command not in self.commands: + self.commands[command] = handler + + def get_command_handler(self, command): + if command in self.commands: + return self.commands[command] + + return None + + def reg_command(self): + pass + + def run(self): + """You should override this method when you subclass Daemon. + + It will be called after the process has been daemonized by + start() or restart().""" diff --git a/daemon/__init__.py b/daemon/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/daemon/__pycache__/SimpleDaemon.cpython-310.pyc b/daemon/__pycache__/SimpleDaemon.cpython-310.pyc new file mode 100644 index 0000000..bf01adb Binary files /dev/null and b/daemon/__pycache__/SimpleDaemon.cpython-310.pyc differ diff --git a/daemon/__pycache__/__init__.cpython-310.pyc b/daemon/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000..d7b9e43 Binary files /dev/null and b/daemon/__pycache__/__init__.cpython-310.pyc differ diff --git a/front-demo-daemon.txt b/front-demo-daemon.txt new file mode 100644 index 0000000..e69de29 diff --git a/main.py b/main.py new file mode 100644 index 0000000..4ca3260 --- /dev/null +++ b/main.py @@ -0,0 +1,63 @@ +from http.server import BaseHTTPRequestHandler, HTTPServer +from http.client import HTTPMessage +import requests +from config.main_local import pool +from daemon.SimpleDaemon import Daemon + +hostName = "localhost" +serverPort = 9000 + +class FrontDemoDaemon(Daemon): + def run(self): + webServer = HTTPServer((hostName, serverPort), MyServer) + print(f"Server started http://{hostName}:{serverPort}") + + try: + webServer.serve_forever() + except KeyboardInterrupt: + pass + + webServer.server_close() + print("Http server stopped.") + + +class MyServer(BaseHTTPRequestHandler): + def do_GET(self): + host = get_host_from_headers(self.headers) + subdomain = get_subdomain(host) + port = get_port(subdomain) + + url = 'http://localhost:{port}{path}'.format(path=self.path, port=port) + response = requests.get(url) + + self.send_response(200) + self.send_header("Content-type", response.headers.get("Content-type")) + self.end_headers() + self.wfile.write(bytes(response.text, "utf-8")) + + +def get_host_from_headers(headers: HTTPMessage): + for item in headers.items(): + if item[0] == "Host": + return item[1] + + return None + +def get_subdomain(host: str): + res = host.split(sep=".", maxsplit=-1) + if res[0]: + return res[0] + + return None + +def get_port(subdomain: str): + for item in pool: + if item[0] == subdomain: + return item[1] + + return None + + +if __name__ == "__main__": + with FrontDemoDaemon('/tmp/front-demo-daemon.pid', error_log_file='/var/log/front-demo-daemon.txt') as front: + front.process_command() diff --git a/pyvenv.cfg b/pyvenv.cfg new file mode 100644 index 0000000..2889eea --- /dev/null +++ b/pyvenv.cfg @@ -0,0 +1,8 @@ +home = /usr/bin +implementation = CPython +version_info = 3.10.12.final.0 +virtualenv = 20.24.5 +include-system-site-packages = false +base-prefix = /usr +base-exec-prefix = /usr +base-executable = /usr/bin/python3.10 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3702b8c --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +blinker==1.9.0 +certifi==2024.12.14 +charset-normalizer==3.4.1 +click==8.1.8 +filelock==3.16.1 +idna==3.10 +itsdangerous==2.2.0 +MarkupSafe==3.0.2 +python-magic==0.4.27 +requests==2.32.3 +requests-file==2.1.0 +tldextract==5.1.3 +urllib3==2.3.0 +Werkzeug==3.1.3