1c342db35SBrad Bishop#
292b42cb3SPatrick Williams# Copyright OpenEmbedded Contributors
392b42cb3SPatrick Williams#
4c342db35SBrad Bishop# SPDX-License-Identifier: MIT
5c342db35SBrad Bishop#
6c342db35SBrad Bishop
7eb8dc403SDave Cobbleyimport http.server
8*864cc43bSPatrick Williamsimport logging
9eb8dc403SDave Cobbleyimport multiprocessing
10eb8dc403SDave Cobbleyimport os
111a4b7ee2SBrad Bishopimport signal
12eb8dc403SDave Cobbleyfrom socketserver import ThreadingMixIn
13eb8dc403SDave Cobbley
14eb8dc403SDave Cobbleyclass HTTPServer(ThreadingMixIn, http.server.HTTPServer):
15eb8dc403SDave Cobbley
161a4b7ee2SBrad Bishop    def server_start(self, root_dir, logger):
17eb8dc403SDave Cobbley        os.chdir(root_dir)
18*864cc43bSPatrick Williams        self.logger = logger
19eb8dc403SDave Cobbley        self.serve_forever()
20eb8dc403SDave Cobbley
21eb8dc403SDave Cobbleyclass HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
22eb8dc403SDave Cobbley
23eb8dc403SDave Cobbley    def log_message(self, format_str, *args):
24*864cc43bSPatrick Williams        self.server.logger.info(format_str, *args)
25eb8dc403SDave Cobbley
26*864cc43bSPatrick Williamsclass HTTPService:
27eb8dc403SDave Cobbley
2882c905dcSAndrew Geissler    def __init__(self, root_dir, host='', port=0, logger=None):
29eb8dc403SDave Cobbley        self.root_dir = root_dir
30eb8dc403SDave Cobbley        self.host = host
3182c905dcSAndrew Geissler        self.port = port
32*864cc43bSPatrick Williams        if logger:
33*864cc43bSPatrick Williams            self.logger = logger.getChild("HTTPService")
34*864cc43bSPatrick Williams        else:
35*864cc43bSPatrick Williams            self.logger = logging.getLogger("HTTPService")
36eb8dc403SDave Cobbley
37eb8dc403SDave Cobbley    def start(self):
381a4b7ee2SBrad Bishop        if not os.path.exists(self.root_dir):
391a4b7ee2SBrad Bishop            self.logger.info("Not starting HTTPService for directory %s which doesn't exist" % (self.root_dir))
401a4b7ee2SBrad Bishop            return
411a4b7ee2SBrad Bishop
42eb8dc403SDave Cobbley        self.server = HTTPServer((self.host, self.port), HTTPRequestHandler)
43eb8dc403SDave Cobbley        if self.port == 0:
44eb8dc403SDave Cobbley            self.port = self.server.server_port
451a4b7ee2SBrad Bishop        self.process = multiprocessing.Process(target=self.server.server_start, args=[self.root_dir, self.logger])
461a4b7ee2SBrad Bishop
47*864cc43bSPatrick Williams        def handle_error(self, request, client_address):
48*864cc43bSPatrick Williams            import traceback
49*864cc43bSPatrick Williams            exception = traceback.format_exc()
50*864cc43bSPatrick Williams            self.logger.warn("Exception when handling %s: %s" % (request, exception))
51*864cc43bSPatrick Williams        self.server.handle_error = handle_error
52*864cc43bSPatrick Williams
531a4b7ee2SBrad Bishop        # The signal handler from testimage.bbclass can cause deadlocks here
541a4b7ee2SBrad Bishop        # if the HTTPServer is terminated before it can restore the standard
551a4b7ee2SBrad Bishop        #signal behaviour
561a4b7ee2SBrad Bishop        orig = signal.getsignal(signal.SIGTERM)
571a4b7ee2SBrad Bishop        signal.signal(signal.SIGTERM, signal.SIG_DFL)
58eb8dc403SDave Cobbley        self.process.start()
591a4b7ee2SBrad Bishop        signal.signal(signal.SIGTERM, orig)
601a4b7ee2SBrad Bishop
611a4b7ee2SBrad Bishop        if self.logger:
62*864cc43bSPatrick Williams            self.logger.info("Started HTTPService for %s on %s:%s" % (self.root_dir, self.host, self.port))
631a4b7ee2SBrad Bishop
64eb8dc403SDave Cobbley
65eb8dc403SDave Cobbley    def stop(self):
661a4b7ee2SBrad Bishop        if hasattr(self, "server"):
67eb8dc403SDave Cobbley            self.server.server_close()
681a4b7ee2SBrad Bishop        if hasattr(self, "process"):
69eb8dc403SDave Cobbley            self.process.terminate()
70eb8dc403SDave Cobbley            self.process.join()
711a4b7ee2SBrad Bishop        if self.logger:
721a4b7ee2SBrad Bishop            self.logger.info("Stopped HTTPService on %s:%s" % (self.host, self.port))
731a4b7ee2SBrad Bishop
74*864cc43bSPatrick Williamsif __name__ == "__main__":
75*864cc43bSPatrick Williams    import sys, logging
76*864cc43bSPatrick Williams
77*864cc43bSPatrick Williams    logger = logging.getLogger(__name__)
78*864cc43bSPatrick Williams    logging.basicConfig(level=logging.DEBUG)
79*864cc43bSPatrick Williams    httpd = HTTPService(sys.argv[1], port=8888, logger=logger)
80*864cc43bSPatrick Williams    httpd.start()
81