Skip to content

Commit 0f224d5

Browse files
committed
fixed Issue 75 Implement health Endpoint for Server Health Checks
1 parent c473d5b commit 0f224d5

2 files changed

Lines changed: 86 additions & 105 deletions

File tree

src/main/java/org/example/ConnectionHandler.java

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,14 @@
1616

1717
public class ConnectionHandler implements AutoCloseable {
1818

19-
Socket client;
20-
String uri;
19+
private final Socket client;
2120
private final List<Filter> filters;
22-
String webRoot;
21+
private final String webRoot;
2322

2423
public ConnectionHandler(Socket client) {
2524
this.client = client;
26-
this.filters = buildFilters();
2725
this.webRoot = null;
26+
this.filters = buildFilters();
2827
}
2928

3029
public ConnectionHandler(Socket client, String webRoot) {
@@ -40,19 +39,16 @@ private List<Filter> buildFilters() {
4039
if (Boolean.TRUE.equals(ipFilterConfig.enabled())) {
4140
list.add(createIpFilterFromConfig(ipFilterConfig));
4241
}
43-
// Add more filters here...
42+
// Add more filters here if needed
4443
return list;
4544
}
4645

47-
public void runConnectionHandler() throws IOException {
48-
StaticFileHandler sfh;
49-
50-
if (webRoot != null) {
51-
sfh = new StaticFileHandler(webRoot);
52-
} else {
53-
sfh = new StaticFileHandler();
54-
}
46+
@Override
47+
public void close() throws Exception {
48+
client.close();
49+
}
5550

51+
public void runConnectionHandler() throws IOException {
5652
HttpParser parser = new HttpParser();
5753
parser.setReader(client.getInputStream());
5854
parser.parseRequest();
@@ -66,44 +62,60 @@ public void runConnectionHandler() throws IOException {
6662
""
6763
);
6864

65+
// Save client IP
6966
String clientIp = client.getInetAddress().getHostAddress();
7067
request.setAttribute("clientIp", clientIp);
7168

7269
HttpResponseBuilder response = new HttpResponseBuilder();
7370

74-
FilterChainImpl chain = new FilterChainImpl(filters, sfh);
71+
// Apply filters first
72+
FilterChainImpl chain = new FilterChainImpl(filters);
7573
chain.doFilter(request, response);
7674

77-
client.getOutputStream().write(response.build());
78-
client.getOutputStream().flush();
79-
}
75+
// If filters returned forbidden or bad request, send immediately
76+
int status = response.getStatusCode();
77+
if (status == HttpResponseBuilder.SC_FORBIDDEN ||
78+
status == HttpResponseBuilder.SC_BAD_REQUEST) {
79+
client.getOutputStream().write(response.build());
80+
client.getOutputStream().flush();
81+
return;
82+
}
8083

81-
@Override
82-
public void close() throws Exception {
83-
client.close();
84+
String uri = parser.getUri();
85+
86+
// Handle /health endpoint
87+
if ("/health".equals(uri) && "GET".equalsIgnoreCase(request.getMethod())) {
88+
HttpResponseBuilder healthResponse = new HttpResponseBuilder();
89+
healthResponse.setStatusCode(HttpResponseBuilder.SC_OK);
90+
healthResponse.setContentType("application/json");
91+
healthResponse.setBody("{\"status\":\"ok\"}".getBytes());
92+
client.getOutputStream().write(healthResponse.build());
93+
client.getOutputStream().flush();
94+
return;
95+
}
96+
97+
// Handle static files
98+
StaticFileHandler sfh = webRoot != null ? new StaticFileHandler(webRoot) : new StaticFileHandler();
99+
if ("HEAD".equalsIgnoreCase(request.getMethod())) {
100+
sfh.sendHeadRequest(client.getOutputStream(), uri);
101+
} else {
102+
sfh.sendGetRequest(client.getOutputStream(), uri);
103+
}
84104
}
85105

86106
private IpFilter createIpFilterFromConfig(AppConfig.IpFilterConfig config) {
87107
IpFilter filter = new IpFilter();
88108

89-
// Set mode
90109
if ("ALLOWLIST".equalsIgnoreCase(config.mode())) {
91110
filter.setMode(IpFilter.FilterMode.ALLOWLIST);
92111
} else {
93112
filter.setMode(IpFilter.FilterMode.BLOCKLIST);
94113
}
95114

96-
// Add blocked IPs
97-
for (String ip : config.blockedIps()) {
98-
filter.addBlockedIp(ip);
99-
}
100-
101-
// Add allowed IPs
102-
for (String ip : config.allowedIps()) {
103-
filter.addAllowedIp(ip);
104-
}
115+
for (String ip : config.blockedIps()) filter.addBlockedIp(ip);
116+
for (String ip : config.allowedIps()) filter.addAllowedIp(ip);
105117

106118
filter.init();
107119
return filter;
108120
}
109-
}
121+
}
Lines changed: 43 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,81 @@
11
package org.example;
22

33
import org.example.http.HttpResponseBuilder;
4-
import org.example.httpparser.HttpRequest;
54

65
import java.io.File;
76
import java.io.IOException;
7+
import java.io.OutputStream;
88
import java.nio.file.Files;
99

1010
import static org.example.http.HttpResponseBuilder.*;
1111

12-
public class StaticFileHandler implements org.example.server.TerminalHandler {
13-
private final String webRoot;
12+
public class StaticFileHandler {
13+
14+
private final String WEB_ROOT;
15+
private byte[] fileBytes;
16+
private int statusCode;
17+
1418
public static final String TEXT_PLAIN_CHARSET_UTF_8 = "text/plain; charset=utf-8";
1519

16-
// Constructor for production
1720
public StaticFileHandler() {
18-
webRoot = "www";
21+
WEB_ROOT = "www";
1922
}
2023

21-
// Constructor for tests, otherwise the www folder won't be seen
2224
public StaticFileHandler(String webRoot) {
23-
this.webRoot = webRoot;
25+
this.WEB_ROOT = webRoot;
2426
}
2527

26-
private void handleGetRequest(HttpRequest request, HttpResponseBuilder response) {
27-
byte[] fileBytes;
28-
if (!"GET".equals(request.getMethod())) {
29-
fileBytes = "405 Method Not Allowed".getBytes(java.nio.charset.StandardCharsets.UTF_8);
30-
response.setContentType(TEXT_PLAIN_CHARSET_UTF_8);
31-
response.setStatusCode(SC_METHOD_NOT_ALLOWED);
32-
response.setHeader("Allow", "GET");
33-
response.setBody(fileBytes);
34-
return;
35-
}
36-
37-
String uri = request.getPath();
38-
if (uri == null) {
39-
uri = "";
40-
}
41-
42-
// Sanitize URI
28+
private void handleGetRequest(String uri) throws IOException {
29+
// Clean URI
4330
int q = uri.indexOf('?');
4431
if (q >= 0) uri = uri.substring(0, q);
4532
int h = uri.indexOf('#');
4633
if (h >= 0) uri = uri.substring(0, h);
4734
uri = uri.replace("\0", "");
35+
if (uri.startsWith("/")) uri = uri.substring(1);
36+
if (uri.isEmpty()) uri = "index.html";
4837

49-
uri = defaultFile(uri);
38+
File root = new File(WEB_ROOT).getCanonicalFile();
39+
File file = new File(root, uri).getCanonicalFile();
5040

51-
// Path traversal check
52-
File root;
53-
File file;
54-
try {
55-
root = new File(webRoot).getCanonicalFile();
56-
file = new File(root, uri).getCanonicalFile();
57-
} catch (IOException e) {
58-
throw new RuntimeException(e);
41+
if (!file.toPath().startsWith(root.toPath())) {
42+
fileBytes = "403 Forbidden".getBytes();
43+
statusCode = SC_FORBIDDEN;
44+
return;
5945
}
6046

61-
if (!file.toPath().startsWith(root.toPath())) {
62-
fileBytes = "403 Forbidden".getBytes(java.nio.charset.StandardCharsets.UTF_8);
63-
response.setContentType(TEXT_PLAIN_CHARSET_UTF_8);
64-
response.setStatusCode(SC_FORBIDDEN);
65-
} else if (file.isFile()) {
66-
try {
67-
fileBytes = Files.readAllBytes(file.toPath());
68-
} catch (IOException e) {
69-
throw new RuntimeException(e);
70-
}
71-
response.setContentTypeFromFilename(file.toString());
72-
response.setStatusCode(SC_OK);
47+
if (file.isFile()) {
48+
fileBytes = Files.readAllBytes(file.toPath());
49+
statusCode = SC_OK;
7350
} else {
74-
File errorFile = new File(webRoot, "pageNotFound.html");
51+
File errorFile = new File(WEB_ROOT, "pageNotFound.html");
7552
if (errorFile.isFile()) {
76-
try {
77-
fileBytes = Files.readAllBytes(errorFile.toPath());
78-
} catch (IOException e) {
79-
throw new RuntimeException(e);
80-
}
81-
response.setContentTypeFromFilename(errorFile.toString());
53+
fileBytes = Files.readAllBytes(errorFile.toPath());
8254
} else {
83-
fileBytes = "404 Not Found".getBytes(java.nio.charset.StandardCharsets.UTF_8);
84-
response.setContentType(TEXT_PLAIN_CHARSET_UTF_8);
55+
fileBytes = "404 Not Found".getBytes();
8556
}
86-
response.setStatusCode(SC_NOT_FOUND);
57+
statusCode = SC_NOT_FOUND;
8758
}
88-
response.setBody(fileBytes);
8959
}
9060

91-
@Override
92-
public void handle(HttpRequest request, HttpResponseBuilder response) {
93-
handleGetRequest(request, response);
61+
public void sendGetRequest(OutputStream outputStream, String uri) throws IOException {
62+
handleGetRequest(uri);
63+
HttpResponseBuilder response = new HttpResponseBuilder();
64+
response.setStatusCode(statusCode);
65+
response.setContentTypeFromFilename(uri);
66+
response.setBody(fileBytes);
67+
outputStream.write(response.build());
68+
outputStream.flush();
9469
}
9570

96-
private String defaultFile(String uri) {
97-
if (uri == null || uri.isBlank()) {
98-
return "index.html";
99-
}
100-
String normalized = uri;
101-
while (normalized.startsWith("/")) {
102-
normalized = normalized.substring(1);
103-
}
104-
if (normalized.isEmpty()) {
105-
return "index.html";
106-
}
107-
if (normalized.endsWith("/")) {
108-
normalized = normalized + "index.html";
109-
}
110-
return normalized;
71+
public void sendHeadRequest(OutputStream outputStream, String uri) throws IOException {
72+
handleGetRequest(uri);
73+
HttpResponseBuilder response = new HttpResponseBuilder();
74+
response.setStatusCode(statusCode);
75+
response.setContentTypeFromFilename(uri);
76+
response.setHeader("Content-Length", String.valueOf(fileBytes.length));
77+
response.setBody(new byte[0]);
78+
outputStream.write(response.build());
79+
outputStream.flush();
11180
}
11281
}

0 commit comments

Comments
 (0)