Skip to content

Commit 86f2ba7

Browse files
Refactor status codes to constants #71 (#77)
* refactor: add predefined HTTP status code constants to HttpResponseBuilder * refactor: use status code constants in StaticFileHandler * test: refactor StaticFileHandlerTest to use status code constants * test: refactor HttpResponseBuilderTest to use status code constants and explicit assertations
1 parent 78f7e21 commit 86f2ba7

4 files changed

Lines changed: 77 additions & 31 deletions

File tree

src/main/java/org/example/StaticFileHandler.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.example;
22

33
import org.example.http.HttpResponseBuilder;
4+
import static org.example.http.HttpResponseBuilder.*;
45

56
import java.io.File;
67
import java.io.IOException;
@@ -36,22 +37,22 @@ private void handleGetRequest(String uri) throws IOException {
3637
File file = new File(root, uri).getCanonicalFile();
3738
if (!file.toPath().startsWith(root.toPath())) {
3839
fileBytes = "403 Forbidden".getBytes(java.nio.charset.StandardCharsets.UTF_8);
39-
statusCode = 403;
40+
statusCode = SC_FORBIDDEN;
4041
return;
4142
}
4243

4344
// Read file
4445
if (file.isFile()) {
4546
fileBytes = Files.readAllBytes(file.toPath());
46-
statusCode = 200;
47+
statusCode = SC_OK;
4748
} else {
4849
File errorFile = new File(WEB_ROOT, "pageNotFound.html");
4950
if (errorFile.isFile()) {
5051
fileBytes = Files.readAllBytes(errorFile.toPath());
5152
} else {
5253
fileBytes = "404 Not Found".getBytes(java.nio.charset.StandardCharsets.UTF_8);
5354
}
54-
statusCode = 404;
55+
statusCode = SC_NOT_FOUND;
5556
}
5657
}
5758

@@ -65,4 +66,4 @@ public void sendGetRequest(OutputStream outputStream, String uri) throws IOExcep
6566
outputStream.write(response.build());
6667
outputStream.flush();
6768
}
68-
}
69+
}

src/main/java/org/example/http/HttpResponseBuilder.java

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,59 @@
66

