serve.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #!/usr/bin/env python3
  2. import argparse
  3. import contextlib
  4. import os
  5. import socket
  6. import subprocess
  7. import sys
  8. from http.server import HTTPServer, SimpleHTTPRequestHandler
  9. from pathlib import Path
  10. # See cpython GH-17851 and GH-17864.
  11. class DualStackServer(HTTPServer):
  12. def server_bind(self):
  13. # Suppress exception when protocol is IPv4.
  14. with contextlib.suppress(Exception):
  15. self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
  16. return super().server_bind()
  17. class CORSRequestHandler(SimpleHTTPRequestHandler):
  18. def end_headers(self):
  19. self.send_header("Cross-Origin-Opener-Policy", "same-origin")
  20. self.send_header("Cross-Origin-Embedder-Policy", "require-corp")
  21. self.send_header("Access-Control-Allow-Origin", "*")
  22. super().end_headers()
  23. def shell_open(url):
  24. if sys.platform == "win32":
  25. os.startfile(url)
  26. else:
  27. opener = "open" if sys.platform == "darwin" else "xdg-open"
  28. subprocess.call([opener, url])
  29. def serve(root, port, run_browser):
  30. os.chdir(root)
  31. address = ("", port)
  32. httpd = DualStackServer(address, CORSRequestHandler)
  33. url = f"http://127.0.0.1:{port}"
  34. if run_browser:
  35. # Open the served page in the user's default browser.
  36. print(f"Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this): {url}")
  37. shell_open(url)
  38. else:
  39. print(f"Serving at: {url}")
  40. try:
  41. httpd.serve_forever()
  42. except KeyboardInterrupt:
  43. print("\nKeyboard interrupt received, stopping server.")
  44. finally:
  45. # Clean-up server
  46. httpd.server_close()
  47. if __name__ == "__main__":
  48. parser = argparse.ArgumentParser()
  49. parser.add_argument("-p", "--port", help="port to listen on", default=8060, type=int)
  50. parser.add_argument(
  51. "-r", "--root", help="path to serve as root (relative to `platform/web/`)", default="../../bin", type=Path
  52. )
  53. browser_parser = parser.add_mutually_exclusive_group(required=False)
  54. browser_parser.add_argument(
  55. "-n", "--no-browser", help="don't open default web browser automatically", dest="browser", action="store_false"
  56. )
  57. parser.set_defaults(browser=True)
  58. args = parser.parse_args()
  59. # Change to the directory where the script is located,
  60. # so that the script can be run from any location.
  61. os.chdir(Path(__file__).resolve().parent)
  62. serve(args.root, args.port, args.browser)