Skip to content

Commit 6cc7a16

Browse files
committed
Another rewrite, fixed bytes getting lost during reading, added upgrade response
1 parent 434a394 commit 6cc7a16

23 files changed

+320
-246
lines changed
Lines changed: 119 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package dev.latvian.apps.tinyserver;
22

3+
import dev.latvian.apps.tinyserver.http.HTTPRequest;
4+
import dev.latvian.apps.tinyserver.http.HTTPUpgrade;
35
import dev.latvian.apps.tinyserver.http.response.HTTPPayload;
6+
import org.jetbrains.annotations.Nullable;
47

58
import java.io.ByteArrayOutputStream;
69
import java.io.IOException;
@@ -11,31 +14,84 @@
1114
import java.nio.charset.StandardCharsets;
1215
import 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

Comments
 (0)