Skip to content

Commit e0c324c

Browse files
authored
Add basic HTTP response support (#24)
Rebased 4 commits in this PR.
1 parent 68b5341 commit e0c324c

4 files changed

Lines changed: 150 additions & 1 deletion

File tree

src/main/java/org/juv25d/SocketServer.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.util.logging.Logger;
1212

1313
public class SocketServer {
14-
1514
private final HttpParser httpParser;
1615

1716
public SocketServer(HttpParser httpParser) {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.juv25d.http;
2+
3+
import java.util.Collections;
4+
import java.util.LinkedHashMap;
5+
import java.util.Map;
6+
import java.util.Objects;
7+
8+
public class HttpResponse {
9+
10+
private final int statusCode;
11+
private final String statusText;
12+
private final Map<String, String> headers;
13+
private final byte[] body;
14+
15+
public HttpResponse(int statusCode, String statusText, Map<String, String> headers, byte[] body) {
16+
Objects.requireNonNull(statusText, "statusText must not be null");
17+
Objects.requireNonNull(headers, "headers must not be null");
18+
Objects.requireNonNull(body, "body must not be null");
19+
this.statusCode = statusCode;
20+
this.statusText = statusText;
21+
this.headers = Collections.unmodifiableMap(new LinkedHashMap<>(headers));
22+
this.body = body.clone();
23+
}
24+
25+
public int statusCode(){
26+
return statusCode;
27+
}
28+
29+
public String statusText(){
30+
return statusText;
31+
}
32+
33+
public Map<String, String> headers() {
34+
return headers;
35+
}
36+
37+
public byte[] body(){
38+
return body.clone();
39+
}
40+
41+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package org.juv25d.http;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
import java.nio.charset.StandardCharsets;
6+
import java.util.Map;
7+
8+
public class HttpResponseWriter {
9+
10+
private HttpResponseWriter() {
11+
}
12+
13+
// This method should be called by SocketServer/ConnectionHandler later
14+
public static void write(OutputStream out, HttpResponse response) throws IOException {
15+
writeStatusLine(out, response);
16+
writeHeaders(out, response.headers(), response.body());
17+
writeBody(out, response.body());
18+
out.flush();
19+
}
20+
21+
private static void writeStatusLine(OutputStream out, HttpResponse response) throws IOException {
22+
String statusLine =
23+
"HTTP/1.1 " + response.statusCode() + " " + response.statusText() + "\r\n";
24+
out.write(statusLine.getBytes(StandardCharsets.UTF_8));
25+
}
26+
27+
private static void writeHeaders(
28+
OutputStream out,
29+
Map<String, String> headers,
30+
byte[] body
31+
) throws IOException {
32+
33+
for (Map.Entry<String, String> header : headers.entrySet()) {
34+
if (!header.getKey().equalsIgnoreCase("Content-Length")) {
35+
String line = header.getKey() + ": " + header.getValue() + "\r\n";
36+
out.write(line.getBytes(StandardCharsets.UTF_8));
37+
}
38+
}
39+
40+
String contentLength = "Content-Length: " + body.length + "\r\n";
41+
out.write(contentLength.getBytes(StandardCharsets.UTF_8));
42+
43+
out.write("\r\n".getBytes(StandardCharsets.UTF_8));
44+
}
45+
46+
47+
private static void writeBody(OutputStream out, byte[] body) throws IOException {
48+
out.write(body);
49+
}
50+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package org.juv25d.http;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.io.ByteArrayOutputStream;
7+
import java.nio.charset.StandardCharsets;
8+
import java.util.Map;
9+
10+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
11+
12+
public class HttpResponseWriterTest {
13+
14+
15+
@Test
16+
@DisplayName("Should write a valid HTTP 200 OK response ")
17+
void writesValidHttp200Response() throws Exception {
18+
// Arrange
19+
HttpResponse response = new HttpResponse(
20+
200,
21+
"OK",
22+
Map.of("Content-Type", "text/plain"),
23+
"Hello World".getBytes(StandardCharsets.UTF_8)
24+
);
25+
26+
ByteArrayOutputStream out = new ByteArrayOutputStream();
27+
28+
// Act
29+
HttpResponseWriter.write(out, response);
30+
31+
// Assert
32+
String result = out.toString(StandardCharsets.UTF_8);
33+
34+
assertThat(result).startsWith("HTTP/1.1 200 OK");
35+
assertThat(result).contains("Content-Type: text/plain");
36+
assertThat(result).contains("Content-Length: 11");
37+
assertThat(result).endsWith("Hello World");
38+
}
39+
40+
@Test
41+
@DisplayName("Should write a valid HTTP 404 Not Found Response")
42+
void writes404NotFoundResponse() throws Exception {
43+
HttpResponse response = new HttpResponse(
44+
404,
45+
"Not Found",
46+
Map.of("Content-Type", "text/plain"),
47+
"Not found".getBytes(StandardCharsets.UTF_8)
48+
);
49+
50+
ByteArrayOutputStream out = new ByteArrayOutputStream();
51+
52+
HttpResponseWriter.write(out, response);
53+
54+
String result = out.toString(StandardCharsets.UTF_8);
55+
56+
assertThat(result).startsWith("HTTP/1.1 404 Not Found");
57+
}
58+
59+
}

0 commit comments

Comments
 (0)