11package dev .latvian .apps .tinyserver ;
22
3+ import dev .latvian .apps .tinyserver .http .HTTPRequest ;
4+ import dev .latvian .apps .tinyserver .http .HTTPUpgrade ;
35import dev .latvian .apps .tinyserver .http .response .HTTPPayload ;
6+ import org .jetbrains .annotations .Nullable ;
47
58import java .io .ByteArrayOutputStream ;
69import java .io .IOException ;
1114import java .nio .charset .StandardCharsets ;
1215import java .time .Instant ;
1316
14- public class HTTPConnection implements AutoCloseable , Runnable {
15- public final HTTPServer <?> server ;
16- public final SocketChannel socketChannel ;
17+ public class HTTPConnection <REQ extends HTTPRequest > implements Runnable {
18+ public static final StatusCode OPEN = new StatusCode (0 , "Open" );
19+ public static final StatusCode CLOSED = new StatusCode (1 , "Closed" );
20+ public static final StatusCode TIMEOUT = new StatusCode (2 , "Timeout" );
21+ public static final StatusCode SOCKET_CLOSED = new StatusCode (3 , "Socket Closed" );
22+ public static final StatusCode INVALID_REQUEST = new StatusCode (3 , "Invalid HTTP Request" );
23+
24+ private final HTTPServer <REQ > server ;
25+ private final SocketChannel socketChannel ;
1726 public final Instant createdTime ;
1827 long lastActivity ;
1928 private final ByteBuffer singleByte ;
29+ private final byte [] temp ;
30+ HTTPUpgrade <REQ > upgrade ;
31+ StatusCode status = OPEN ;
2032
21- public HTTPConnection (HTTPServer <? > server , SocketChannel socketChannel , Instant createdTime ) {
33+ public HTTPConnection (HTTPServer <REQ > server , SocketChannel socketChannel , Instant createdTime ) {
2234 this .server = server ;
2335 this .socketChannel = socketChannel ;
2436 this .createdTime = createdTime ;
2537 this .singleByte = ByteBuffer .allocate (1 );
38+ this .temp = new byte [8 ];
39+ }
40+
41+ public HTTPServer <REQ > server () {
42+ return server ;
43+ }
44+
45+ @ Nullable
46+ public HTTPUpgrade <REQ > upgrade () {
47+ return upgrade ;
2648 }
2749
2850 @ Override
2951 public void run () {
3052 try {
31- server .handleClient (this );
53+ // noinspection StatementWithEmptyBody
54+ while (!socketChannel .finishConnect ()) ;
55+ // noinspection StatementWithEmptyBody
56+ while (server .handleClient (this )) ;
57+
58+ if (upgrade == null ) {
59+ close ();
60+ }
3261 } catch (Throwable ex ) {
3362 error (ex );
3463 }
3564 }
3665
37- @ Override
38- public void close () {
66+ public final void close () {
67+ if (status == OPEN ) {
68+ status = CLOSED ;
69+ }
70+ }
71+
72+ public final void close (String reason , boolean error ) {
73+ if (status == OPEN ) {
74+ status = new StatusCode (error ? 3 : 1 , reason );
75+ }
76+ }
77+
78+ final boolean handleClosure () {
79+ if (status == OPEN && !socketChannel .isOpen ()) {
80+ status = SOCKET_CLOSED ;
81+ }
82+
83+ if (status == OPEN && upgrade != null && upgrade .isClosed ()) {
84+ status = CLOSED ;
85+ }
86+
87+ if (status == OPEN && upgrade == null && server .now - lastActivity > server .keepAliveTimeout * 1000L ) {
88+ status = TIMEOUT ;
89+ }
90+
91+ if (status == OPEN ) {
92+ return false ;
93+ }
94+
3995 try {
4096 socketChannel .shutdownInput ();
4197 } catch (IOException ex ) {
@@ -54,17 +110,14 @@ public void close() {
54110 error (ex );
55111 }
56112
57- closed ();
58- }
59-
60- public boolean isClosed () {
61- return !socketChannel .isOpen ();
113+ closed (status );
114+ return true ;
62115 }
63116
64117 protected void beforeHandshake () {
65118 }
66119
67- protected void closed () {
120+ protected void closed (StatusCode reason ) {
68121 }
69122
70123 protected void error (Throwable error ) {
@@ -75,42 +128,64 @@ protected void error(Throwable error) {
75128
76129 @ Override
77130 public String toString () {
78- return socketChannel .socket ().getPort () + " @ " + HTTPPayload .DATE_TIME_FORMATTER .format (createdTime );
131+ return socketChannel .socket ().getPort () + " @ " + HTTPPayload .DATE_TIME_FORMATTER .format (createdTime ) + (upgrade == null ? "" : (" (" + upgrade .protocol () + ")" ));
132+ }
133+
134+ public int readDirectly (ByteBuffer buffer ) throws IOException {
135+ return socketChannel .read (buffer );
136+ }
137+
138+ public void read (ByteBuffer buffer ) throws IOException {
139+ while (buffer .hasRemaining ()) {
140+ readDirectly (buffer );
141+ }
142+ }
143+
144+ public void readBytes (byte [] bytes , int off , int len ) throws IOException {
145+ for (var i = 0 ; i < len ; i ++) {
146+ singleByte .clear ();
147+
148+ int r ;
149+
150+ do {
151+ r = readDirectly (singleByte );
152+ }
153+ while (r != 1 );
154+
155+ bytes [off + i ] = singleByte .get (0 );
156+ }
79157 }
80158
81159 public void readBytes (byte [] bytes ) throws IOException {
82- var buf = ByteBuffer .wrap (bytes );
83- socketChannel .read (buf );
84- buf .flip ();
85- buf .get (bytes );
160+ readBytes (bytes , 0 , bytes .length );
86161 }
87162
88163 public byte readByte () throws IOException {
89- singleByte .clear ();
90- socketChannel .read (singleByte );
91- singleByte .flip ();
92- return singleByte .get ();
164+ readBytes (temp , 0 , 1 );
165+ return temp [0 ];
93166 }
94167
95168 public short readShort () throws IOException {
96- var buf = ByteBuffer .allocate (2 );
97- socketChannel .read (buf );
98- buf .flip ();
99- return buf .getShort ();
169+ readBytes (temp , 0 , 2 );
170+ return (short ) ((temp [0 ] & 0xFF ) << 8 | (temp [1 ] & 0xFF ));
100171 }
101172
102173 public int readInt () throws IOException {
103- var buf = ByteBuffer .allocate (4 );
104- socketChannel .read (buf );
105- buf .flip ();
106- return buf .getInt ();
174+ readBytes (temp , 0 , 4 );
175+ return (temp [0 ] & 0xFF ) << 24 | (temp [1 ] & 0xFF ) << 16 | (temp [2 ] & 0xFF ) << 8 | (temp [3 ] & 0xFF );
176+ }
177+
178+ public float readFloat () throws IOException {
179+ return Float .intBitsToFloat (readInt ());
107180 }
108181
109182 public long readLong () throws IOException {
110- var buf = ByteBuffer .allocate (8 );
111- socketChannel .read (buf );
112- buf .flip ();
113- return buf .getLong ();
183+ readBytes (temp , 0 , 8 );
184+ return (long ) (temp [0 ] & 0xFF ) << 56 | (long ) (temp [1 ] & 0xFF ) << 48 | (long ) (temp [2 ] & 0xFF ) << 40 | (long ) (temp [3 ] & 0xFF ) << 32 | (long ) (temp [4 ] & 0xFF ) << 24 | (long ) (temp [5 ] & 0xFF ) << 16 | (long ) (temp [6 ] & 0xFF ) << 8 | (long ) (temp [7 ] & 0xFF );
185+ }
186+
187+ public double readDouble () throws IOException {
188+ return Double .longBitsToDouble (readLong ());
114189 }
115190
116191 public String readCRLF () throws IOException {
@@ -135,4 +210,14 @@ public String readCRLF() throws IOException {
135210
136211 return bytes .toString (StandardCharsets .UTF_8 );
137212 }
213+
214+ public void writeDirectly (ByteBuffer buffer ) throws IOException {
215+ socketChannel .write (buffer );
216+ }
217+
218+ public void write (ByteBuffer buffer ) throws IOException {
219+ while (buffer .hasRemaining ()) {
220+ writeDirectly (buffer );
221+ }
222+ }
138223}
0 commit comments