1+ package processing .app ;
2+
3+ import com .sun .net .httpserver .HttpExchange ;
4+ import com .sun .net .httpserver .HttpHandler ;
5+ import com .sun .net .httpserver .HttpServer ;
6+
7+ import java .io .*;
8+ import java .net .InetSocketAddress ;
9+ import java .util .*;
10+ import java .util .concurrent .ConcurrentHashMap ;
11+ import java .util .concurrent .Executors ;
12+ import java .util .zip .*;
13+
14+ /**
15+ * A simple HTTP server that serves files from a zip archive.
16+ * Replaces the previous custom WebServer implementation.
17+ */
18+ public class ReferenceServer {
19+ private final HttpServer server ;
20+ private final ZipFile zip ;
21+ private final Map <String , ZipEntry > entries ;
22+ private final int port ;
23+
24+ /** mapping of file extensions to content-types */
25+ static final Map <String , String > contentTypes = new ConcurrentHashMap <>();
26+
27+ static {
28+ contentTypes .put ("" , "content/unknown" );
29+ contentTypes .put (".css" , "text/css" );
30+ contentTypes .put (".csv" , "text/csv" );
31+ contentTypes .put (".eot" , "application/vnd.ms-fontobject" );
32+ contentTypes .put (".gif" , "image/gif" );
33+ contentTypes .put (".html" , "text/html" );
34+ contentTypes .put (".ico" , "image/x-icon" );
35+ contentTypes .put (".jpeg" , "image/jpeg" );
36+ contentTypes .put (".jpg" , "image/jpeg" );
37+ contentTypes .put (".js" , "text/javascript" );
38+ contentTypes .put (".json" , "application/json" );
39+ contentTypes .put (".md" , "text/markdown" );
40+ contentTypes .put (".mdx" , "text/mdx" );
41+ contentTypes .put (".mtl" , "text/plain" );
42+ contentTypes .put (".obj" , "text/plain" );
43+ contentTypes .put (".otf" , "font/otf" );
44+ contentTypes .put (".pde" , "text/plain" );
45+ contentTypes .put (".png" , "image/png" );
46+ contentTypes .put (".svg" , "image/svg+xml" );
47+ contentTypes .put (".tsv" , "text/tab-separated-values" );
48+ contentTypes .put (".ttf" , "font/ttf" );
49+ contentTypes .put (".txt" , "text/plain" );
50+ contentTypes .put (".vlw" , "application/octet-stream" );
51+ contentTypes .put (".woff" , "font/woff" );
52+ contentTypes .put (".woff2" , "font/woff2" );
53+ contentTypes .put (".xml" , "application/xml" );
54+ contentTypes .put (".yml" , "text/yaml" );
55+ contentTypes .put (".zip" , "application/zip" );
56+ }
57+
58+ /**
59+ * Creates a new reference server that serves files from the specified zip file.
60+ *
61+ * @param zipFile The zip file containing reference documentation
62+ * @param port The port to serve on
63+ * @throws IOException If there is an error starting the server
64+ */
65+ public ReferenceServer (File zipFile , int port ) throws IOException {
66+ this .zip = new ZipFile (zipFile );
67+ this .port = port ;
68+
69+ // Index all entries in the zip file
70+ entries = new HashMap <>();
71+ Enumeration <? extends ZipEntry > en = zip .entries ();
72+ while (en .hasMoreElements ()) {
73+ ZipEntry entry = en .nextElement ();
74+ entries .put (entry .getName (), entry );
75+ }
76+
77+ // Create and configure the server
78+ server = HttpServer .create (new InetSocketAddress (port ), 0 );
79+ server .createContext ("/" , new ReferenceHandler ());
80+ server .setExecutor (Executors .newFixedThreadPool (10 ));
81+ server .start ();
82+
83+ Messages .log ("Reference server started on port " + port );
84+ }
85+
86+ /**
87+ * Gets the base URL for the server.
88+ *
89+ * @return The base URL
90+ */
91+ public String getPrefix () {
92+ return "http://localhost:" + port + "/" ;
93+ }
94+
95+ /**
96+ * Stops the server.
97+ */
98+ public void stop () {
99+ server .stop (0 );
100+ try {
101+ zip .close ();
102+ } catch (IOException e ) {
103+ e .printStackTrace ();
104+ }
105+ }
106+
107+ /**
108+ * Handler for reference documentation requests.
109+ */
110+ class ReferenceHandler implements HttpHandler {
111+ @ Override
112+ public void handle (HttpExchange exchange ) throws IOException {
113+ String path = exchange .getRequestURI ().getPath ();
114+
115+ // Remove leading slash to match zip entry paths
116+ if (path .startsWith ("/" )) {
117+ path = path .substring (1 );
118+ }
119+
120+ // Handle empty paths or directory requests
121+ if (path .isEmpty () || path .endsWith ("/" )) {
122+ path = path + "index.html" ;
123+ }
124+
125+ ZipEntry entry = entries .get (path );
126+
127+ if (entry != null ) {
128+ // Determine content type
129+ String contentType = "application/octet-stream" ;
130+ int dotIndex = path .lastIndexOf ('.' );
131+ if (dotIndex > 0 ) {
132+ String extension = path .substring (dotIndex );
133+ contentType = contentTypes .getOrDefault (extension , "application/octet-stream" );
134+ }
135+
136+ // Send the file
137+ exchange .getResponseHeaders ().set ("Content-Type" , contentType );
138+ exchange .getResponseHeaders ().set ("Content-Length" , String .valueOf (entry .getSize ()));
139+ exchange .sendResponseHeaders (200 , entry .getSize ());
140+
141+ try (OutputStream os = exchange .getResponseBody ();
142+ InputStream is = zip .getInputStream (entry )) {
143+ byte [] buffer = new byte [8192 ];
144+ int bytesRead ;
145+ while ((bytesRead = is .read (buffer )) != -1 ) {
146+ os .write (buffer , 0 , bytesRead );
147+ }
148+ }
149+
150+ Messages .log ("Serving: " + path + " (" + contentType + ")" );
151+ } else {
152+ // Send 404
153+ String response = "<html><body><h1>404 Not Found</h1><p>The requested resource was not found.</p></body></html>" ;
154+ exchange .getResponseHeaders ().set ("Content-Type" , "text/html" );
155+ exchange .sendResponseHeaders (404 , response .length ());
156+ try (OutputStream os = exchange .getResponseBody ()) {
157+ os .write (response .getBytes ());
158+ }
159+
160+ Messages .log ("404 Not Found: " + path );
161+ }
162+ }
163+ }
164+
165+ /**
166+ * A main() method for testing.
167+ */
168+ static public void main (String [] args ) {
169+ try {
170+ new ReferenceServer (new File (args [0 ]), 8053 );
171+ System .out .println ("Server running at http://localhost:8053/" );
172+ } catch (IOException e ) {
173+ e .printStackTrace ();
174+ }
175+ }
176+ }
0 commit comments