optimized dynamic body size impl, decluttering javadocs/comments
This commit is contained in:
@@ -6,6 +6,7 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
@@ -62,17 +63,32 @@ class HttpServerTest {
|
||||
try (Socket socket = new Socket("127.0.0.1", port);
|
||||
OutputStream out = socket.getOutputStream();
|
||||
InputStream in = socket.getInputStream()) {
|
||||
|
||||
|
||||
out.write(rawHttp.getBytes(StandardCharsets.UTF_8));
|
||||
out.flush();
|
||||
|
||||
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
baos.write(buffer, 0, read);
|
||||
// Read until \r\n\r\n to get the full header block
|
||||
ByteArrayOutputStream headerBuf = new ByteArrayOutputStream();
|
||||
int b, prev3 = -1, prev2 = -1, prev1 = -1;
|
||||
while ((b = in.read()) != -1) {
|
||||
headerBuf.write(b);
|
||||
if (prev3 == '\r' && prev2 == '\n' && prev1 == '\r' && b == '\n') break;
|
||||
prev3 = prev2; prev2 = prev1; prev1 = b;
|
||||
}
|
||||
return baos.toString(StandardCharsets.UTF_8);
|
||||
String headers = headerBuf.toString(StandardCharsets.UTF_8);
|
||||
|
||||
// Parse Content-Length
|
||||
int contentLength = 0;
|
||||
for (String line : headers.split("\r\n")) {
|
||||
if (line.toLowerCase().startsWith("content-length:")) {
|
||||
contentLength = Integer.parseInt(line.substring(line.indexOf(':') + 1).trim());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Read exactly Content-Length bytes for the body
|
||||
byte[] body = in.readNBytes(contentLength);
|
||||
return headers + new String(body, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class RequestParserTest {
|
||||
|
||||
private static Request parse(String raw) throws IOException {
|
||||
byte[] bytes = raw.replace("\n", "\r\n").getBytes(StandardCharsets.UTF_8);
|
||||
return RequestParser.parse(new ByteArrayInputStream(bytes));
|
||||
return new RequestParser().parse(new ByteArrayInputStream(bytes));
|
||||
}
|
||||
|
||||
private static String req(String requestLine, String... headers) {
|
||||
@@ -70,7 +70,7 @@ class RequestParserTest {
|
||||
void body_parsed() throws IOException {
|
||||
String body = "hello body";
|
||||
String raw = "POST / HTTP/1.1\r\nContent-Length: " + body.length() + "\r\n\r\n" + body;
|
||||
Request r = RequestParser.parse(new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8)));
|
||||
Request r = new RequestParser().parse(new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8)));
|
||||
assertNotNull(r);
|
||||
assertEquals(body, new String(r.getBody(), StandardCharsets.UTF_8));
|
||||
}
|
||||
@@ -85,14 +85,14 @@ class RequestParserTest {
|
||||
|
||||
@Test
|
||||
void emptyInputStream_returnsNull() throws IOException {
|
||||
assertNull(RequestParser.parse(new ByteArrayInputStream(new byte[0])));
|
||||
assertNull(new RequestParser().parse(new ByteArrayInputStream(new byte[0])));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingHeaderTerminator_throwsIOException() {
|
||||
// Valid request line but stream ends before \r\n\r\n
|
||||
byte[] raw = "GET / HTTP/1.1\r\nHost: localhost\r\n".getBytes(StandardCharsets.UTF_8);
|
||||
assertThrows(IOException.class, () -> RequestParser.parse(new ByteArrayInputStream(raw)));
|
||||
assertThrows(IOException.class, () -> new RequestParser().parse(new ByteArrayInputStream(raw)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,11 +107,12 @@ class RequestParserTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void headersOverBufferSize_throwsIOException() {
|
||||
// 9 KB of data with no \r\n\r\n exhausts the 8 KB buffer
|
||||
byte[] giant = new byte[9000];
|
||||
void headers_exceedingMaxBufferSize_throwsIOException() {
|
||||
// Feed more bytes than the configured cap with no \r\n\r\n — must throw
|
||||
int cap = 16 * 1024;
|
||||
byte[] giant = new byte[cap + 1];
|
||||
Arrays.fill(giant, (byte) 'A');
|
||||
assertThrows(IOException.class, () -> RequestParser.parse(new ByteArrayInputStream(giant)));
|
||||
assertThrows(IOException.class, () -> new RequestParser(cap).parse(new ByteArrayInputStream(giant)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,7 +120,7 @@ class RequestParserTest {
|
||||
// Content-Length claims 50 but stream ends after 5 bytes
|
||||
String body = "hello";
|
||||
String raw = "POST / HTTP/1.1\r\nContent-Length: 50\r\n\r\n" + body;
|
||||
Request r = RequestParser.parse(new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8)));
|
||||
Request r = new RequestParser().parse(new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8)));
|
||||
assertNotNull(r);
|
||||
assertEquals(50, r.getBody().length);
|
||||
assertEquals(body, new String(r.getBody(), 0, body.length(), StandardCharsets.UTF_8));
|
||||
|
||||
Reference in New Issue
Block a user