77
public class HttpResponseBuilder {
88

9+
// SUCCESS
10+
public static final int SC_OK = 200;
11+
public static final int SC_CREATED = 201;
12+
public static final int SC_NO_CONTENT = 204;
13+
14+
// REDIRECTION
15+
public static final int SC_MOVED_PERMANENTLY = 301;
16+
public static final int SC_FOUND = 302;
17+
public static final int SC_SEE_OTHER = 303;
18+
public static final int SC_NOT_MODIFIED = 304;
19+
public static final int SC_TEMPORARY_REDIRECT = 307;
20+
public static final int SC_PERMANENT_REDIRECT = 308;
21+
22+
// CLIENT ERROR
23+
public static final int SC_BAD_REQUEST = 400;
24+
public static final int SC_UNAUTHORIZED = 401;
25+
public static final int SC_FORBIDDEN = 403;
26+
public static final int SC_NOT_FOUND = 404;
27+
28+
// SERVER ERROR
29+
public static final int SC_INTERNAL_SERVER_ERROR = 500;
30+
public static final int SC_BAD_GATEWAY = 502;
31+
public static final int SC_SERVICE_UNAVAILABLE = 503;
32+
public static final int SC_GATEWAY_TIMEOUT = 504;
33+
34+
35+
936
private static final String PROTOCOL = "HTTP/1.1";
10-
private int statusCode = 200;
37+
private int statusCode = SC_OK;
1138
private String body = "";
1239
private byte[] bytebody;
1340
private Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
1441

1542
private static final String CRLF = "\r\n";
1643

1744
private static final Map<Integer, String> REASON_PHRASES = Map.ofEntries(
18-
Map.entry(200, "OK"),
19-
Map.entry(201, "Created"),
20-
Map.entry(204, "No Content"),
21-
Map.entry(301, "Moved Permanently"),
22-
Map.entry(302, "Found"),
23-
Map.entry(303, "See Other"),
24-
Map.entry(304, "Not Modified"),
25-
Map.entry(307, "Temporary Redirect"),
26-
Map.entry(308, "Permanent Redirect"),
27-
Map.entry(400, "Bad Request"),
28-
Map.entry(401, "Unauthorized"),
29-
Map.entry(403, "Forbidden"),
30-
Map.entry(404, "Not Found"),
31-
Map.entry(500, "Internal Server Error"),
32-
Map.entry(502, "Bad Gateway"),
33-
Map.entry(503, "Service Unavailable")
45+
Map.entry(SC_OK, "OK"),
46+
Map.entry(SC_CREATED, "Created"),
47+
Map.entry(SC_NO_CONTENT, "No Content"),
48+
Map.entry(SC_MOVED_PERMANENTLY, "Moved Permanently"),
49+
Map.entry(SC_FOUND, "Found"),
50+
Map.entry(SC_SEE_OTHER, "See Other"),
51+
Map.entry(SC_NOT_MODIFIED, "Not Modified"),
52+
Map.entry(SC_TEMPORARY_REDIRECT, "Temporary Redirect"),
53+
Map.entry(SC_PERMANENT_REDIRECT, "Permanent Redirect"),
54+
Map.entry(SC_BAD_REQUEST, "Bad Request"),
55+
Map.entry(SC_UNAUTHORIZED, "Unauthorized"),
56+
Map.entry(SC_FORBIDDEN, "Forbidden"),
57+
Map.entry(SC_NOT_FOUND, "Not Found"),
58+
Map.entry(SC_INTERNAL_SERVER_ERROR, "Internal Server Error"),
59+
Map.entry(SC_BAD_GATEWAY, "Bad Gateway"),
60+
Map.entry(SC_SERVICE_UNAVAILABLE, "Service Unavailable"),
61+
Map.entry(SC_GATEWAY_TIMEOUT, "Gateway Timeout")
3462
);
3563

3664
public void setStatusCode(int statusCode) {
@@ -106,4 +134,4 @@ public byte[] build() {
106134

107135
return response;
108136
}
109-
}
137+
}

src/test/java/org/example/StaticFileHandlerTest.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.nio.file.Files;
1111
import java.nio.file.Path;
1212
import static org.junit.jupiter.api.Assertions.*;
13+
import static org.example.http.HttpResponseBuilder.*;
1314

