• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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