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