1415
/**
1516
* Unit test class for verifying the behavior of the StaticFileHandler class.
@@ -48,7 +49,7 @@ void test_file_that_exists_should_return_200() throws IOException {
4849
//Assert
4950
String response = fakeOutput.toString();//Converts the captured byte stream into a String for verification
5051

51-
assertTrue(response.contains("HTTP/1.1 200 OK")); // Assert the status
52+
assertTrue(response.contains("HTTP/1.1 " + SC_OK + " OK")); // Assert the status
5253
assertTrue(response.contains("Hello Test")); //Assert the content in the file
5354

5455
assertTrue(response.contains("Content-Type: text/html; charset=UTF-8")); // Verify the correct Content-type header
@@ -74,7 +75,7 @@ void test_file_that_does_not_exists_should_return_404() throws IOException {
7475
//Assert
7576
String response = fakeOutput.toString();//Converts the captured byte stream into a String for verification
7677

77-
assertTrue(response.contains("HTTP/1.1 404 Not Found")); // Assert the status
78+
assertTrue(response.contains("HTTP/1.1 " + SC_NOT_FOUND + " Not Found")); // Assert the status
7879

7980
}
8081

@@ -94,7 +95,7 @@ void test_path_traversal_should_return_403() throws IOException {
9495
// Assert
9596
String response = fakeOutput.toString();
9697
assertFalse(response.contains("TOP SECRET"));
97-
assertTrue(response.contains("HTTP/1.1 403 Forbidden"));
98+
assertTrue(response.contains("HTTP/1.1 " + SC_FORBIDDEN + " Forbidden"));
9899
}
99100

100101
@ParameterizedTest
@@ -115,7 +116,7 @@ void sanitized_uris_should_return_200(String uri) throws IOException {
115116
handler.sendGetRequest(out, uri);
116117

117118
// Assert
118-
assertTrue(out.toString().contains("HTTP/1.1 200 OK"));
119+
assertTrue(out.toString().contains("HTTP/1.1 " + SC_OK + " OK"));
119120
}
120121

121122
@Test
@@ -131,7 +132,7 @@ void null_byte_injection_should_not_return_200() throws IOException {
131132

132133
// Assert
133134
String response = out.toString();
134-
assertFalse(response.contains("HTTP/1.1 200 OK"));
135-
assertTrue(response.contains("HTTP/1.1 404 Not Found"));
135+
assertFalse(response.contains("HTTP/1.1 " + SC_OK + " OK"));
136+
assertTrue(response.contains("HTTP/1.1 " + SC_NOT_FOUND + " Not Found"));
136137
}
137138
}

src/test/java/org/example/http/HttpResponseBuilderTest.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.stream.Stream;
1515

1616
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.example.http.HttpResponseBuilder.*;
1718

1819
class HttpResponseBuilderTest {
1920

@@ -25,13 +26,14 @@ private String asString(byte[] response) {
2526
@Test
2627
void build_returnsValidHttpResponse() {
2728
HttpResponseBuilder builder = new HttpResponseBuilder();
29+
builder.setStatusCode(SC_OK);
2830
builder.setBody("Hello");
2931

3032
byte[] result = builder.build();
3133
String resultStr = asString(result);
3234

3335
assertThat(resultStr)
34-
.contains("HTTP/1.1 200 OK")
36+
.contains("HTTP/1.1 " + SC_OK + " OK")
3537
.contains("Content-Length: 5")
3638
.contains("\r\n\r\n")
3739
.contains("Hello");
@@ -49,6 +51,7 @@ void build_returnsValidHttpResponse() {
4951
@DisplayName("Should calculate correct Content-Length for various strings")
5052
void build_handlesUtf8ContentLength(String body, int expectedLength) {
5153
HttpResponseBuilder builder = new HttpResponseBuilder();
54+
builder.setStatusCode(SC_OK);
5255
builder.setBody(body);
5356

5457
byte[] result = builder.build();
@@ -62,6 +65,7 @@ void build_handlesUtf8ContentLength(String body, int expectedLength) {
6265
void setHeader_addsHeaderToResponse() {
6366
HttpResponseBuilder builder = new HttpResponseBuilder();
6467
builder.setHeader("Content-Type", "text/html; charset=UTF-8");
68+
builder.setStatusCode(SC_OK);
6569
builder.setBody("Hello");
6670

6771
byte[] result = builder.build();
@@ -76,6 +80,7 @@ void setHeader_allowsMultipleHeaders() {
7680
HttpResponseBuilder builder = new HttpResponseBuilder();
7781
builder.setHeader("Content-Type", "application/json");
7882
builder.setHeader("Cache-Control", "no-cache");
83+
builder.setStatusCode(SC_OK);
7984
builder.setBody("{}");
8085

8186
byte[] result = builder.build();
@@ -107,6 +112,7 @@ void setHeader_allowsMultipleHeaders() {
107112
void setContentTypeFromFilename_detectsVariousTypes(String filename, String expectedContentType) {
108113
HttpResponseBuilder builder = new HttpResponseBuilder();
109114
builder.setContentTypeFromFilename(filename);
115+
builder.setStatusCode(SC_OK);
110116
builder.setBody("test content");
111117

112118
byte[] result = builder.build();
@@ -130,6 +136,7 @@ void setContentTypeFromFilename_detectsVariousTypes(String filename, String expe
130136
void setContentTypeFromFilename_allCases(String filename, String expectedContentType) {
131137
HttpResponseBuilder builder = new HttpResponseBuilder();
132138
builder.setContentTypeFromFilename(filename);
139+
builder.setStatusCode(SC_OK);
133140
builder.setBody("test");
134141

135142
byte[] result = builder.build();
@@ -143,6 +150,7 @@ void setContentTypeFromFilename_allCases(String filename, String expectedContent
143150
@DisplayName("Should not duplicate headers when manually set")
144151
void build_doesNotDuplicateHeaders(String headerName, String manualValue, String bodyContent) {
145152
HttpResponseBuilder builder = new HttpResponseBuilder();
153+
builder.setStatusCode(SC_OK);
146154
builder.setHeader(headerName, manualValue);
147155
builder.setBody(bodyContent);
148156

@@ -179,6 +187,7 @@ void setHeaders_preservesCaseInsensitivity() {
179187
builder.setHeaders(headers);
180188

181189
builder.setHeader("Content-Length", "100");
190+
builder.setStatusCode(SC_OK);
182191
builder.setBody("Hello");
183192

184193
byte[] result = builder.build();
@@ -287,6 +296,7 @@ void build_handlesUnknownStatusCodes(int statusCode) {
287296
@DisplayName("Should auto-append headers when not manually set")
288297
void build_autoAppendsHeadersWhenNotSet() {
289298
HttpResponseBuilder builder = new HttpResponseBuilder();
299+
builder.setStatusCode(SC_OK);
290300
builder.setBody("Hello");
291301

292302
byte[] result = builder.build();
@@ -303,6 +313,7 @@ void build_combinesCustomAndAutoHeaders() {
303313
HttpResponseBuilder builder = new HttpResponseBuilder();
304314
builder.setHeader("Content-Type", "text/html");
305315
builder.setHeader("Cache-Control", "no-cache");
316+
builder.setStatusCode(SC_OK);
306317
builder.setBody("Hello");
307318

308319
byte[] result = builder.build();
@@ -328,6 +339,7 @@ void setHeader_caseInsensitive(String headerName, String headerValue) {
328339
HttpResponseBuilder builder = new HttpResponseBuilder();
329340

330341
builder.setHeader(headerName, headerValue);
342+
builder.setStatusCode(SC_OK);
331343
builder.setBody("Hello");
332344

333345
byte[] result = builder.build();
@@ -350,13 +362,14 @@ void setHeader_caseInsensitive(String headerName, String headerValue) {
350362
@DisplayName("Should handle empty and null body")
351363
void build_emptyAndNullBody(String body, int expectedLength) {
352364
HttpResponseBuilder builder = new HttpResponseBuilder();
365+
builder.setStatusCode(SC_OK);
353366
builder.setBody(body);
354367

355368
byte[] result = builder.build();
356369
String resultStr = asString(result);
357370

358371
assertThat(resultStr)
359-
.contains("HTTP/1.1 200 OK")
372+
.contains("HTTP/1.1 " + SC_OK + " OK")
360373
.contains("Content-Length: " + expectedLength);
361374
}
362375

@@ -373,6 +386,7 @@ void setHeader_overridesPreviousValue(String headerName, String firstValue, Stri
373386
HttpResponseBuilder builder = new HttpResponseBuilder();
374387
builder.setHeader(headerName, firstValue);
375388
builder.setHeader(headerName, secondValue); // Override
389+
builder.setStatusCode(SC_OK);
376390
builder.setBody("Test");
377391

378392
byte[] result = builder.build();
@@ -390,6 +404,7 @@ void setHeader_overridesPreviousValue(String headerName, String firstValue, Stri
390404
void build_autoAppendsSpecificHeaders(String body, boolean setContentLength, boolean setConnection,
391405
String expectedContentLength, String expectedConnection) {
392406
HttpResponseBuilder builder = new HttpResponseBuilder();
407+
builder.setStatusCode(SC_OK);
393408

394409
if (setContentLength) {
395410
builder.setHeader("Content-Length", "999");
@@ -425,6 +440,7 @@ private static Stream<Arguments> provideAutoAppendScenarios() {
425440
@DisplayName("Should preserve binary content without corruption")
426441
void build_preservesBinaryContent() {
427442
HttpResponseBuilder builder = new HttpResponseBuilder();
443+
builder.setStatusCode(SC_OK);
428444

429445
// Create binary data with non-UTF-8 bytes
430446
byte[] binaryData = new byte[]{
@@ -455,4 +471,4 @@ void build_preservesBinaryContent() {
455471

456472
assertThat(actualBody).isEqualTo(binaryData);
457473
}
458-
}
474+
}

0 commit comments

Comments
 (0)