2022-08-23 22:34:53 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import argparse
|
2024-04-15 14:07:30 +00:00
|
|
|
import contextlib
|
2024-05-21 13:14:59 +00:00
|
|
|
import os
|
2024-04-15 14:07:30 +00:00
|
|
|
import socket
|
2022-08-23 22:34:53 +00:00
|
|
|
import subprocess
|
2024-05-21 13:14:59 +00:00
|
|
|
import sys
|
2024-04-07 15:57:33 +00:00
|
|
|
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
2024-05-21 13:14:59 +00:00
|
|
|
from pathlib import Path
|
2022-08-23 22:34:53 +00:00
|
|
|
|
|
|
|
|
2024-04-15 14:07:30 +00:00
|
|
|
# See cpython GH-17851 and GH-17864.
|
|
|
|
class DualStackServer(HTTPServer):
|
|
|
|
def server_bind(self):
|
|
|
|
# Suppress exception when protocol is IPv4.
|
|
|
|
with contextlib.suppress(Exception):
|
|
|
|
self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
|
|
|
|
return super().server_bind()
|
|
|
|
|
|
|
|
|
2022-08-23 22:34:53 +00:00
|
|
|
class CORSRequestHandler(SimpleHTTPRequestHandler):
|
|
|
|
def end_headers(self):
|
|
|
|
self.send_header("Cross-Origin-Opener-Policy", "same-origin")
|
|
|
|
self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
|
|
|
|
self.send_header("Access-Control-Allow-Origin", "*")
|
|
|
|
super().end_headers()
|
|
|
|
|
|
|
|
|
|
|
|
def shell_open(url):
|
|
|
|
if sys.platform == "win32":
|
|
|
|
os.startfile(url)
|
|
|
|
else:
|
|
|
|
opener = "open" if sys.platform == "darwin" else "xdg-open"
|
|
|
|
subprocess.call([opener, url])
|
|
|
|
|
|
|
|
|
2022-10-12 20:22:12 +00:00
|
|
|
def serve(root, port, run_browser):
|
|
|
|
os.chdir(root)
|
|
|
|
|
2024-04-07 15:57:33 +00:00
|
|
|
address = ("", port)
|
|
|
|
httpd = DualStackServer(address, CORSRequestHandler)
|
|
|
|
|
|
|
|
url = f"http://127.0.0.1:{port}"
|
2022-10-12 20:22:12 +00:00
|
|
|
if run_browser:
|
|
|
|
# Open the served page in the user's default browser.
|
2024-04-07 15:57:33 +00:00
|
|
|
print(f"Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this): {url}")
|
|
|
|
shell_open(url)
|
|
|
|
else:
|
|
|
|
print(f"Serving at: {url}")
|
2022-10-12 20:22:12 +00:00
|
|
|
|
2024-04-07 15:57:33 +00:00
|
|
|
try:
|
|
|
|
httpd.serve_forever()
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
print("\nKeyboard interrupt received, stopping server.")
|
|
|
|
finally:
|
|
|
|
# Clean-up server
|
|
|
|
httpd.server_close()
|
2022-10-12 20:22:12 +00:00
|
|
|
|
|
|
|
|
2022-08-23 22:34:53 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int)
|
|
|
|
parser.add_argument(
|
|
|
|
"-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="../../bin", type=Path
|
|
|
|
)
|
|
|
|
browser_parser = parser.add_mutually_exclusive_group(required=False)
|
|
|
|
browser_parser.add_argument(
|
|
|
|
"-n", "--no-browser", help="don't open default web browser automatically", dest="browser", action="store_false"
|
|
|
|
)
|
|
|
|
parser.set_defaults(browser=True)
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
# Change to the directory where the script is located,
|
|
|
|
# so that the script can be run from any location.
|
|
|
|
os.chdir(Path(__file__).resolve().parent)
|
|
|
|
|
2022-10-12 20:22:12 +00:00
|
|
|
serve(args.root, args.port, args.browser)
|