Skip to content
Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
98b40a1
Initial commit med all kod (#16)
Kathify Feb 10, 2026
c1b5f70
build: configure pom.xml with needed plugin/tools. (#19)
eeebbaandersson Feb 10, 2026
bf4d977
Initial commit för tcp-server (#17)
Kathify Feb 10, 2026
148411e
Issue #12 (#21)
Xeutos Feb 10, 2026
9ac7b57
Feature/docker image builder issue#11 (#25)
Xeutos Feb 11, 2026
875d1ef
Feature/http parse headers (#18)
FeFFe1996 Feb 11, 2026
9289c7d
Feature/http response builder (#24)
JohanHiths Feb 11, 2026
6bdb1ef
Feature/http parse request line (#20)
FeFFe1996 Feb 11, 2026
781e34a
Add Bucket4j dependency in pom file (#40)
gvaguirres Feb 11, 2026
511b5ee
Add support for serving static files (#42)
codebyNorthsteep Feb 12, 2026
aaeba6d
Updates pom.xml, with jackson-dependencies, for config file (#48)
fredrikmohlen Feb 12, 2026
524f33c
* Move HTTP handling to a dedicated ConnectionHandler (#50)
donne41 Feb 12, 2026
b9600f6
ska vara klart nu har glömt att commit jobbet inser jag dock
Boppler12 Feb 17, 2026
07e47bf
har lagt till i line 20-25 samt line 29
Boppler12 Feb 17, 2026
8cc69d8
Feature/13 implement config file (#22)
MartinStenhagen Feb 17, 2026
8e0ab50
tester för FileCacheTest
Boppler12 Feb 17, 2026
c0e3de6
Enhancement/404 page not found (#53)
codebyNorthsteep Feb 17, 2026
bcb828c
Feature/issue59 run configloader (#61)
MartinStenhagen Feb 18, 2026
945d32b
23 define and create filter interface (#46)
eraiicphu Feb 18, 2026
3128ac7
Feature/mime type detection #8 (#47)
gitnes94 Feb 18, 2026
d4e7481
Dockerfile update (#52) (#63)
Xeutos Feb 19, 2026
5c80eaa
Added comprehensive README.MD (#67)
gitnes94 Feb 19, 2026
6950c14
Fix: Path traversal vulnerability in StaticFileHandler (#65)
apaegs Feb 19, 2026
fffdebf
updaterat i StaticFileHandler och skapat CacheFilter
Boppler12 Feb 20, 2026
c69b9dd
ska vara klart nu har glömt att commit jobbet inser jag dock
Boppler12 Feb 17, 2026
bfa3129
har lagt till i line 20-25 samt line 29
Boppler12 Feb 21, 2026
3b34b91
tester för FileCacheTest
Boppler12 Feb 17, 2026
b2ef693
updaterat i StaticFileHandler och skapat CacheFilter
Boppler12 Feb 20, 2026
515bc8c
Merge remote-tracking branch 'origin/issue36' into Server‑side-cachin…
Boppler12 Feb 21, 2026
0a53b7f
vad problem med github
Boppler12 Feb 21, 2026
ddd7b40
vad problem med github
Boppler12 Feb 21, 2026
0c47d58
nu ska det funka
Boppler12 Feb 21, 2026
78f7e21
Resolve port: CLI > config > default (#29)
viktorlindell12 Feb 23, 2026
dd0530a
tog i bort cache git för missar
Boppler12 Feb 23, 2026
7f73baf
la till concurrentHashMap i rad 8
Boppler12 Feb 23, 2026
86f2ba7
Refactor status codes to constants #71 (#77)
eeebbaandersson Feb 23, 2026
0a3c08c
refactored caching by removing FileCache completely; implemented an i…
Boppler12 Feb 23, 2026
56cc3e8
removed extra whitespace in MAX_CACHE_BYTES declaration
Boppler12 Feb 24, 2026
d20bb6f
removed things not used in code
Boppler12 Feb 24, 2026
103178a
fixed file path (#86)
gurkvatten Feb 24, 2026
e72f073
Fix path in Dockerfile for `www` directory copy operation (#87)
codebyNorthsteep Feb 24, 2026
ff4cd12
Feature/27 ipfilter (#70)
apaegs Feb 24, 2026
7652687
Feature/LocaleFilter (#81)
AntonAhlqvist Feb 25, 2026
3231ee1
ändringar som kodrabbit förstog
Boppler12 Feb 25, 2026
99f5fd7
ändringar som kodrabbit förstog
Boppler12 Feb 25, 2026
04cba97
ändringar som kodrabbit förstog
Boppler12 Feb 25, 2026
c158677
refactored StaticFileHandler to improve error handling, enhance sanit…
Boppler12 Feb 25, 2026
811ccce
fixa lite och hade glömt att commit vissa saker efter ändringar
Boppler12 Feb 25, 2026
1df4286
glömde av att fixa problem i cachefilter
Boppler12 Feb 25, 2026
19d0a48
updated CacheFilter to clean dead comments and import Paths/Path; imp…
Boppler12 Feb 25, 2026
d4227b0
fixat så problem som kommer up vid commit
Boppler12 Feb 25, 2026
3497f8a
Merge branch 'main' into issue36
Boppler12 Feb 25, 2026
bb0d51a
kom up problem när github kör ska ha fixat det tror jag
Boppler12 Feb 25, 2026
f5b2f44
kom up problem när github kör ska ha fixat det tror jag
Boppler12 Feb 25, 2026
d6f1d26
Create LoggFilter (#83)
AnnaZiafar Feb 25, 2026
704f8d8
Gjort det coderabbit bätt mig om
Boppler12 Feb 25, 2026
3f107c6
fixa problem
Boppler12 Feb 25, 2026
27e627c
Return status code 500 (#79)
AnnaZiafar Feb 25, 2026
db0c574
Feature/LocaleFilterCookie (#92)
AntonAhlqvist Feb 25, 2026
b7154fa
added brotli4j (#94)
gurkvatten Feb 25, 2026
d87171b
fixat mer
Boppler12 Feb 25, 2026
c74b88b
fixat mer från coderabbit
Boppler12 Feb 25, 2026
baa3981
daniel har hjälp mig med ändringar
Boppler12 Feb 25, 2026
fa1599a
Issue/69 remove html concat (#73)
Rickank Feb 26, 2026
f3bb1ac
daniel har hjälp mig med ändringar
Boppler12 Feb 26, 2026
2f8312b
fixat så uri rensas och återanvänds, lade till ny metod för sanering
Boppler12 Feb 26, 2026
32cbdd3
Merge branch 'main' into issue36
Boppler12 Feb 26, 2026
331615a
fixa compleir issue
Boppler12 Feb 26, 2026
1be780f
behövde fixa saker efter en rebase
Boppler12 Feb 26, 2026
56ba520
should have made it according to the mall given
Boppler12 Feb 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Java CI with Maven

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Get Java Version
run: |
JAVA_VERSION=$(mvn help:evaluate "-Dexpression=maven.compiler.release" -q -DforceStdout)
echo "JAVA_VERSION=$JAVA_VERSION" >> $GITHUB_ENV

- name: Set up JDK ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v4
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: 'temurin'
cache: maven

- name: Compile with Maven
run: mvn -B compile --file pom.xml

- name: Test with Maven
run: mvn -B test --file pom.xml
35 changes: 35 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Publish Docker Image to Github Packages on Release
on:
release:
types:
- published
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6.0.2
- uses: docker/setup-qemu-action@v3.7.0
- uses: docker/setup-buildx-action@v3.12.0
- name: Log in to GHCR
uses: docker/login-action@v3.7.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5.10.0
with:
images: ghcr.io/ithsjava25/webserver
- name: Build and push
uses: docker/build-push-action@v6.18.0
with:
context: .
push: true
platforms: linux/amd64, linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

11 changes: 11 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM maven:3-eclipse-temurin-25-alpine AS build
WORKDIR /build
COPY src/ src/
COPY pom.xml pom.xml
RUN mvn compile

FROM eclipse-temurin:25-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=build /build/target/classes/ /app/
ENTRYPOINT ["java", "-classpath", "/app", "org.example.App"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Get full Dockerfile content
cat -n Dockerfile

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 515


🏁 Script executed:

# Check pom.xml for dependencies and maven plugins
cat -n pom.xml

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 8522


🏁 Script executed:

# Find StaticFileHandler and examine how it resolves the www directory
find . -name "StaticFileHandler.java" -type f | head -5

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 158


🏁 Script executed:

# Check if www directory exists
find . -type d -name "www" | head -10

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 113


🏁 Script executed:

# List root-level directories and files to understand project structure
ls -la | head -20

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 672


🏁 Script executed:

# Examine StaticFileHandler to understand how it resolves the www directory path
cat -n src/main/java/org/example/StaticFileHandler.java

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 1680


🏁 Script executed:

# Check if www directory is referenced in App.java or other main code
find src/main/java -name "App.java" -type f -exec cat -n {} \;

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 287


🏁 Script executed:

# Look for any hardcoded paths or configuration related to www
rg -n "www" src/

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 209


Runtime image is missing dependency JARs and the www/ static content directory.

The build stage only runs mvn compile, which puts compiled classes under target/classes/ but does not download dependency JARs into that directory. The runtime stage copies only target/classes/ to /app/ and sets the classpath to /app. Since pom.xml declares non-test dependencies (bucket4j, jackson-databind, jackson-dataformat-yaml), the application will fail at runtime with ClassNotFoundException.

Additionally, StaticFileHandler uses a relative path "www" to locate static content. The Dockerfile does not copy the www/ directory into the image, so the application will fail to serve static files at runtime.

Consider using mvn package with the maven-dependency-plugin:copy-dependencies goal (or the shade/assembly plugin to create a fat JAR), add a COPY www/ /app/www/ step in the runtime stage, and set WORKDIR /app to ensure the relative path resolves correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 5 - 10, The runtime image will miss dependency JARs
and the www static folder; change the build stage to produce and collect runtime
dependencies (e.g., replace RUN mvn compile with RUN mvn package and run the
maven-dependency-plugin:copy-dependencies or build a fat JAR with the
shade/assembly plugin) so dependency JARs are available, adjust the runtime COPY
from the build stage to include those dependency JARs (or copy the fat JAR)
instead of only target/classes (see COPY --from=build /build/target/classes/),
add COPY www/ /app/www/ and set WORKDIR /app so StaticFileHandler's relative
"www" path resolves, and update ENTRYPOINT/classpath invocation (ENTRYPOINT
["java", "-classpath", "/app:...dependencies...", "org.example.App"] or point to
the fat JAR) accordingly.

USER appuser
67 changes: 66 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.release>23</maven.compiler.release>
<maven.compiler.release>25</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>6.0.2</junit.jupiter.version>
<assertj.core.version>3.27.7</assertj.core.version>
<mockito.version>5.21.0</mockito.version>
<bucket4j.version>8.14.0</bucket4j.version>

</properties>

<dependencies>
<dependency>
<groupId>com.bucket4j</groupId>
<artifactId>bucket4j_jdk17-core</artifactId>
<version>${bucket4j.version}</version>
</dependency>
Comment on lines +22 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -rn 'bucket4j|io\.github\.bucket4j|Bucket4j|Bandwidth|Refill|Bucket\.builder' --type=java src/

Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template

Length of output: 90


bucket4j_jdk17-core is declared as a compile-scope dependency but is not used in any Java source files.

Verify whether it's used in files outside this scope (configuration loaders, tests, etc.), or remove it if truly unused to reduce classpath and Docker image size.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pom.xml` around lines 22 - 26, The declared Maven dependency artifactId
'bucket4j_jdk17-core' (groupId com.bucket4j) appears unused in production
sources; inspect usages across the repository (including config loaders,
integration tests, and any non-Java resources) to confirm, and then either
remove this <dependency> entry from pom.xml or change its scope to test/provided
as appropriate; update pom.xml by deleting or modifying the dependency and run
mvn dependency:analyze and a full build/test to ensure nothing breaks before
committing.

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
Expand All @@ -28,12 +36,36 @@
<version>${assertj.core.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.3.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>tools.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>3.0.3</version>
</dependency>

</dependencies>
<build>
<plugins>
Expand Down Expand Up @@ -118,6 +150,39 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<version>1.22.0</version>
<dependencies>
<dependency>
<groupId>org.pitest</groupId>
<artifactId>pitest-junit5-plugin</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<java>
<removeUnusedImports/>
<formatAnnotations/>
</java>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>
check
</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
4 changes: 2 additions & 2 deletions src/main/java/org/example/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

public class App {
public static void main(String[] args) {
System.out.println("Hello There!");
new TcpServer(8080).start();
}
}
}
42 changes: 42 additions & 0 deletions src/main/java/org/example/ConnectionHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.example;

import org.example.httpparser.HttpParser;

import java.io.IOException;
import java.net.Socket;

public class ConnectionHandler implements AutoCloseable {

Socket client;
String uri;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

client and uri fields should be private.

Both are currently package-private, exposing ConnectionHandler's internal state to the entire package.

♻️ Proposed fix
-    Socket client;
-    String uri;
+    private Socket client;
+    private String uri;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Socket client;
String uri;
private Socket client;
private String uri;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/ConnectionHandler.java` around lines 10 - 11, The
fields client and uri in ConnectionHandler are package-private; make them
private by changing their declarations to private Socket client; and private
String uri; and then update any direct package accesses to use new accessors or
constructor parameters—add/getClient(), add/getUri() or modify existing
constructor/methods in ConnectionHandler to operate on the private fields so no
external code accesses them directly.


public ConnectionHandler(Socket client) {
this.client = client;
}

public void runConnectionHandler() throws IOException {
StaticFileHandler sfh = new StaticFileHandler();
HttpParser parser = new HttpParser();
parser.setReader(client.getInputStream());
parser.parseRequest();
parser.parseHttp();
resolveTargetFile(parser.getUri());
sfh.sendGetRequest(client.getOutputStream(), uri);
}

private void resolveTargetFile(String uri) {
if (uri.matches("/$")) { //matches(/)
this.uri = "index.html";
} else if (uri.matches("^(?!.*\\.html$).*$")) {
this.uri = uri.concat(".html");
} else {
this.uri = uri;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

resolveTargetFile preserves the leading / for all non-root URIs, causing a path-resolution bug in StaticFileHandler.

String.matches("/$") only matches the exact string "/". Every other URI (e.g. "/index.html", "/about") keeps its leading slash. When that value is later passed to new File(WEB_ROOT, uri) in StaticFileHandler, Java's File(parent, child) contract ignores parent when child is an absolute path (starts with /), so the www/ root is completely bypassed for every request other than /. This also makes the complex regex unnecessary — equivalent logic via simple string operations is clearer and eliminates the bug:

♻️ Proposed fix
     private void resolveTargetFile(String uri) {
-        if (uri.matches("/$")) { //matches(/)
-            this.uri = "index.html";
-        } else if (uri.matches("^(?!.*\\.html$).*$")) {
-            this.uri = uri.concat(".html");
-        } else {
-            this.uri = uri;
-        }
+        // Strip leading slash so new File(WEB_ROOT, uri) resolves relative to WEB_ROOT
+        String normalized = uri.startsWith("/") ? uri.substring(1) : uri;
+        if (normalized.isEmpty()) {
+            this.uri = "index.html";
+        } else if (!normalized.endsWith(".html")) {
+            this.uri = normalized + ".html";
+        } else {
+            this.uri = normalized;
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/ConnectionHandler.java` around lines 27 - 35,
resolveTargetFile currently preserves the leading '/' for non-root URIs causing
StaticFileHandler's new File(WEB_ROOT, uri) to treat uri as absolute and bypass
the WEB_ROOT; change resolveTargetFile to: if uri equals "/" set
this.uri="index.html", else remove any leading '/' from uri, then if the
resulting uri does not end with ".html" append ".html", otherwise keep it —
avoid the regex use and operate with simple string methods so new File(WEB_ROOT,
this.uri) correctly resolves under WEB_ROOT.

}

@Override
public void close() throws Exception {
client.close();
}
}
28 changes: 28 additions & 0 deletions src/main/java/org/example/FileCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.example;

import java.util.HashMap;
import java.util.Map;

public class FileCache {
private final Map<String, byte []> cache = new HashMap<>();

public boolean contains (String key) {
return cache.containsKey(key);
}

public byte[] get(String key) {
return cache.get(key);
}

public void put(String key, byte[] value){
cache.put(key, value);
}
public void clear() {
cache.clear();
}

public int size() {
return cache.size();
}

}
42 changes: 42 additions & 0 deletions src/main/java/org/example/StaticFileHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.example;

import org.example.http.HttpResponseBuilder;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.Map;

public class StaticFileHandler {
private static final String WEB_ROOT = "www";
private byte[] fileBytes;
private final FileCache cache = new FileCache();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

public StaticFileHandler(){}

private void handleGetRequest(String uri) throws IOException {
if (cache.contains(uri)) {
System.out.println("✓ Cache hit for: " + uri);
fileBytes = cache.get(uri);
return;
}
System.out.println("✗ Cache miss for: " + uri);
File file = new File(WEB_ROOT, uri);
fileBytes = Files.readAllBytes(file.toPath());
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

cache.put(uri, fileBytes);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

public void sendGetRequest(OutputStream outputStream, String uri) throws IOException{
handleGetRequest(uri);
HttpResponseBuilder response = new HttpResponseBuilder();
response.setHeaders(Map.of("Content-Type", "text/html; charset=utf-8"));
response.setBody(fileBytes);
PrintWriter writer = new PrintWriter(outputStream, true);
writer.println(response.build());
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

}

}
36 changes: 36 additions & 0 deletions src/main/java/org/example/TcpServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.example;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {

private final int port;

public TcpServer(int port) {
this.port = port;
}

public void start() {
System.out.println("Starting TCP server on port " + port);

try (ServerSocket serverSocket = new ServerSocket(port)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Static analysis: plain ServerSocket transmits data in cleartext (CWE-319).

The static analysis tool flags this as "Cleartext Transmission of Sensitive Information." For an internal/educational HTTP server this is likely intentional, but if the server ever handles any credentials or sensitive content in production, SSLServerSocket via SSLServerSocketFactory should be used instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/TcpServer.java` at line 18, The code opens a plain
ServerSocket in TcpServer (try (ServerSocket serverSocket = new
ServerSocket(port))) which allows cleartext transmission; change it to create an
SSLServerSocket from an SSLServerSocketFactory/SSLContext instead: load or
reference the keystore/SSLContext configuration, initialize an SSLContext (or
obtain SSLServerSocketFactory via
SSLServerSocketFactory.getDefault()/SSLContext.getSocketFactory()), create an
SSLServerSocket bound to the same port, and replace usages of the ServerSocket
variable with the SSLServerSocket instance so all accepted sockets are
TLS-protected (ensure proper resource handling in the try-with-resources block).

while (true) {
Socket clientSocket = serverSocket.accept(); // block
System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());
Thread.ofVirtual().start(() -> handleClient(clientSocket));
}
} catch (IOException e) {
throw new RuntimeException("Failed to start TCP server", e);
}
Comment on lines +24 to +31
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Any transient IOException from accept() terminates the entire server loop.

The single catch (IOException e) wraps both startup failures (legitimate fatal) and runtime accept() errors (often transient) identically. A brief network blip or OS-level error causes the loop — and the server — to die permanently.

Consider distinguishing startup failure from runtime accept errors:

♻️ Proposed fix
 try (ServerSocket serverSocket = new ServerSocket(port)) {
     while (true) {
-        Socket clientSocket = serverSocket.accept();
-        System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());
-        Thread.ofVirtual().start(() -> handleClient(clientSocket));
+        try {
+            Socket clientSocket = serverSocket.accept();
+            System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());
+            Thread.ofVirtual().start(() -> handleClient(clientSocket));
+        } catch (IOException e) {
+            if (serverSocket.isClosed()) break; // intentional shutdown
+            System.err.println("Transient accept error, continuing: " + e.getMessage());
+        }
     }
 } catch (IOException e) {
     throw new RuntimeException("Failed to start TCP server", e);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while (true) {
Socket clientSocket = serverSocket.accept(); // block
System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());
Thread.ofVirtual().start(() -> handleClient(clientSocket));
}
} catch (IOException e) {
throw new RuntimeException("Failed to start TCP server", e);
}
while (true) {
try {
Socket clientSocket = serverSocket.accept(); // block
System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());
Thread.ofVirtual().start(() -> handleClient(clientSocket));
} catch (IOException e) {
if (serverSocket.isClosed()) break; // intentional shutdown
System.err.println("Transient accept error, continuing: " + e.getMessage());
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to start TCP server", e);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/TcpServer.java` around lines 19 - 26, The current
catch lumps startup and runtime accept() IOExceptions together causing the
server to exit on transient errors; modify TcpServer so the initial ServerSocket
creation failure still throws (keep the outer try for startup), but move the
accept loop into its own try/catch: inside the while(true) wrap
serverSocket.accept() (and Thread.ofVirtual().start(() ->
handleClient(clientSocket))) in a small try that catches IOException, logs the
error and remote info, and continues the loop rather than rethrowing; only treat
exceptions from ServerSocket creation (before entering the loop) as fatal.
Reference: ServerSocket/ serverSocket.accept(), TcpServer class, and
handleClient().

}

private void handleClient(Socket client) {
try (ConnectionHandler connectionHandler = new ConnectionHandler(client)) {
connectionHandler.runConnectionHandler();
} catch (Exception e) {
throw new RuntimeException("Error handling client connection " + e);
}
Comment on lines +45 to +60
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Original exception cause and stack trace are silently discarded.

"Error handling client connection " + e calls e.toString() — only the class name and message are embedded in the new exception's message string. The original stack trace, cause chain, and exception type are lost. Since this fires inside a virtual thread, the JVM invokes the thread's UncaughtExceptionHandler with the wrapper RuntimeException, which means whatever is printed to stderr shows only the synthetic wrapper's frames — not where the real error occurred.

Pass e as the cause argument instead (and consider simply logging rather than rethrowing, since no caller in the virtual thread catches this):

🐛 Proposed fix
 } catch (Exception e) {
-    throw new RuntimeException("Error handling client connection " + e);
+    throw new RuntimeException("Error handling client connection", e);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (Exception e) {
throw new RuntimeException("Error handling client connection " + e);
}
} catch (Exception e) {
throw new RuntimeException("Error handling client connection", e);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/TcpServer.java` around lines 32 - 34, The catch
block in TcpServer that currently does `throw new RuntimeException("Error
handling client connection " + e)` loses the original exception type and stack
trace; change it to throw the RuntimeException with the original exception as
the cause (i.e., construct the RuntimeException with a message and pass the
caught Exception e as the cause) or alternatively log the error with the full
exception (using the logger in the class) instead of rethrowing inside the
virtual thread so the original stack trace and cause chain are preserved; locate
the catch in the client-handling method in class TcpServer and update it
accordingly.

}
}
Loading