Conversation
* chore: Update POM to Java 25 and rename artifactId/groupId * update folder name from example to juv25d --------- Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
* http parser * Bunny fixes. (only using input stream to recieve requests) * Bunny review improvements * Improved http parser ReadLine helper method to eliminate dependency on mark() and reset(). Implemented handleClient() using socket as a try-with-resources to avoid memory leakage in case of exception thrown by httpparser-methods. * NumberFormatException fix on line 53 -> 60 * chore: Update POM to Java 25 and rename artifactId/groupId (#11) * chore: Update POM to Java 25 and rename artifactId/groupId * update folder name from example to juv25d --------- Co-authored-by: WHITEROSE <firasmoussa60@gmail.com> * resolve conflicts --------- Co-authored-by: Kristina <kristina0x7@gmail.com> Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
* Add ServerLogging.java as separate class for logging. Implement said class in SocketServer.java to return logging information upon opening socket and user connecting to server. * Update ServerLogging.java to include a static initializer block and an empty utility class to prevent instantiation. * Update ServerLogging.java to reference same class in getLogger argument. * Update ServerLogging.java to check if handler has already been instantiated and allow for log level to be set by args in JVM (default level 'INFO' if no args provided). * normalize logging statements to be consistent * remove unused imports * Update SockerServer.java to properly log server socket errors. --------- Co-authored-by: Mats Rönnqvist <mats.f.ronnqvist@gmail.com> Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
* update POM with pitest * add junit plugin dependency
Rebased 4 commits in this PR.
…#27) * Fix PiTest by defining argLine and removing invalid Mockito javaagent * self close argline
…edicated ConnectionHandler. (#28) * Rename SocketServer to Server Move HTTP request handling logic to a dedicated ConnectionHandler. * Convert createSocket to an instance method named start(). * Replace console prints with Logger in ConnectionHandler * Fixed typo in ConnectionHandler * refactor Server and ConnectionHandler: - Inject Logger inside of constructor - Inject handlerFactory into the Server that handles creation of a new ConnectionHandler on each request - Remove HttpParser from Server as it is not handling the parsing of a request * accept ConnectionHandlerFactory and not a specific implementation * normalize handlerFactory name --------- Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
…tests. (#34) * Add testing for ServerLogging.java. Configure ServerLogging.java to improve its testability. * Update logger_shouldNotAddDuplicateHandlers test to properly test non-inclusion of duplicates. * Update ServerLogging.java to guard against invalid level string entries in configure method (logger.parse => setLevel).
* http parser * Bunny fixes. (only using input stream to recieve requests) * Bunny review improvements * Improved http parser ReadLine helper method to eliminate dependency on mark() and reset(). Implemented handleClient() using socket as a try-with-resources to avoid memory leakage in case of exception thrown by httpparser-methods. * NumberFormatException fix on line 53 -> 60 * Added foundation for Filters and Plugins. Added FilterChain to use created filters, and a Pipeline class to handle the workflow (Client → Filters → Plugin → Response → Client). Modified SocketServer handleClient() to use FilterChain. Added example code in App.java for Pipeline usage. TODO: Initialize HttpResponse class * Fixing build fail * add init and destroy to filter interface * add methods to pipeline class * Add servlet-style filter pipeline with lifecycle support * add documentation about temporary server impl * test: verify filters execute in order and plugin is called last * test: add coverage for filter blocking, lifecycle init, and empty pipeline * add loggingfiltertest * fix: construct HttpResponse with required arguments * fix: construct HttpResponse with required arguments * Update FilterChainImpl tests to use full HttpResponse constructor to ensure compatibility with future implementations * remove duplicated class --------- Co-authored-by: Kristina M <kristina0x7@gmail.com> Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
…39) * feat: make HttpResponse mutable and implement NotFoundPlugin default - Updated HttpResponse to be mutable to allow filters and plugins to modify responses. - Implemented NotFoundPlugin as a default fallback for the Pipeline. - Added null safety check in Pipeline.setPlugin. - Added unit tests for Pipeline default behavior and NotFoundPlugin. * ran mvn spotless:apply to fix violations * Rabbit comment fixes * Review fixes * Final buggfixes for this PR (again)
* Implement static file handler with security and tests Core Implementation: - Add StaticFileHandler for serving files from /resources/static/ - Add MimeTypeResolver for Content-Type detection - Add security validation to prevent path traversal attacks Testing: - Add MimeTypeResolverTest (15 test cases) - Add StaticFileHandlerTest (20+ test cases) - All tests passing Example Files: - Add index.html demo page with gradient styling - Add styles.css for professional styling - Add app.js for JavaScript functionality demo Note: Integration with Server/ConnectionHandler will be added after PR #28 merges to avoid conflicts. Foundation work for #18 * Introduce ADR structure and first ADR - Add ADR README explaining the ADR process for the team - Add TEMPLATE for writing future ADRs - Add ADR-001 documenting static file serving architecture Closes #16 * Rename SocketServer to Server Move HTTP request handling logic to a dedicated ConnectionHandler. (#28) * Rename SocketServer to Server Move HTTP request handling logic to a dedicated ConnectionHandler. * Convert createSocket to an instance method named start(). * Replace console prints with Logger in ConnectionHandler * Fixed typo in ConnectionHandler * refactor Server and ConnectionHandler: - Inject Logger inside of constructor - Inject handlerFactory into the Server that handles creation of a new ConnectionHandler on each request - Remove HttpParser from Server as it is not handling the parsing of a request * accept ConnectionHandlerFactory and not a specific implementation * normalize handlerFactory name --------- Co-authored-by: WHITEROSE <firasmoussa60@gmail.com> * Add testing for ServerLogging.java. Configure ServerLogging.java for tests. (#34) * Add testing for ServerLogging.java. Configure ServerLogging.java to improve its testability. * Update logger_shouldNotAddDuplicateHandlers test to properly test non-inclusion of duplicates. * Update ServerLogging.java to guard against invalid level string entries in configure method (logger.parse => setLevel). * feature/FilterPlugin (#17) * http parser * Bunny fixes. (only using input stream to recieve requests) * Bunny review improvements * Improved http parser ReadLine helper method to eliminate dependency on mark() and reset(). Implemented handleClient() using socket as a try-with-resources to avoid memory leakage in case of exception thrown by httpparser-methods. * NumberFormatException fix on line 53 -> 60 * Added foundation for Filters and Plugins. Added FilterChain to use created filters, and a Pipeline class to handle the workflow (Client → Filters → Plugin → Response → Client). Modified SocketServer handleClient() to use FilterChain. Added example code in App.java for Pipeline usage. TODO: Initialize HttpResponse class * Fixing build fail * add init and destroy to filter interface * add methods to pipeline class * Add servlet-style filter pipeline with lifecycle support * add documentation about temporary server impl * test: verify filters execute in order and plugin is called last * test: add coverage for filter blocking, lifecycle init, and empty pipeline * add loggingfiltertest * fix: construct HttpResponse with required arguments * fix: construct HttpResponse with required arguments * Update FilterChainImpl tests to use full HttpResponse constructor to ensure compatibility with future implementations * remove duplicated class --------- Co-authored-by: Kristina M <kristina0x7@gmail.com> Co-authored-by: WHITEROSE <firasmoussa60@gmail.com> * Add charset=utf-8 to text-based Content-Type headers * Integrate StaticFileHandler with Pipeline architecture * Refactor to use immutable fields and clean up unused setters; update with usage notes for future integration. * Enhance StaticFilesPlugin to properly handle headers with map iteration; simplify and clarify class documentation. * Update tests to verify Content-Type headers include charset=utf-8 * connect StaticFilesPlugin --------- Co-authored-by: johanbriger <johanbriger@gmail.com> Co-authored-by: WHITEROSE <firasmoussa60@gmail.com> Co-authored-by: Mats Rönnqvist <203552386+bamsemats@users.noreply.github.com> Co-authored-by: Linus Westling <141355850+LinusWestling@users.noreply.github.com> Co-authored-by: Kristina M <kristina0x7@gmail.com>
* bump pom version for release * revert pom version * fix: docker release workflow to run on tag push * fix: update tag ref in docker release workflow * fix: update temurine version in DockerFile to match pom version 25 * fix: configure maven-jar-plugin to find the main class for manifest
* Add test for valid GET request and fix key case-sensitivity in header parsing Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify empty request throws exception Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify malformed request throws exception Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify valid query string Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify null and empty requests throws exception Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify malformed header line in get request throws exception Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify valid post request Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Verify invalid or negative content length throws exception Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Add test documentation and improve test readability for HttpParser Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Update tests and ensure UTF-8 handling; use case-insensitive headers in HttpParser Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Refactor: Introduce constant for "Content-Length" header in HttpParser --------- Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com>
…ons (#52) * add application properties and a ConfigLoader to load set configurations * integrate ConfigLoader into ServerLogging to read logging.level * inject Server port from ConfigLoader into constructor
* test: add global filter execution test * feat: implement global filter support with ordering * test: add route specific filter test * feat: add route specific filter matching logic * update main comments with upcoming filter impl * chore: run spotless:apply to fix unused imports and formatting * bunny-fix * refactor: optimize route filter lookup with Map structure * small fix * fix: make global filter cache thread-safe and immutable * implement the correct plugins so server works as intended and move comments to a doc --------- Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
* Add IP filter skeleton with placeholder IP * Add IP filter and support client IP in HttpRequest * Add remote IP to HttpRequest in ConnectionHandler * Configured IP filter whitelist for localhost * Enable IP filter with open access for development * Update tests for HttpRequest remote IP failed * Document IP filter configuration in App * Resolved merge conflict in App pipeline configuration
…eadme.md (#63) * Update index.html to include a nav bar at the top, styles.css for adjustments to frontend aspects, add readme.html to host readme.md, basic javascript for loading markdown language content. * Update frontend logic to avoid errors loading data from readme.md. Smoothed out transitions between sites from navigation. * Update minor bugs and duplicate css code lines. * Update minor bugs and duplicate css code lines. * Update for sanitization of readme.md through DOMPurify. Added dependency min.js and its callback in index.html and readme.html. Removed superfluous markdown.js.
* Add Maven Shade Plugin for building a fat JAR; update README with detailed project overview and usage instructions * Update project version to 1.0.2-beta in pom.xml * Update README to reflect fat JAR packaging using Maven Shade Plugin * fix: specify final jar name so the DockerBuild picks the correct one, rollback pom version * rollback POM version to original * update README with dynamic tag and correct port number * update README with the dynamic specified jar file generated from maven-shaded --------- Co-authored-by: WHITEROSE <firasmoussa60@gmail.com>
* Refactor plugin handling by introducing `Router` abstraction; added `SimpleRouter` implementation. Updated pipeline and tests to support new routing system. * Refactor plugin handling by introducing `Router` abstraction; added `SimpleRouter` implementation. Updated pipeline and tests to support new routing system. * Improve wildcard routing in `SimpleRouter` with longest-prefix match logic; add test coverage for specific and wildcard match scenarios.
* add testcontainers dependency to pom.xml * adds 2 simple integration tests usiing Testcontainers to AppIT.java * changes from *.jar to app.jar in JAR path in Dockerfile to avoid copy conflict * changes AppIT.java according to code rabbit suggestion
* Add Bucket4j dependency to pom.xml for rate-limiting support Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Introduce RateLimitingFilter with Bucket4j for per-IP request throttling Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Work In Progress: Implement per-IP rate-limiting logic in RateLimitingFilter using Bucket4j and add response handling for rate limit exceeded * Finalize and integrate RateLimitingFilter with improved logging, validation, and server cleanup. Add to App pipeline and configure properties. Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Add unit tests for RateLimitingFilter to verify per-IP request handling, rate limit enforcement, and cleanup behavior Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Refactor RateLimitingFilter and tests: simplify comments in filter, improve test method naming, and add validation test Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Add Javadoc to RateLimitingFilter and its tests for improved clarity and documentation Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Remove StaticFilesPlugin from the App pipeline configuration Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Make RateLimitingFilter configuration dynamic and improve response handling in tests Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Add rate-limiting configuration to ConfigLoader and update App pipeline to use dynamic values Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Increase rate-limiting burst capacity to 100 in configuration properties Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> * Add configurable flag to enable/disable rate-limiting in App pipeline and ConfigLoader Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com> --------- Co-authored-by: Mattias Hagström <mattiashagstrommusic@gmail.com>
* Add `RedirectRule` class to handle URL redirection logic with support for exact and wildcard path matching
* Add `RedirectFilter` to handle HTTP URL redirection logic using configurable rules
* WIP: Save current work
* Fix HttpResponse empty constructor to initialize fields
Initialize headers map and body array in empty constructor to prevent
NullPointerException when setHeader() or setBody() are called.
Before:
HttpResponse() {} // headers = null, body = null
After:
HttpResponse() {
this.headers = new LinkedHashMap<>();
this.body = new byte[0];
}
This fix is required for RedirectFilter and any other code that creates
empty HttpResponse objects and modifies them using setters.
Fixes crashes in RedirectFilterTest.
* Improve wildcard matching in `RedirectRule` by using `Pattern.quote` for safer and more precise regex generation.
* Update `RedirectFilterTest` to include client IP address in test request creation
---------
Co-authored-by: Kristina M <kristina0x7@gmail.com>
* remove sourcemapping url comment from the JS file * remove stack trace from the 404 log message to console
* Add SecurityHeadersFilter for hardened HTTP responses * Add SecurityHeadersFilter for hardened HTTP responses * Changed X-XSS-Protection value to recommended 0, * address code review feedback from CodeRabbit * Add @global annotation to SecurityHeadersFilter for automatic registration * Removed line of code in App.java
#76) * Added IpFilterTest class with unit test verifying IpFilter allows whitelisted IPs. * Fix IpFilterTest to verify response interaction instead of mock state * Added unit test for blocking IP that is not in the whitelist, results in 403 Forbidden response. Fixed HttpResponse construtors to always initialize headers and body to prevent NPE when filters call setHeader or setBody. * Update IpFilter whitelist allow test to use real HttpResponse * Assert expected status code in IpFilter whitelist allow test
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughAdds a full Java HTTP server implementation with request parsing/writing, filter-based pipeline, router/plugin dispatch, static file serving, YAML config, logging, build/CI and Docker workflows, frontend static assets, extensive documentation (ADRs), and a comprehensive test suite. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Server
participant ConnHandler as ConnectionHandler
participant Parser as HttpParser
participant Pipeline
participant Filters as FilterChain
participant Router
participant Plugin
Client->>Server: TCP connect + send HTTP request
Server->>ConnHandler: create & start handler (virtual thread)
ConnHandler->>Parser: parse(InputStream)
Parser-->>ConnHandler: HttpRequest
ConnHandler->>Pipeline: createChain(HttpRequest)
Pipeline-->>ConnHandler: FilterChainImpl
ConnHandler->>Filters: doFilter(req, res)
rect rgba(100, 150, 255, 0.5)
Note over Filters: Execute ordered filters (global + route-specific)
Filters->>Filters: filter[0].doFilter(...)
Filters->>Filters: filter[1].doFilter(...)
end
Filters->>Router: resolve(HttpRequest)
Router-->>Filters: Plugin
Filters->>Plugin: handle(req, res)
Plugin-->>Filters: (response set/modified)
Filters-->>ConnHandler: chain complete
ConnHandler->>ConnHandler: HttpResponseWriter.write(out, response)
ConnHandler-->>Client: HTTP/1.1 response bytes
sequenceDiagram
participant Pipeline
participant Global as GlobalFilters
participant Route as RouteFilters
participant Router
Pipeline->>Global: collect ordered global filters
Pipeline->>Route: collect matching route filters (exact + wildcard)
Note over Global,Route: Merge and sort by order
Pipeline->>Router: create FilterChainImpl(filters, router)
Router-->>Pipeline: FilterChainImpl returned
Note over Pipeline: Execution will run filters in sorted order then Router.resolve -> Plugin.handle
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 15
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (13)
.mvn/wrapper/maven-wrapper.properties-3-3 (1)
3-3:⚠️ Potential issue | 🟡 MinorMaven version in wrapper (3.8.7) drifts from the Docker build image (3.9.x).
Local
./mvnwruns will download and use Maven 3.8.7, while the Dockerfile build stage pullsmaven:3.9-eclipse-temurin-25. This can cause subtle build differences (plugin behaviour, dependency resolution) between local and CI/Docker builds. Align both to the same version, preferably the already-used 3.9.x line.🔧 Proposed fix
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.mvn/wrapper/maven-wrapper.properties at line 3, Update the Maven wrapper version to match the Docker build image by editing the distributionUrl property in .mvn/wrapper/maven-wrapper.properties (the distributionUrl entry currently points to apache-maven/3.8.7); change it to the 3.9.x artifact that matches the Dockerfile's maven:3.9-eclipse-temurin-25 image so local ./mvnw uses the same Maven release as CI/Docker.src/main/resources/static/readme.html-18-18 (1)
18-18:⚠️ Potential issue | 🟡 MinorMissing semicolons on HTML numeric character references.
🔍and🔎should be🔍and🔎respectively. Most browsers recover, but omitting the semicolon is invalid HTML and can cause parsing ambiguity when followed by certain characters.Proposed fix
- <h1>🔍 ReadMe 🔎</h1> + <h1>🔍 ReadMe 🔎</h1>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/resources/static/readme.html` at line 18, The HTML uses numeric character references without trailing semicolons in the <h1> element (🔍 and 🔎); update those references to include semicolons (🔍 and 🔎) so the browser parses them as valid character references. Locate the <h1> line in readme.html and add the missing semicolons after each numeric entity to fix the HTML validity.src/main/resources/static/index.html-46-48 (1)
46-48:⚠️ Potential issue | 🟡 MinorInconsistent script path:
js/purify.min.jsis missing the leading/.Lines 46 and 48 use absolute paths (
/js/marked.min.js,/js/app.js), but line 47 uses a relative path (js/purify.min.js). This will break if the page is ever served at a nested URL path. Keep paths consistent.Proposed fix
<script src="/js/marked.min.js"></script> -<script src="js/purify.min.js"></script> +<script src="/js/purify.min.js"></script> <script src="/js/app.js"></script>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/resources/static/index.html` around lines 46 - 48, Update the inconsistent script src for purify by changing the relative path "js/purify.min.js" to the absolute path "/js/purify.min.js" so it matches the other script tags ("/js/marked.min.js" and "/js/app.js"); locate the <script> tag referencing purify.min.js in index.html and update its src to include the leading slash to avoid breakage when served from nested URLs.src/main/resources/static/readme.html-36-38 (1)
36-38:⚠️ Potential issue | 🟡 MinorSame inconsistent script path as
index.html.Line 37 uses relative
js/purify.min.jswhile lines 36 and 38 use absolute paths. Apply the same fix here.Proposed fix
<script src="/js/marked.min.js"></script> -<script src="js/purify.min.js"></script> +<script src="/js/purify.min.js"></script> <script src="/js/app.js"></script>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/resources/static/readme.html` around lines 36 - 38, The script tag for purify is using a relative path while the others use absolute paths; update the src for purify.min.js to use the absolute path "/js/purify.min.js" so it matches the other script includes (refer to the script elements loading marked.min.js, purify.min.js, and app.js in the readme.html snippet).src/main/resources/static/js/app.js-72-81 (1)
72-81:⚠️ Potential issue | 🟡 MinorClick handler intercepts external
.htmllinks.Any
<a>whosehrefends in.html—including absolute cross-origin URLs—will be captured and fetched vianavigate(), which will fail or produce confusing behaviour. Add an origin or relative-path guard.Proposed fix
document.addEventListener("click", (e) => { const link = e.target.closest("a"); if (!link) return; const href = link.getAttribute("href"); if (!href || !href.endsWith(".html")) return; + if (href.startsWith("http") || href.startsWith("//")) return; e.preventDefault(); navigate(href); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/resources/static/js/app.js` around lines 72 - 81, The click handler currently captures any <a> with href ending in ".html" including cross-origin links; update the handler in the event listener that uses link, href, and navigate to first resolve the href into a URL (e.g. new URL(href, window.location.href)) and bail out if url.origin !== window.location.origin (thus only handling same-origin or relative links); keep the existing check for href ending with ".html", and only call e.preventDefault() and navigate(href) after the same-origin check so external absolute URLs are not intercepted.src/test/java/org/juv25d/logging/ServerLoggingTest.java-123-141 (1)
123-141:⚠️ Potential issue | 🟡 MinorTest is fragile when
LOG_LEVELenvironment variable is set.
ServerLogging.configure()checksSystem.getenv("LOG_LEVEL")before falling back toConfigLoader. This test asserts the level equalsConfigLoader's value, but ifLOG_LEVELis set in the environment (e.g., CI runner, Docker), the actual level will differ from the expected value and the test will fail.Since environment variables can't be cleared portably in Java without third-party libraries, consider documenting this assumption or using a dedicated test logger that bypasses the env lookup.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/java/org/juv25d/logging/ServerLoggingTest.java` around lines 123 - 141, The test fails if LOG_LEVEL is set in the environment because ServerLogging.configure(Logger) reads System.getenv("LOG_LEVEL"); add a test-friendly overload ServerLogging.configure(Logger, boolean ignoreEnv) (or a configureUsingConfigLoader(Logger) helper) that skips the getenv lookup and forces use of ConfigLoader.getInstance().getLogLevel(), then update the test method logger_shouldUseLogLevelFromApplicationProperties to call the new overload with ignoreEnv=true so the assertion against ConfigLoader.getInstance().getLogLevel() is deterministic.src/main/java/org/juv25d/logging/ServerLogging.java-21-25 (1)
21-25:⚠️ Potential issue | 🟡 Minor
ConsoleHandlerlevel is not set — sub-INFO levels will be silently dropped.
ConsoleHandlerdefaults toLevel.INFO. If the logger level is set toFINE,FINER, orFINEST, the handler will still filter those messages out. Set the handler level to match (or useLevel.ALL) so the logger level is the sole gate.Proposed fix
if (logger.getHandlers().length == 0) { ConsoleHandler handler = new ConsoleHandler(); handler.setFormatter(new SimpleFormatter()); + handler.setLevel(Level.ALL); logger.addHandler(handler); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/juv25d/logging/ServerLogging.java` around lines 21 - 25, The ConsoleHandler created in the ServerLogging initialization is left at its default Level.INFO which will drop any log records below INFO even if the Logger (logger) is set to a finer level; update the handler's level (e.g., call handler.setLevel(logger.getLevel()) or handler.setLevel(Level.ALL)) after creating the ConsoleHandler so the handler does not filter out sub-INFO messages and the logger level becomes the effective gate for log records..github/workflows/docker-release.yml-37-37 (1)
37-37:⚠️ Potential issue | 🟡 Minor
org.opencontainers.image.sourcemust be a URL, not a repository path.
${{ github.repository }}expands toowner/repo, which is not a valid URL. The OCI image spec definesorg.opencontainers.image.sourceas a URL pointing to the source repository.🔧 Proposed fix
- labels: org.opencontainers.image.source=${{ github.repository }} + labels: org.opencontainers.image.source=https://github.com/${{ github.repository }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/docker-release.yml at line 37, The label org.opencontainers.image.source currently uses ${{ github.repository }} which yields "owner/repo" (not a URL); update that label to provide a valid repository URL instead (for example use ${{ github.repository_url }} or construct one via ${{ github.server_url }}/${{ github.repository }}) so the OCI metadata contains a proper URL.src/main/java/org/juv25d/plugin/NotFoundPlugin.java-10-14 (1)
10-14:⚠️ Potential issue | 🟡 MinorSpecify charset in
getBytes()and set aContent-Typeheader.Two issues:
getBytes()uses the platform-default charset. On non-UTF-8 JVM environments the response body will be mis-encoded. UseStandardCharsets.UTF_8explicitly.- No
Content-Typeheader is set, so HTTP clients cannot reliably interpret the response body.🛡️ Proposed fix
+import java.nio.charset.StandardCharsets; public class NotFoundPlugin implements Plugin { `@Override` public void handle(HttpRequest req, HttpResponse res) throws IOException { res.setStatusCode(404); res.setStatusText("Not Found"); - res.setBody("404 - Resource Not Found".getBytes()); + res.setHeader("Content-Type", "text/plain; charset=utf-8"); + res.setBody("404 - Resource Not Found".getBytes(StandardCharsets.UTF_8)); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/juv25d/plugin/NotFoundPlugin.java` around lines 10 - 14, In NotFoundPlugin.handle, avoid platform-default encoding and missing content type: change the body bytes call to use StandardCharsets.UTF_8 (e.g., "404 - Resource Not Found".getBytes(StandardCharsets.UTF_8)) and add a Content-Type header on the HttpResponse (e.g., res.setHeader("Content-Type", "text/plain; charset=UTF-8")) so clients correctly decode the response; update the res.setBody and add res.setHeader calls accordingly.src/main/java/org/juv25d/handler/StaticFileHandler.java-44-46 (1)
44-46:⚠️ Potential issue | 🟡 Minor405 response is missing the required
AllowheaderPer RFC 7231 §6.5.5, a
405 Method Not Allowedresponse MUST include anAllowheader field listing the supported methods.♻️ Proposed fix
if (!request.method().equalsIgnoreCase("GET")) { - return createErrorResponse(405, "Method Not Allowed"); + HttpResponse resp = createErrorResponse(405, "Method Not Allowed"); + resp.setHeader("Allow", "GET"); + return resp; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/juv25d/handler/StaticFileHandler.java` around lines 44 - 46, The 405 branch in StaticFileHandler returns a response without the required Allow header; update the code that handles non-GET requests to include an Allow: GET header in the response (either by calling a variant of createErrorResponse that accepts headers or by adding the header to the Response object before returning). Specifically modify the if (!request.method().equalsIgnoreCase("GET")) branch in StaticFileHandler to build the 405 response and set the "Allow" header to "GET" (referencing the createErrorResponse helper or the Response builder you use) so the returned 405 includes the required Allow field.src/main/java/org/juv25d/filter/IpFilter.java-44-53 (1)
44-53:⚠️ Potential issue | 🟡 MinorAvoid reflecting the client IP in the response body.
Including the IP in the 403 body (
"403 Forbidden: IP not allowed (" + ip + ")") can leak internal network details or proxy-forwarded addresses to the client. A generic "403 Forbidden" message is safer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/org/juv25d/filter/IpFilter.java` around lines 44 - 53, The forbidden(HttpResponse res, String ip) method in IpFilter currently reflects the client IP in the response body; change the body to a generic message (e.g., "403 Forbidden") and recompute the byte[] and Content-Length accordingly so no client-controlled IP is echoed. Keep the same status code/text and headers but remove the ip variable from the response string and any logging that writes it to the response body.docs/adr/ADR-001-static-file-serving-architecture.md-33-33 (1)
33-33:⚠️ Potential issue | 🟡 MinorStale class name references:
SocketServer→Server.The ADR references
SocketServer(lines 33, 198, 203), but the actual class in the codebase isServer(src/main/java/org/juv25d/Server.java). These references should be updated to match the implementation.Also applies to: 198-203
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/adr/ADR-001-static-file-serving-architecture.md` at line 33, Update ADR references that mention the nonexistent class SocketServer to use the actual implementation class name Server; search for occurrences of "SocketServer" in the ADR (notably around the noted lines) and replace them with "Server" so the document matches the codebase's class name (Server) and avoids stale references.docs/adr/ADR-001-static-file-serving-architecture.md-238-238 (1)
238-238:⚠️ Potential issue | 🟡 MinorBroken placeholder link.
The issue link uses a placeholder URL (
https://github.com/your-repo/issues/18) that should be replaced with the actual repository URL.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/adr/ADR-001-static-file-serving-architecture.md` at line 238, Replace the broken placeholder URL in the markdown link for "[Issue `#18`: GET handling for static files]" with the real repository issue URL (e.g. https://github.com/<org-or-user>/<repo>/issues/18) so the link resolves correctly; locate the link text "[Issue `#18`: GET handling for static files]" in ADR-001-static-file-serving-architecture.md and update only the href portion to the actual repo/issues path.
| FROM eclipse-temurin:25-jre-alpine | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # might need to update this later when we have our explicit class names | ||
| COPY --from=build /app/target/*.jar app.jar | ||
| ENTRYPOINT ["java", "-jar", "app.jar"] |
There was a problem hiding this comment.
Container runs as root — add a non-root USER.
Java 25 is the latest LTS release, so the base image choice is solid. However, neither the build nor the runtime stage declares a USER, so all processes (including the JVM serving requests) run as root inside the container. This violates the principle of least privilege and is flagged by Trivy (DS-0002).
🔒 Proposed fix
FROM eclipse-temurin:25-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
+
+RUN addgroup -S appgroup && adduser -S appuser -G appgroup
+USER appuser
+
ENTRYPOINT ["java", "-jar", "app.jar"]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 11 - 17, The container runs as root; create and
switch to a non-root user in the Dockerfile runtime stage: add commands in the
final stage (after WORKDIR and COPY --from=build ...) to create a dedicated
user/group (e.g., appuser), chown the application files (app.jar and /app) to
that user, and use USER appuser before the ENTRYPOINT so the JVM does not run as
root; ensure any JVM-required permissions are granted to that user and keep
ENTRYPOINT ["java","-jar","app.jar"] unchanged.
pom.xml
Outdated
| <maven.compiler.release>25</maven.compiler.release> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| <junit.jupiter.version>6.0.2</junit.jupiter.version> |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
JUnit Jupiter 6.0.2 release Maven Central
💡 Result:
JUnit Jupiter 6.0.2 is published on Maven Central (released January 6, 2026). (docs.junit.org)
Maven coordinates (aggregator):
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>6.0.2</version>
<scope>test</scope>
</dependency>The 6.0.2 version is also available for core modules like junit-jupiter-api and junit-jupiter-engine on Maven Central. (repo.maven.apache.org)
Citations:
- 1: https://docs.junit.org/snapshot/release-notes/?utm_source=openai
- 2: https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/?utm_source=openai
🏁 Script executed:
# Find and read the full pom.xml to understand the project context
find . -name "pom.xml" -type f | head -5Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template
Length of output: 117
🏁 Script executed:
cat -n ./pom.xmlRepository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template
Length of output: 8751
🌐 Web query:
pitest-junit5-plugin 1.2.2 JUnit 6 compatibility
💡 Result:
org.pitest:pitest-junit5-plugin:1.2.2 is not JUnit 6–compatible in any officially supported/guaranteed sense.
- The plugin docs state that 1.2.2 is built against JUnit Platform 1.x and “you may encounter issues if you use it with a different version”, and that it “automatically work[s] with JUnit platform 1.5.0 to 1.10.0-M1 (and probably above)” when used via the PIT Maven/Gradle integrations. That range is JUnit 5-era Platform, not JUnit 6. [1]
- JUnit 6.0.0 (released Sep 30, 2025) moves to a single unified 6.x version line (Platform/Jupiter/Vintage all 6.0.0) and includes breaking changes/new launcher APIs, i.e., it’s a major-line shift from Platform 1.x to Platform 6.x. [2][3]
What to do: if you’re upgrading to JUnit 6, you’ll need a pitest-junit5-plugin version that explicitly states support for JUnit Platform 6.x (the plugin README currently does not for 1.2.2, and even its “automatic” range is still expressed in Platform 1.x terms). [1]
Sources
[1] pitest/pitest-junit5-plugin README (versions + supported JUnit Platform range) (github.com)
[2] JUnit 6.0.0 release notes (release date + unified versioning + platform changes) (docs.junit.org)
[3] JUnit 6.0.0 ships Sep 30, 2025 (background/summary) (infoq.com)
Citations:
- 1: https://github.com/pitest/pitest-junit5-plugin?utm_source=openai
- 2: https://docs.junit.org/6.0.1/release-notes/index.html?utm_source=openai
- 3: https://www.infoq.com/news/2025/10/junit6-java17-kotlin/?utm_source=openai
🌐 Web query:
pitest 1.22.0 JUnit 6 support
💡 Result:
PIT 1.22.0 does not have “JUnit 6” support.
- PIT core supports JUnit 4 out of the box; JUnit 5 requires the separate pitest-junit5-plugin. [1]
- The pitest-junit5-plugin is built around JUnit Platform 1.x (it notes compatibility with roughly 1.5.0 → 1.10.0-M1). That does not cover the JUnit 6 / Platform 6.x line. [2]
- JUnit 6.0.0 was released on September 30, 2025. [3]
- PIT 1.22.0 (released Nov 18, 2025) only mentions a test-filter extension point—nothing about JUnit 6/Platform 6. [4][5]
What to do: if you need PIT today, keep tests on JUnit 5 (Platform 1.x) (e.g., JUnit 5.14.x) and use pitest-junit5-plugin; otherwise you’ll need to wait for (or contribute) an updated PIT JUnit plugin that explicitly supports JUnit Platform 6.x. [2][3]
Sources:
[1] PIT FAQ (JUnit support)
[2] pitest-junit5-plugin README (supported JUnit Platform range)
[3] JUnit 6.0.0 release notes (release date / version line)
[4] PIT GitHub releases (1.22.0 notes)
[5] Maven Central listing showing PIT 1.22.0 publish date
Downgrade JUnit Jupiter to 5.x or remove pitest dependency — pitest-junit5-plugin 1.2.2 does not support JUnit 6.
JUnit Jupiter 6.0.2 (released January 6, 2026) and Java 25 (September 2025) are both valid and available. However, pitest-junit5-plugin 1.2.2 is built against JUnit Platform 1.x (JUnit 5 era) and does not support JUnit Platform 6.x introduced in JUnit 6.0.0. This incompatibility will prevent pitest mutations from running. Either downgrade to JUnit 5.14.x or wait for an updated pitest plugin with JUnit 6 support.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pom.xml` around lines 12 - 14, The project currently sets
junit.jupiter.version to 6.0.2 which is incompatible with pitest-junit5-plugin
1.2.2; update the pom so the junit.jupiter.version Maven property is downgraded
to a JUnit 5 release (e.g., 5.14.x) or remove/replace the pitest-junit5-plugin
dependency so mutation testing is not forced to run against JUnit 6—look for the
junit.jupiter.version property and any pitest-junit5-plugin configuration in the
POM and change the property to a 5.x value (or remove/upgrade the plugin) to
restore compatibility with pitest.
| try (socket) { | ||
| var in = socket.getInputStream(); | ||
| var out = socket.getOutputStream(); | ||
|
|
||
| HttpRequest parsed = httpParser.parse(in); |
There was a problem hiding this comment.
No socket read timeout — connections can block indefinitely
httpParser.parse(in) reads the socket's InputStream byte-by-byte with no deadline. A client that opens a connection and then stops sending data (slow-loris style, or simply abandons the connection) will hold a virtual thread and a file descriptor open forever.
Add a read timeout via socket.setSoTimeout(millis) before delegating to the parser:
🛡️ Proposed fix
`@Override`
public void run() {
try (socket) {
+ socket.setSoTimeout(30_000); // 30-second read deadline
var in = socket.getInputStream();
var out = socket.getOutputStream();The timeout value should ideally be read from ConfigLoader (e.g., server.read-timeout-ms) to keep it configurable.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/juv25d/ConnectionHandler.java` around lines 28 - 32, The
ConnectionHandler currently calls httpParser.parse(in) without configuring a
socket read timeout, allowing connections to hang indefinitely; fix this by
retrieving a configured timeout (e.g.,
ConfigLoader.getInt("server.read-timeout-ms") or equivalent) and calling
socket.setSoTimeout(timeoutMillis) before invoking httpParser.parse(in) so the
socket's read operations will time out, then handle SocketTimeoutException
appropriately (close/return a 408 or log and close) around the call to
httpParser.parse; reference the ConnectionHandler class, the socket variable,
and httpParser.parse to locate where to add the setSoTimeout and the timeout
retrieval.
| public class DefaultConnectionHandlerFactory implements ConnectionHandlerFactory{ | ||
| private final HttpParser httpParser; | ||
| private final Logger logger; | ||
| private final Pipeline pipeline; | ||
|
|
||
| public DefaultConnectionHandlerFactory(HttpParser httpParser, Logger logger, Pipeline pipeline) { | ||
| this.httpParser = httpParser; | ||
| this.logger = logger; | ||
| this.pipeline = pipeline; | ||
| } | ||
|
|
||
| @Override | ||
| public Runnable create(Socket socket, Pipeline pipeline) { | ||
| return new ConnectionHandler(socket, httpParser, logger, pipeline); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check all usages of DefaultConnectionHandlerFactory
echo "=== All references to DefaultConnectionHandlerFactory ==="
rg -n "DefaultConnectionHandlerFactory" --type=java -C3
echo -e "\n=== Check if this.pipeline is referenced in DefaultConnectionHandlerFactory ==="
rg -n "this\.pipeline" --type=java -C2Repository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template
Length of output: 3172
🏁 Script executed:
#!/bin/bash
# Read the complete DefaultConnectionHandlerFactory.java file
echo "=== Complete DefaultConnectionHandlerFactory.java ==="
cat -n src/main/java/org/juv25d/DefaultConnectionHandlerFactory.java
echo -e "\n=== Check ConnectionHandlerFactory interface ==="
rg -A10 "interface ConnectionHandlerFactory" --type=javaRepository: ithsjava25/ithsjava25-java-project-webserver-maven-java-template
Length of output: 1319
pipeline field is stored but never used — the create() method shadows it with its parameter.
The constructor accepts and stores a Pipeline (line 16), but create(Socket, Pipeline) receives its own pipeline parameter (line 20) and passes that to ConnectionHandler (line 21). The field this.pipeline is dead code. The ConnectionHandlerFactory interface requires the create() signature to include the Pipeline parameter, and App.java (line 42) passes a pipeline to the constructor while Server.java passes its own pipeline when invoking create(), making the factory-stored one unnecessary.
Remove pipeline from the constructor and constructor parameter to eliminate the dead code:
Suggested fix
-public class DefaultConnectionHandlerFactory implements ConnectionHandlerFactory{
- private final HttpParser httpParser;
- private final Logger logger;
- private final Pipeline pipeline;
-
- public DefaultConnectionHandlerFactory(HttpParser httpParser, Logger logger, Pipeline pipeline) {
+public class DefaultConnectionHandlerFactory implements ConnectionHandlerFactory {
+ private final HttpParser httpParser;
+ private final Logger logger;
+
+ public DefaultConnectionHandlerFactory(HttpParser httpParser, Logger logger) {
this.httpParser = httpParser;
this.logger = logger;
- this.pipeline = pipeline;
}
`@Override`
public Runnable create(Socket socket, Pipeline pipeline) {
return new ConnectionHandler(socket, httpParser, logger, pipeline);
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/juv25d/DefaultConnectionHandlerFactory.java` around lines 8
- 22, DefaultConnectionHandlerFactory stores a dead pipeline field and shadows
it in create(Socket, Pipeline); remove the unused Pipeline field and its
constructor parameter from DefaultConnectionHandlerFactory, update the
constructor signature to only accept (HttpParser httpParser, Logger logger),
delete the private final Pipeline pipeline field and any assignments to it, and
ensure create(Socket socket, Pipeline pipeline) continues to accept the pipeline
parameter and passes that pipeline to the ConnectionHandler constructor (new
ConnectionHandler(socket, httpParser, logger, pipeline)).
| public Map<String, String> headers() { | ||
| return headers; | ||
| } |
There was a problem hiding this comment.
headers() returns the internal mutable map — breaks encapsulation and risks ConcurrentModificationException.
body() (line 51-53) defensively clones, but headers() returns the raw internal map. Any caller (including HttpResponseWriter.writeHeaders which iterates it) can see or cause concurrent modifications. This inconsistency is noted in the class Javadoc's intent to allow mutation by filters, but it means two filters or a filter and the writer can interfere with each other.
If mutation by callers is intentional (filters call response.setHeader()), then the direct return is fine — but document it and don't claim encapsulation. If encapsulation is intended, return an unmodifiable view or a copy.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/juv25d/http/HttpResponse.java` around lines 43 - 45,
headers() currently exposes the internal mutable Map which breaks encapsulation
and risks concurrent modification; change headers() to return an unmodifiable
view or defensive copy (e.g., Collections.unmodifiableMap(new
HashMap<>(headers)) or Collections.unmodifiableMap(headers)) to prevent callers
from mutating it directly, and update callers like
HttpResponseWriter.writeHeaders to use response.setHeader()/addHeader() (or
operate on a defensive copy) if mutation is required; if you intend to allow
external mutation, instead document headers() clearly in the class Javadoc and
provide controlled mutation APIs such as
setHeader(String,String)/removeHeader(String) to centralize mutation.
| public void handle(HttpRequest req, HttpResponse res) throws IOException { | ||
| // placeholder response logic | ||
| } |
There was a problem hiding this comment.
Empty handle() will cause a NullPointerException in HttpResponseWriter.
HttpResponse's no-arg constructor (used throughout the pipeline) leaves headers as null. When HttpResponseWriter.writeHeaders() is called after HelloPlugin.handle() it iterates headers.entrySet() → NPE. The status line will also render as "HTTP/1.1 0 null" since statusText is also unset.
Two changes are needed:
HelloPlugin.handle()must set at least a valid status code, status text, and empty headers.HttpResponse()no-arg constructor should defensively initializeheadersto avoid this class of bug across all plugins:
🐛 Proposed fixes
HelloPlugin.java — set a minimal valid response:
`@Override`
public void handle(HttpRequest req, HttpResponse res) throws IOException {
- // placeholder response logic
+ res.setStatusCode(200);
+ res.setStatusText("OK");
+ res.setBody("Hello, World!".getBytes(StandardCharsets.UTF_8));
}HttpResponse.java — initialize headers in the no-arg constructor:
-public HttpResponse(){}
+public HttpResponse(){
+ this.headers = new LinkedHashMap<>();
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/juv25d/plugin/HelloPlugin.java` around lines 11 - 13,
HelloPlugin.handle() is currently empty which leads to NullPointerException in
HttpResponseWriter.writeHeaders() because HttpResponse.headers is null and
statusText/statusCode are unset; update HelloPlugin.handle(HttpRequest req,
HttpResponse res) to set a minimal valid response (e.g., res.setStatusCode(200),
res.setStatusText("OK"), and initialize headers to an empty map via
res.setHeaders(...)) and also modify HttpResponse's no-arg constructor to
defensively initialize the headers field (e.g., new HashMap<>()), ensuring
HttpResponseWriter.writeHeaders() can safely iterate headers and the status line
renders correctly.
| private void loadConfiguration() { | ||
| Yaml yaml = new Yaml(); | ||
|
|
||
| try (InputStream input = getClass().getClassLoader().getResourceAsStream("application-properties.yml")) { | ||
| if (input == null) { | ||
| throw new IllegalArgumentException("Did not find application-properties.yml"); | ||
| } | ||
|
|
||
| Map<String, Object> config = yaml.load(input); | ||
|
|
||
| // server | ||
| Map<String, Object> serverConfig = (Map<String, Object>) config.get("server"); | ||
| if (serverConfig != null) { | ||
| this.port = (Integer) serverConfig.getOrDefault("port", 8080); | ||
| this.rootDirectory = (String) serverConfig.getOrDefault("root-dir", "static"); | ||
| } | ||
|
|
||
| // logging | ||
| Map<String, Object> loggingConfig = (Map<String, Object>) config.get("logging"); | ||
| if (loggingConfig != null) { | ||
| this.logLevel = (String) loggingConfig.get("level"); | ||
| } | ||
|
|
||
| } catch (Exception e) { | ||
| throw new RuntimeException("Failed to load application config"); | ||
| } |
There was a problem hiding this comment.
getLogLevel() can return null, causing NPE in ServerLogging.
If the logging section is absent from the YAML (or the file is empty), logLevel is never assigned and remains null. When ServerLogging.configure() calls ConfigLoader.getInstance().getLogLevel() as the fallback default, levelName.toUpperCase() (line 33 of ServerLogging.java) will throw a NullPointerException.
Apply defaults for all fields so that missing YAML sections degrade gracefully, and chain the original exception for debuggability.
Proposed fix
private void loadConfiguration() {
Yaml yaml = new Yaml();
try (InputStream input = getClass().getClassLoader().getResourceAsStream("application-properties.yml")) {
if (input == null) {
throw new IllegalArgumentException("Did not find application-properties.yml");
}
Map<String, Object> config = yaml.load(input);
+ if (config == null) {
+ return; // empty YAML file – keep defaults
+ }
// server
Map<String, Object> serverConfig = (Map<String, Object>) config.get("server");
if (serverConfig != null) {
this.port = (Integer) serverConfig.getOrDefault("port", 8080);
this.rootDirectory = (String) serverConfig.getOrDefault("root-dir", "static");
}
// logging
Map<String, Object> loggingConfig = (Map<String, Object>) config.get("logging");
if (loggingConfig != null) {
this.logLevel = (String) loggingConfig.get("level");
}
} catch (Exception e) {
- throw new RuntimeException("Failed to load application config");
+ throw new RuntimeException("Failed to load application config", e);
}
}And initialise defaults on the fields:
private static ConfigLoader instance;
-private int port;
-private String logLevel;
-private String rootDirectory;
+private int port = 8080;
+private String logLevel = "INFO";
+private String rootDirectory = "static";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/org/juv25d/util/ConfigLoader.java` around lines 25 - 50,
loadConfiguration currently leaves fields like logLevel unset when YAML sections
are missing, causing getLogLevel() to return null and NPE in
ServerLogging.configure; update ConfigLoader to initialize sensible defaults for
all fields (e.g., port=8080, rootDirectory="static", logLevel="INFO") as class
field initializers or before parsing, and in loadConfiguration only override
them when config entries exist (use getOrDefault or null-checks); also when
catching Exception in loadConfiguration, rethrow a RuntimeException that chains
the original exception (new RuntimeException("Failed to load application
config", e)) so callers can debug root causes; verify references:
loadConfiguration(), getLogLevel(), and
ServerLogging.configure()/ConfigLoader.getInstance().getLogLevel().
| window.addEventListener("popstate", () => { | ||
| navigate(window.location.pathname); | ||
| }); |
There was a problem hiding this comment.
popstate handler creates duplicate history entries.
navigate() calls history.pushState on line 54. When the browser fires popstate (back/forward), calling navigate() pushes a new entry on top, breaking back/forward navigation entirely.
Use a separate content-update function that skips pushState, or pass a flag:
Proposed fix
window.addEventListener("popstate", () => {
- navigate(window.location.pathname);
+ updateContent(window.location.pathname);
});Then extract the fetch-and-replace logic from navigate into a shared updateContent(href) function, and have navigate call updateContent followed by history.pushState.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/resources/static/js/app.js` around lines 16 - 18, The popstate
handler calls navigate(), which itself calls history.pushState and thus creates
duplicate history entries; refactor the fetch-and-replace logic out of navigate
into a new updateContent(href) function (or add a pushState boolean parameter to
navigate) so that navigate(href) calls updateContent(href) then
history.pushState(...), while the popstate handler calls
updateContent(window.location.pathname) (or navigate(window.location.pathname,
{push: false})) to update the page without pushing a new history entry.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
pom.xml (1)
32-37:⚠️ Potential issue | 🟡 MinorExclude transitive JUnit 5 dependency from
mockito-junit-jupiterto prevent classpath conflicts with JUnit 6.
mockito-junit-jupiter:5.21.0declares a runtime dependency onjunit-jupiter-api:5.13.4(JUnit 5). While Mockito's JUnit 6 support is not yet officially declared (GitHub issue#3779, opened Jan 2026, remains open),mockito-junit-jupiteris likely compatible with JUnit 6 sinceMockitoExtensionrelies on core callback interfaces that were preserved in JUnit 6.However, mixing both JUnit 5 and JUnit 6 artifacts on the classpath risks resolution conflicts. To safely use
mockito-junit-jupiterwith JUnit 6, exclude the transitive JUnit 5 dependency:Suggested fix
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-junit-jupiter</artifactId> <version>${mockito.version}</version> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> </exclusion> </exclusions> </dependency>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pom.xml` around lines 32 - 37, The mockito-junit-jupiter dependency pulls in junit-jupiter-api (JUnit 5) transitively which can conflict with JUnit 6; update the mockito-junit-jupiter dependency declaration (artifactId: mockito-junit-jupiter, version: ${mockito.version}) to add an <exclusions> entry that excludes groupId org.junit.jupiter and artifactId junit-jupiter-api so the JUnit 5 API is not on the test classpath.
🧹 Nitpick comments (1)
pom.xml (1)
122-138:spotless-maven-pluginhas no<executions>— it will never run during the standard build lifecycle.Without binding to a phase (e.g.,
validateforspotless:check), spotless only runs when invoked explicitly (mvn spotless:check/mvn spotless:apply). If the intent is to enforce formatting in CI, add an execution:♻️ Proposed fix to bind spotless:check to validate phase
<plugin> <groupId>com.diffplug.spotless</groupId> <artifactId>spotless-maven-plugin</artifactId> <version>3.2.1</version> <configuration> <java> <includes> <include>src/main/java/**/*.java</include> <include>src/test/java/**/*.java</include> </includes> <removeUnusedImports /> <trimTrailingWhitespace /> <formatAnnotations /> </java> </configuration> + <executions> + <execution> + <goals> + <goal>check</goal> + </goals> + </execution> + </executions> </plugin>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pom.xml` around lines 122 - 138, The Spotless plugin configuration for com.diffplug.spotless:spotless-maven-plugin lacks an <executions> section so it won't run during the standard Maven lifecycle; update the plugin block (spotless-maven-plugin) to add an <executions> element that binds the desired goals (e.g., spotless:check and/or spotless:apply) to an appropriate phase such as validate (or verify) so formatting is enforced in CI, ensuring the plugin will execute automatically during builds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@pom.xml`:
- Around line 32-37: The mockito-junit-jupiter dependency pulls in
junit-jupiter-api (JUnit 5) transitively which can conflict with JUnit 6; update
the mockito-junit-jupiter dependency declaration (artifactId:
mockito-junit-jupiter, version: ${mockito.version}) to add an <exclusions> entry
that excludes groupId org.junit.jupiter and artifactId junit-jupiter-api so the
JUnit 5 API is not on the test classpath.
---
Duplicate comments:
In `@pom.xml`:
- Around line 139-160: The pitest-maven plugin configuration currently depends
on org.pitest:pitest-junit5-plugin version 1.2.2 which targets JUnit Platform
1.x and is incompatible with JUnit Jupiter 6.x; update the pitest-junit5-plugin
dependency under the pitest-maven <plugin> (artifactId pitest-junit5-plugin) to
a version that supports JUnit Platform 6.x (bump the <version> to a compatible
release, e.g., >=1.3.0 or the latest plugin that documents JUnit 6 support) and
run your mutation tests to confirm they discover and execute JUnit 6 tests.
---
Nitpick comments:
In `@pom.xml`:
- Around line 122-138: The Spotless plugin configuration for
com.diffplug.spotless:spotless-maven-plugin lacks an <executions> section so it
won't run during the standard Maven lifecycle; update the plugin block
(spotless-maven-plugin) to add an <executions> element that binds the desired
goals (e.g., spotless:check and/or spotless:apply) to an appropriate phase such
as validate (or verify) so formatting is enforced in CI, ensuring the plugin
will execute automatically during builds.
Implemented a filter that measures how long each incoming HTTP request takes to process. The filter should record the start time before delegating to the next filter in the chain, then calculate the elapsed time once the response is ready. It should log the total duration in milliseconds together with the request method and path.
Summary by CodeRabbit
New Features
CI / Chores
Documentation
Tests