5
5
import dev .latvian .apps .tinyserver .http .HTTPMethod ;
6
6
import dev .latvian .apps .tinyserver .http .HTTPPathHandler ;
7
7
import dev .latvian .apps .tinyserver .http .HTTPRequest ;
8
- import dev .latvian .apps .tinyserver .http .response .HTTPResponse ;
9
8
import dev .latvian .apps .tinyserver .http .response .HTTPResponseBuilder ;
10
9
import dev .latvian .apps .tinyserver .http .response .HTTPStatus ;
10
+ import dev .latvian .apps .tinyserver .ws .WSEndpointHandler ;
11
11
import dev .latvian .apps .tinyserver .ws .WSHandler ;
12
12
import dev .latvian .apps .tinyserver .ws .WSSession ;
13
13
import dev .latvian .apps .tinyserver .ws .WSSessionFactory ;
14
14
import org .jetbrains .annotations .Nullable ;
15
15
16
+ import java .io .BufferedInputStream ;
16
17
import java .io .BufferedOutputStream ;
17
- import java .io .BufferedReader ;
18
18
import java .io .IOException ;
19
19
import java .io .InputStream ;
20
- import java .io .InputStreamReader ;
21
20
import java .io .OutputStream ;
22
21
import java .net .InetAddress ;
23
22
import java .net .ServerSocket ;
30
29
import java .util .HashMap ;
31
30
import java .util .HashSet ;
32
31
import java .util .Map ;
33
- import java .util .UUID ;
32
+ import java .util .concurrent . ConcurrentHashMap ;
34
33
import java .util .function .Supplier ;
35
34
import java .util .stream .Collectors ;
36
35
@@ -44,6 +43,7 @@ public class HTTPServer<REQ extends HTTPRequest> implements Runnable, ServerRegi
44
43
private int port = 8080 ;
45
44
private int maxPortShift = 0 ;
46
45
private boolean daemon = false ;
46
+ private int bufferSize = 8192 ;
47
47
48
48
public HTTPServer (Supplier <REQ > requestFactory ) {
49
49
this .requestFactory = requestFactory ;
@@ -71,6 +71,10 @@ public void setDaemon(boolean daemon) {
71
71
this .daemon = daemon ;
72
72
}
73
73
74
+ public void setBufferSize (int bufferSize ) {
75
+ this .bufferSize = bufferSize ;
76
+ }
77
+
74
78
public int start () {
75
79
if (serverSocket != null ) {
76
80
throw new IllegalStateException ("Server is already running" );
@@ -123,21 +127,9 @@ public void http(HTTPMethod method, String path, HTTPHandler<REQ> handler) {
123
127
}
124
128
}
125
129
126
- private record WSEndpointHandler <REQ extends HTTPRequest , WSS extends WSSession <REQ >>(WSSessionFactory <REQ , WSS > factory ) implements WSHandler <REQ , WSS >, HTTPHandler <REQ > {
127
- @ Override
128
- public Map <UUID , WSS > sessions () {
129
- return Map .of ();
130
- }
131
-
132
- @ Override
133
- public HTTPResponse handle (REQ req ) {
134
- return HTTPStatus .NOT_IMPLEMENTED ;
135
- }
136
- }
137
-
138
130
@ Override
139
131
public <WSS extends WSSession <REQ >> WSHandler <REQ , WSS > ws (String path , WSSessionFactory <REQ , WSS > factory ) {
140
- var handler = new WSEndpointHandler <>(factory );
132
+ var handler = new WSEndpointHandler <>(factory , new ConcurrentHashMap <>(), daemon );
141
133
get (path , handler );
142
134
return handler ;
143
135
}
@@ -153,16 +145,33 @@ public void run() {
153
145
}
154
146
}
155
147
148
+ private String readLine (InputStream in ) throws IOException {
149
+ var sb = new StringBuilder ();
150
+ int b ;
151
+
152
+ while ((b = in .read ()) != -1 ) {
153
+ if (b == '\n' ) {
154
+ break ;
155
+ }
156
+
157
+ if (b != '\r' ) {
158
+ sb .append ((char ) b );
159
+ }
160
+ }
161
+
162
+ return sb .toString ();
163
+ }
164
+
156
165
private void handleClient (Socket socket ) {
157
166
InputStream in = null ;
158
167
OutputStream out = null ;
168
+ WSSession <REQ > upgradedToWebSocket = null ;
159
169
160
170
try {
161
- in = socket .getInputStream ();
162
- var reader = new BufferedReader (new InputStreamReader (in , StandardCharsets .UTF_8 ));
163
- var firstLineStr = reader .readLine ();
171
+ in = new BufferedInputStream (socket .getInputStream (), bufferSize );
172
+ var firstLineStr = readLine (in );
164
173
165
- if (firstLineStr == null || !firstLineStr .toLowerCase ().endsWith (" http/1.1" )) {
174
+ if (!firstLineStr .toLowerCase ().endsWith (" http/1.1" )) {
166
175
return ;
167
176
}
168
177
@@ -212,9 +221,9 @@ private void handleClient(Socket socket) {
212
221
var headers = new HashMap <String , String >();
213
222
214
223
while (true ) {
215
- var line = reader . readLine ();
224
+ var line = readLine (in );
216
225
217
- if (line == null || line .isBlank ()) {
226
+ if (line .isBlank ()) {
218
227
break ;
219
228
}
220
229
@@ -262,7 +271,7 @@ private void handleClient(Socket socket) {
262
271
var builder = createBuilder (req , null );
263
272
builder .setStatus (HTTPStatus .NO_CONTENT );
264
273
builder .setHeader ("Allow" , allowed .stream ().map (HTTPMethod ::name ).collect (Collectors .joining ("," )));
265
- out = new BufferedOutputStream (socket .getOutputStream ());
274
+ out = new BufferedOutputStream (socket .getOutputStream (), bufferSize );
266
275
builder .write (out , writeBody );
267
276
out .flush ();
268
277
} else if (method == HTTPMethod .TRACE ) {
@@ -276,7 +285,7 @@ private void handleClient(Socket socket) {
276
285
var handler = rootHandlers .get (method );
277
286
278
287
if (handler != null ) {
279
- req .init (new String [0 ], CompiledPath .EMPTY , headers , query , in );
288
+ req .init (this , new String [0 ], CompiledPath .EMPTY , headers , query , in );
280
289
builder = createBuilder (req , handler .handler ());
281
290
}
282
291
} else {
@@ -287,14 +296,14 @@ private void handleClient(Socket socket) {
287
296
var h = hl .staticHandlers ().get (path );
288
297
289
298
if (h != null ) {
290
- req .init (pathParts , h .path (), headers , query , in );
299
+ req .init (this , pathParts , h .path (), headers , query , in );
291
300
builder = createBuilder (req , h .handler ());
292
301
} else {
293
302
for (var dynamicHandler : hl .dynamicHandlers ()) {
294
303
var matches = dynamicHandler .path ().matches (pathParts );
295
304
296
305
if (matches != null ) {
297
- req .init (matches , dynamicHandler .path (), headers , query , in );
306
+ req .init (this , matches , dynamicHandler .path (), headers , query , in );
298
307
builder = createBuilder (req , dynamicHandler .handler ());
299
308
break ;
300
309
}
@@ -308,53 +317,43 @@ private void handleClient(Socket socket) {
308
317
builder .setStatus (HTTPStatus .NOT_FOUND );
309
318
}
310
319
311
- System .out .println ("Request: " + method .name () + " /" + path );
312
- System .out .println ("- Query:" );
313
-
314
- for (var e : query .entrySet ()) {
315
- System .out .println (" " + e .getKey () + ": " + e .getValue ());
316
- }
317
-
318
- System .out .println ("- Variables:" );
319
-
320
- for (var e : req .variables ().entrySet ()) {
321
- System .out .println (" " + e .getKey () + ": " + e .getValue ());
322
- }
320
+ out = new BufferedOutputStream (socket .getOutputStream (), bufferSize );
321
+ builder .write (out , writeBody );
322
+ out .flush ();
323
323
324
- System . out . println ( "- Headers:" );
324
+ upgradedToWebSocket = ( WSSession ) builder . wsSession ( );
325
325
326
- for (var e : headers .entrySet ()) {
327
- System .out .println (" " + e .getKey () + ": " + e .getValue ());
326
+ if (upgradedToWebSocket != null ) {
327
+ upgradedToWebSocket .start (socket , in , out );
328
+ upgradedToWebSocket .onOpen (req );
328
329
}
329
-
330
- out = new BufferedOutputStream (socket .getOutputStream ());
331
- builder .write (out , writeBody );
332
- out .flush ();
333
330
}
334
331
}
335
332
} catch (Exception ex ) {
336
333
ex .printStackTrace ();
337
334
}
338
335
339
- try {
340
- if (in != null ) {
341
- in .close ();
336
+ if (upgradedToWebSocket == null ) {
337
+ try {
338
+ if (in != null ) {
339
+ in .close ();
340
+ }
341
+ } catch (Exception ignored ) {
342
342
}
343
- } catch (Exception ignored ) {
344
- }
345
343
346
- try {
347
- if (out != null ) {
348
- out .close ();
344
+ try {
345
+ if (out != null ) {
346
+ out .close ();
347
+ }
348
+ } catch (Exception ignored ) {
349
349
}
350
- } catch (Exception ignored ) {
351
- }
352
350
353
- try {
354
- if (socket != null ) {
355
- socket .close ();
351
+ try {
352
+ if (socket != null ) {
353
+ socket .close ();
354
+ }
355
+ } catch (Exception ignored ) {
356
356
}
357
- } catch (Exception ignored ) {
358
357
}
359
358
}
360
359
@@ -369,7 +368,7 @@ public HTTPResponseBuilder createBuilder(REQ req, @Nullable HTTPHandler<REQ> han
369
368
370
369
if (handler != null ) {
371
370
try {
372
- handler .handle (req ). build ( builder );
371
+ builder . setResponse ( handler .handle (req ));
373
372
} catch (Exception ex ) {
374
373
builder .setStatus (HTTPStatus .INTERNAL_ERROR );
375
374
handlePayloadError (builder , ex );
0 commit comments