SDL: Add minimal http server for emscripten test apps

From 64d570f027a3ec3a65cdaa44d48d95171dd1b9f3 Mon Sep 17 00:00:00 2001
From: Anonymous Maarten <[EMAIL REDACTED]>
Date: Wed, 9 Aug 2023 19:05:56 +0200
Subject: [PATCH] Add minimal http server for emscripten test apps

---
 test/CMakeLists.txt       |  6 ++++
 test/emscripten/server.py | 67 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)
 create mode 100755 test/emscripten/server.py

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 1a710ca914cd..02dffde1a6ef 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -302,6 +302,12 @@ if(OPENGL_FOUND)
 endif()
 if(EMSCRIPTEN)
     set_property(TARGET testshader APPEND_STRING PROPERTY LINK_FLAGS " -sLEGACY_GL_EMULATION")
+
+    find_package(Python3 COMPONENTS Interpreter)
+    if(TARGET Python3::Interpreter)
+        add_custom_target(serve-sdl-tests
+            COMMAND Python3::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/emscripten/server.py" -d "${CMAKE_CURRENT_BINARY_DIR}")
+    endif()
 endif()
 
 if(PSP)
diff --git a/test/emscripten/server.py b/test/emscripten/server.py
new file mode 100755
index 000000000000..be751725dc3b
--- /dev/null
+++ b/test/emscripten/server.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+
+# Based on http/server.py from Python
+
+from argparse import ArgumentParser
+from http.server import SimpleHTTPRequestHandler
+from socketserver import TCPServer
+
+
+def serve_forever(port: int, ServerClass):
+    handler = SimpleHTTPRequestHandler
+    handler.extensions_map = {
+        ".manifest": "text/cache-manifest",
+        ".html": "text/html",
+        ".png": "image/png",
+        ".jpg": "image/jpg",
+        ".svg":	"image/svg+xml",
+        ".css":	"text/css",
+        ".js":	"application/x-javascript",
+        ".wasm": "application/wasm",
+        "": "application/octet-stream",
+    }
+
+    addr = ("0.0.0.0", port)
+    HandlerClass = SimpleHTTPRequestHandler
+    with ServerClass(addr, handler) as httpd:
+        host, port = httpd.socket.getsockname()[:2]
+        url_host = f"[{host}]" if ":" in host else host
+        print(
+            f"Serving HTTP on {host} port {port} (http://{url_host}:{port}/) ..."
+        )
+        try:
+            httpd.serve_forever()
+        except KeyboardInterrupt:
+            print("\nKeyboard interrupt received, exiting.")
+            return 0
+
+
+def main():
+    parser = ArgumentParser(allow_abbrev=False)
+    parser.add_argument("port", nargs="?", type=int, default=8080)
+    parser.add_argument("-d", dest="directory", type=str, default=None)
+    args = parser.parse_args()
+
+    import contextlib
+    import http.server
+    import socket
+
+    class DualStackServer(http.server.ThreadingHTTPServer):
+        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()
+
+        def finish_request(self, request, client_address):
+            self.RequestHandlerClass(request, client_address, self, directory=args.directory)
+
+    return serve_forever(
+        port=args.port,
+        ServerClass=DualStackServer,
+    )
+
+
+if __name__ == "__main__":
+    raise SystemExit(main())