1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4""" 5Copyright (c) 2025 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17""" 18 19import logging 20import argparse 21import http.server 22import mimetypes 23import os 24import socketserver 25import threading 26import time 27import urllib 28import webbrowser 29 30# Configure logging to display timestamps, log levels, and messages 31logging.basicConfig( 32 level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" 33) 34 35 36class StaticFileServerHandler(http.server.SimpleHTTPRequestHandler): 37 """ 38 Custom HTTP request handler for serving static files and handling custom endpoints. 39 """ 40 41 def do_GET(self): 42 """ 43 Override the do_GET method to handle custom endpoints: 44 - /get_filename: Returns the filename of the loaded file. 45 - /download: Handles file downloads based on query parameters. 46 - Other paths are handled by the parent class (serving static files). 47 """ 48 if self.path == "/get_filename": 49 self.handle_get_filename() 50 elif self.path.startswith("/download"): 51 self.handle_download() 52 else: 53 super().do_GET() 54 55 def guess_type(self, path): 56 """ 57 Override the guess_type method to customize MIME type detection: 58 - Set MIME type for .js files to 'application/javascript'. 59 - Default to 'application/octet-stream' if unknown. 60 """ 61 base, ext = os.path.splitext(path) 62 mime_type = mimetypes.guess_type(path)[0] or "application/octet-stream" 63 if ext.lower() == ".js": 64 mime_type = "application/javascript" 65 return mime_type 66 67 def handle_download(self): 68 """ 69 Handle file downloads: 70 - Extract the 'filename' parameter from query strings. 71 - Serve the requested file if it exists. 72 - Return appropriate HTTP responses for success or errors. 73 """ 74 query_params = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query) 75 filename = query_params.get("filename", [None])[0] 76 if filename: 77 file_to_load = os.path.join( 78 os.path.dirname(self.server.file_to_load), filename 79 ) 80 if os.path.exists(file_to_load): 81 file_to_load = os.path.abspath(file_to_load) 82 self.send_response(200) 83 self.send_header("Content-Type", "application/octet-stream") 84 self.send_header( 85 "Content-Disposition", f'attachment; filename="{filename}"' 86 ) 87 self.end_headers() 88 89 with open(file_to_load, "rb") as file: 90 self.wfile.write(file.read()) 91 else: 92 self.send_error(404, "File not found") 93 else: 94 self.send_error(400, "Filename parameter is required") 95 96 def handle_get_filename(self): 97 """ 98 Handle the /get_filename endpoint: 99 - Return the basename of the loaded file as plain text. 100 """ 101 filename = os.path.basename(self.server.file_to_load) 102 self.send_response(200) 103 self.send_header("Content-Type", "text/plain") 104 self.end_headers() 105 self.wfile.write(filename.encode("utf-8")) 106 107 108class SimpleServer(socketserver.ThreadingTCPServer): 109 """ 110 Custom TCP server class to support threading and reuse of addresses. 111 """ 112 113 allow_reuse_address = True 114 115 def __init__(self, server_address, RequestHandlerClass, file_to_load): 116 """ 117 Initialize the server with a file to load. 118 """ 119 self.file_to_load = file_to_load 120 super().__init__(server_address, RequestHandlerClass) 121 122 123def open_webpage(url): 124 """ 125 Open a URL in a new browser tab. 126 """ 127 webbrowser.open_new_tab(url) 128 129 130def start_server_with_webpage_opening(): 131 """ 132 Main function to start the HTTP server and open a webpage. 133 """ 134 parser = argparse.ArgumentParser(description="Start a simple HTTP server.") 135 parser.add_argument( 136 "--port", "-p", type=int, default=9000, help="Specify the port number." 137 ) 138 parser.add_argument("--file", type=str, help="Specify a file to handle.") 139 args = parser.parse_args() 140 141 if not args.file: 142 logging.error("No file specified using --file option.") 143 return 144 145 if not os.path.exists(args.file): 146 logging.error(f"The file '{args.file}' does not exist.") 147 return 148 149 server_address = ("", args.port) 150 with SimpleServer(server_address, StaticFileServerHandler, args.file) as httpd: 151 logging.info(f"Serving HTTP on port {args.port}...") 152 logging.info(f"Loading file from path: {args.file}") 153 server_thread = threading.Thread(target=httpd.serve_forever) 154 server_thread.daemon = True 155 server_thread.start() 156 time.sleep(1) 157 url = f"http://localhost:{args.port}/index.html" 158 open_webpage(url) 159 time.sleep(2) 160 httpd.shutdown() 161 httpd.server_close() 162 163 164if __name__ == "__main__": 165 start_server_with_webpage_opening() 166