Skip to content

Feature/healthcheck - #122#30

Closed
EdvinSandgren wants to merge 42 commits intomainfrom
feature/healthcheck
Closed

Feature/healthcheck - #122#30
EdvinSandgren wants to merge 42 commits intomainfrom
feature/healthcheck

Conversation

@EdvinSandgren
Copy link

@EdvinSandgren EdvinSandgren commented Mar 15, 2026

Adds a healthcheck to the Dockerfile to check the status of the server.

Summary by CodeRabbit

  • New Features

    • Server configuration via YAML and JSON files
    • Response compression with Brotli and gzip support
    • IP-based request filtering with allowlist/blocklist modes
    • Request timeout protection
    • Automatic request logging with processing times
    • Custom error pages
    • Docker container support with multi-architecture builds
    • Route-based request filtering pipeline
  • Documentation

    • Expanded README with deployment and configuration guides
    • Port configuration reference guide
  • Chores

    • GitHub Actions CI/CD workflows for automated testing and Docker image publication

Kathify and others added 30 commits February 10, 2026 14:44
Add ci workflow for github actions.
* build: configure pom.xml with needed plugin/tools.
Setup Java 25 environment with JUnit 5, Mockito, JaCoCo, Pitest, and Spotless

* fix: add missing test scope for awaitility
* release.yml that builds and publishes Docker image to GitHub packages on release.

* Fixed unverified commit stopping pull request from being merged
* Dockerfile that builds an image in a docker container then runs in another docker container

Current implementation uses Temporary App.class reference before relevant file is created to start server.

* Fixed unverified commit

* Set up non-root user and updated Dockerfile to use user. Fixed file path to use /app/ instead of /app/org/example to prevent unnessary nesting of packages.
* added parser class for headers

* refactoring

* refactoring

* refactoring, added getHeadersMap

* refactoring

* refactoring code, adding debug boolean

* added unit tests for HttpParser.java class

* refactoring and all green tests for class

* refactoring the code

* refactoring the bufferedReader

* refactoring with coderabbit feedback for header put to merge

* refactoring with coderabbit feedback for header put to merge, changed headersmap

* refactoring from reviewer input

* refactoring for code rabbit comments
* Add HttpResponseBuilder and its tests

Signed-off-by: JohanHiths <johan.hietala@iths.se>

* Implement minimal HTTP response builder

Signed-off-by: JohanHiths <johan.hietala@iths.se>

* update coderabbit

* more coderabbit fixes

Signed-off-by: JohanHiths <johan.hietala@iths.se>

* Bytte från httptest till http efter rekommendationer från Xeutos

* code rabbit fix

* fixed more code rabbit problems

---------

Signed-off-by: JohanHiths <johan.hietala@iths.se>
* added parser class for headers

* refactoring

* refactoring

* refactoring, added getHeadersMap

* refactoring

* adding class for parsing requestLine (method, path, version). Has getters/setters for the three.

* refactoring code, private setters

* added tests for throwing error

* refactoring the bufferedReader

* refactoring, adding check for requestLineArray.length() is less or equals to two with added unit test

* refactoring, adding check for requestLineArray.length() is less or equals to two with added unit test

* refactoring, adding test for null value of input stream

* refactoring from coderabbit input

* refactoring test class.

* refactoring, adding debug boolean flag for logger lines

* refactoring and testing buffered reader stream

* refactoring, changed class HttpParser.java to extend HttpParseRequestLine.java and making them share the same buffered reader. Added method to set the reader and then methods to call the HttpParser

* refactoring, changed class HttpParser.java to extend HttpParseRequestLine.java and making them share the same buffered reader. Added method to set the reader and then methods to call the HttpParser
* Add Bucket4j dependency in pom file

* Extract Bucket4j version into a property and update version in dependency for consistency
* Add support for serving static files

- Introduced `StaticFileHandler` to serve static files from the `www` directory.
- Enhanced `HttpResponseBuilder` to support byte array bodies.
- Made `setReader` method public in `HttpParser` to improve flexibility.
- Code tested via Insomnia by coding a temporary method for multithreads in TcpServer.java, this is now removed before future implementations of ConnectionHandler

Co-authored-by: Daniel Fahlén xsz300@gmail.com

* Fix typo in `HttpResponseBuilder` (contentLength) and refine body handling logic
Co-authored-by: Daniel Fahlén xsz300@gmail.com

* Add basic HTML structure to `index.html` in `www` directory

* Make `handleGetRequest` method private in `StaticFileHandler` to encapsulate functionality
* Updates pom.xml, with jackson-dependencies, for config file

* Updates pom.xml, removes jackson-annotations:2.20, because it was apparently unnecessary.
* * Move HTTP handling logic from TcpServer to a dedicated ConnectionTask class
* Implement virtual thread execution for better scalability
* Add regex-based URI routing to support clean URLs and default to index.html
* Ensure sockets are properly closed

Co-authored-by: Caroline Nordbrandt <caroline_nordbradt@hotmail.com>

* change thrown exception to runtime instead with appropiet message in tcpServer.

---------

Co-authored-by: Caroline Nordbrandt <caroline_nordbradt@hotmail.com>
* Added basic YAML config-file.

* Added class ConfigLoader with static classes for encapsulation

* Added static metod loadOnce and step one of static method load

* Added static method createMapperFor that checks for YAML or JSON-files before creating an ObjectMapper object.

* implement ConfigLoader
refs #13

* Added AppConfig.java record for config after coderabbit feedback

* Updated ConfigLoader to use AppConfig record and jackson 3

* Added tests for ConfigLoader and reset cached method in ConfigLoader to ensure test isolation with static cache

* Removed unused dependency. Minor readability tweaks in AppConfig.

* Added check for illegal port numbers to withDefaultsApplied-method.

* Added test for illegal port numbers.
* Add 404 error handling to `StaticFileHandler` with fallback page

* Add test coverage for `StaticFileHandler` and improve constructor flexibility

- Introduced a new constructor in `StaticFileHandler` to support custom web root paths, improving testability.
- Added `StaticFileHandlerTest` to validate static file serving and error handling logic.

* Add test for 404 handling in `StaticFileHandler`

- Added a test to ensure nonexistent files return a 404 response.
- Utilized a temporary directory and fallback file for improved test isolation.

* Verify `Content-Type` header in `StaticFileHandlerTest` after running Pittest, mutations survived where setHeaders could be removed without test failure.

* Remove unused `.btn` styles from `pageNotFound.html`

* Improve 404 handling in `StaticFileHandler`: add fallback to plain text response if `pageNotFound.html` is missing, and fix typo in `pageNotFound.html`, after comments from CodeRabbit.
* Implements configuration loading and ensures that ConfigLoader is invoked during application startup (App.main).

* Minor formating.
* initial commit, added interfaces Filter and FilterChain

* Added HttpRequest class, groups together all information about a request that the server needs and easier to handle by future filters

* added interfaces Filter and FilterChain with Java Servlet Filter architecture.

* added FilterChainImpl

* Corrected imports from JDKS HttpRequest, to projects HttpRequest class

* Changed, params for FilterChain

* Updated HttpRequest with attributes,

* Revert "Updated HttpRequest with attributes,"

This reverts commit 0fd490e.
* Added MIME detector class and test class

* Added logic for Mime detector class

* Added Unit tests

* Added logic in HttpResponseBuilder and tests to try it out

* Solves duplicate header issue

* Removed a file for another issue

* Changed hashmap to Treemap per code rabbits suggestion

* Corrected logic error that was failing tests as per P2P review

* Added more reason phrases and unit testing, also applied code rabbits suggestions!

* Added changes to Responsebuilder to make merging easier

* Changed back to earlier commit to hande byte corruption new PR

* Added StaticFileHandler from main

* Added staticFileHandler with binary-safe writing

* Fix: Normalize Content-Type charset to uppercase UTF-8

Changed 'charset=utf-8' to 'charset=UTF-8' in StaticFileHandlerTest
to match MimeTypeDetector output and align with HTTP RFC standard.

Uppercase UTF-8 is the correct format per RFC 2616/7231.
* Update Dockerfile

Dockerfiles now copies www folder aswell

* Added building and copying of dependency jar files

* Fix dependency path in Dockerfile and update classpath in ENTRYPOINT configuration.

* Fixed typo in Entrypoint configuration

* Expose port 8080 in Dockerfile and changed appuser to come before ENTRYPOINT configuration.

* Adjust Dockerfile paths for classes and dependencies, update `COPY` targets accordingly.
* Added comprehensive README.MD

* Added formatting recommendations, clearer info
* Prevent path traversal and sanitize URI in StaticFileHandler.

* Add test for path traversal protection and support 403 responses.

* Add tests for URI sanitization and handling of encoded/special URLs.

* Add test for null byte injection prevention in StaticFileHandler

* Improve StaticFileHandler path traversal handling and test coverage

* Improve URI sanitization and add test for 404 response handling in StaticFileHandler
* Resolve port: CLI > config > default

* Wire port resolution to AppConfig/ConfigLoader and update docs/tests

* Update PortConfigurationGuide.md

* Update PortConfigurationGuide.md

* Remove ServerPortResolver; use ConfigLoader for port

* Update PortConfigurationGuide.md

* Update PortConfigurationGuide.md

* may be done
* refactor: add predefined HTTP status code constants to HttpResponseBuilder

* refactor: use status code constants in StaticFileHandler

* test: refactor StaticFileHandlerTest to use status code constants

* test: refactor HttpResponseBuilderTest to use status code constants and explicit assertations
* Fix path in Dockerfile for `www` directory copy operation

* Correct relative path for `www` directory in Dockerfile copy operation
* Add IpFilter and corresponding test skeleton

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Extend IpFilter with blocklist mode and add unit tests

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Enhance IpFilter to return 403 for blocked IPs and add corresponding test case

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Extend IpFilter with allowlist mode and add corresponding unit tests

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Refactor IpFilter to support both allowlist and blocklist modes, update logic, and add unit tests for allowlist mode

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Handle missing client IP in IpFilter, return 400 response, and add corresponding test case

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Refactor tests in `IpFilterTest` to use `assertAll` for improved assertion grouping and readability

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Refactor `IpFilter` to use `HttpResponse` instead of `HttpResponseBuilder` and update tests accordingly

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Add unit tests for edge cases in `IpFilter` allowlist and blocklist modes

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Refactor `IpFilter` and tests to use `HttpResponseBuilder` instead of `HttpResponse`

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Handle empty client IP in `IpFilter`, return 400 response, and add corresponding test case

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Add comments to `IpFilter` empty methods, clarifying no action is required

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Fix typos in comments within `IpFilterTest`

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Add Javadoc comments to `IpFilter` and `IpFilterTest` for improved clarity and documentation

* Refactor `IpFilter` to use thread-safe collections and normalize IP addresses

* Make `mode` field in `IpFilter` volatile to ensure thread safety

* Ensure UTF-8 encoding for response string in `IpFilterTest` and add attribute management to `HttpRequest`

* Ensure UTF-8 encoding for response string in `IpFilterTest` and add attribute management to `HttpRequest`

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Integrate IP filtering into `ConnectionHandler` and update configuration to support filter settings

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Refactor IP filter check in `ConnectionHandler` to use `Boolean.TRUE.equals` for improved null safety

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* Validate null inputs for allowed/blocked IPs in `IpFilter`, enhance test coverage, and fix typographical error in comments

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

* refactor: extract applyFilters() method using FilterChainImpl

Co-authored-by: Andreas Kaiberger <andreas.kaiberger@gmail.com>

* refactor: cache filter list at construction time

Co-authored-by: Andreas Kaiberger <andreas.kaiberger@gmail.com>

* refactor: cache filter list at construction time

Co-authored-by: Andreas Kaiberger <andreas.kaiberger@gmail.com>

* test: verify GPG signing

* Replace hardcoded status codes in `IpFilter` and `ConnectionHandler` with constants from `HttpResponseBuilder` for better maintainability

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>

---------

Co-authored-by: Rickard Ankar <rickard.ankar@iths.se>
* Re-commit LocaleFilter + tests to clean branch for PR

* Update LocaleFilter to handle quality weights and improve javadoc

* Fix: rename test method to reflect actual headers scenario

* Fix: ensure resolveLocale never returns empty string; strip quality weights
* Add LoggFilter and update HttpResponseBuilder with method getStatusCode() in order to use it in logging

* Refactor code

* Change magic number to constans in httpresponsebuilder

* Add LoggFilter and update HttpResponseBuilder with method getStatusCode() in order to use it in logging

* Refactor code

* Added tests -> LoggingFilterTest

* Trigger rebuild

* Git is hallucinating. Trying to fix problem

* Final version (hopefully)
* Add error handling for when client request fail -> return internal server error 500

* Create ConnectionFactory in order to be able to test the TcpServer. Implement factory into TcpServer and App.

* Create test to check whether handleClient throws internal server error when there is an exception

* Modify test

* Update logic -> seperate handleClient and processRequest in order to have open socket when handling internal server error

* Exchange Printwriter to Outputstream in handleInternalServerError

* Add error handling for when client request fail -> return internal server error 500

* Create ConnectionFactory in order to be able to test the TcpServer. Implement factory into TcpServer and App.

* Create test to check whether handleClient throws internal server error when there is an exception

* Modify test

* Update logic -> seperate handleClient and processRequest in order to have open socket when handling internal server error

* Exchange Printwriter to Outputstream in handleInternalServerError

* Rebase main onto bransch. Update App -> add Connectionhandler::new
Also update handleInternalServerError
* Re-commit LocaleFilterWithCookie + tests to clean branch for PR

* Fix: make header lookups case-insensitive to avoid incorrect default locale fallback.
Rickank and others added 12 commits February 26, 2026 09:09
* Remove .html concat to support all files

* Add test for jpg handling in ConnectionHandler (TDD:RED), related to #69

* Make ConnectionHandler testable with webroot parameter

* Fix: Strip leading slash from URI to prevent absolute path (coderabbit)
… (#56)

* ensure global detection for empty route patterns

* larify global vs route-specific registrations

* add support for exact and /prefix/* matching

* add coverage for global, route-specific, ordering and short-circuit

* configurable global and route-specific filters with ordering

* changed package

* removed from branch

* Add missing tests for configurable filter pipeline

* Refactor filter pipeline: sort FilterRegistration directly by order

* Fix RoutePattern wildcard to match base path (/api/* matches /api)

* Restore App entry point and basic tests from main

* Bump org.apache.maven.plugins:maven-dependency-plugin (#6) (#55)

* Bump org.apache.maven.plugins:maven-dependency-plugin (#6)

Bumps the maven-deps group with 1 update: [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin).


Updates `org.apache.maven.plugins:maven-dependency-plugin` from 3.9.0 to 3.10.0
- [Release notes](https://github.com/apache/maven-dependency-plugin/releases)
- [Commits](apache/maven-dependency-plugin@maven-dependency-plugin-3.9.0...maven-dependency-plugin-3.10.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-dependency-plugin
  dependency-version: 3.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: maven-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump org.junit.jupiter:junit-jupiter in the maven-deps group (#13)

Bumps the maven-deps group with 1 update: [org.junit.jupiter:junit-jupiter](https://github.com/junit-team/junit-framework).


Updates `org.junit.jupiter:junit-jupiter` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](junit-team/junit-framework@r6.0.2...r6.0.3)

---
updated-dependencies:
- dependency-name: org.junit.jupiter:junit-jupiter
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: maven-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Add short-circuit tests for configurable filter pipeline

* Add tests for response-phase and global-vs-route ordering

* pom fix - no changes

* Align server filter pipeline with main FilterChainImpl and HttpResponseBuilder

* Align filter pipeline with main filter chain and harden immutability

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Viktor Lindell <v.lindell@hotmail.com>
Co-authored-by: viktorlindell12 <viktor.lindell@iths.se>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add CompressionFilter skeleton with basic structure

* Add Accept-Encoding header detection for gzip

* Add Accept-Encoding header detection for gzip

* Implement gzip compression for response bodies

* Add Content-Encoding

* Added test file

* Add content-type filtering to skip non-compressible formats

* Add Javadoc

* coderabbit fixes

* added getter for HttpResponseBuilder and rewrote code that used reflection

* fixed IOException
* Create RequestTimeOutFilter and RequestTimeOutFilterTest

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Revert "Create RequestTimeOutFilter and RequestTimeOutFilterTest"

This reverts commit efc883d.

* Create RequestTimeOutFilter and RequestTimeOutFilterTest

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Add test for exception in RequestTimeOutFilterTest

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Add a test for exception in RequestTimeOutFilterTest

Changes by ebbaandersson

* Add javadocs for RequestTimeOutFilter

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Changes made after CodeRabbit Review

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Change ExecutorService, add rejectedExecutionException, add method handleInternalError

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Changes tests to assert the new code in RequestTimeOutFilter

* Create RequestTimeOutFilter and RequestTimeOutFilterTest

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Add test for exception in RequestTimeOutFilterTest

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Add a test for exception in RequestTimeOutFilterTest

Changes by ebbaandersson

* Add javadocs for RequestTimeOutFilter

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Changes made after CodeRabbit Review

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Change ExecutorService, add rejectedExecutionException, add method handleInternalError

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Changes tests to assert the new code in RequestTimeOutFilter

* add a future.cancel to stop the worker task.

add defensive copying and unmodifiable views of setter and getter in HttpResponseBuilder

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Removes "static" from ExecutorService.
Adds a test which checks that the  timeout-time is bigger than zero

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

* Adds chain-execution verification to happy-path-test.

* Implementing a transfer from existing respons to shadowResponse in the beginning to make sure we don't lose information

Co-authored-by: Ebba Andersson <eeebbaandersson@gmail.com>

---------

Co-authored-by: Fredrik Mohlen <fredrikmohlen@gmail.com>
* Added new methods loadFromClassPath, loadOnceWithClasspathFallback and createMapperForName.

Modified createMapperFor-method to work with createMapperForName

* Updated main to use new method loadOnceWithClasspathFallback

* Added new test: fallback_to_classpath_when_external_file_missing()

* reworked loadOnceWithCLasspathFallback to adress external file deleted between Files.exists and load() silently bypasses the classpath fallback.

Removed unused code from main.

* Replaced new YAMLFactory() with YAMLFactory.builder().build() after feedback from coderabbit.

* Uppdated main to run both loadOnceWithClasspathFallback and cli.

* Added null preconditions after feedback from coderabbit and removed unused import.
* Add dependency to Pom

* Centralized logger, prints date - time and class. followed by logger level (info/error..) and message

* Replace java.util.logger with new logger

* Add logger to tcp server

* Update tests to match new logger

* Switch old util logger to new centralized logger

* Update LOG_LEVEL with a fallback

* Delete src/main/java/org/example/ServerLogger.java

* Change logg -> log

* Refactor

* Refactor comment
* added brotli Compression

* Implement compression priority (Brotli > Gzip)

* added Brotli compression tests and fixed ensureAvailability

* added brotli documentation

* added error prints

* coderabbit fixes

* Added a static BROTLI_AVAILABLE field
* Prepare LocaleStatsFilter by renaming LocaleFilter and LocaleFilterTest, clearing previous implementation

* Implement LocaleStatsFilter for collecting client locale statistics

* Add: unit tests for LocaleStatsFilter

* Add: resetStatsForTests helper for clearing static state in tests

* Add: Javadoc with usage instructions

* Fix: Javadoc formatting

* Fix: limit tracked locales and normalize locale strings in LocaleStatsFilter after feedback from rabbit

* Fix: normalize locale keys to lowercase, update docs and enforce MAX_TRACKED_LOCALES

* Fix: update expected locale keys in tests to lowercase to match normalization

* Update: clarify filter ordering requirement for LocaleStatsFilter in javadoc
* Created test-files/ directory for testing files and the file-test.html page in www/.

* Added the design of the file-test.html page

* Added JavaScript logic to display files

* Fixed warnings

* - Cleaned up unused HTML code
- Made the JavaScript safer and easier to read
- Added comments and docstrings to the JavaScript
- Added test files, the soundbite is taken from a royalty-free music library and was combined with the GIF version of the logo to create the video files

* - Removed duplicate test.jpg file outside test-files/ directory.
- Applied CodeRabbit suggestions JavaScript if-statement.
* - Added jspecify to pom.xml
- @NullMarked to src/main/java/org/example
- Checked all files in example for handling null return values.

* - @NullMarked to src/main/java/org/example/config
- Checked all files for handling null return values

* - Added @NullMarked to src/main/java/org/example/filter package
- Checked all files for handling null return values

* - Added @NullMarked to src/main/java/org/example/http package
- Checked all files for handling null return values

* - Added @NullMarked to src/main/java/org/example/httpparser package
- Checked all files for handling null return values

* - Added @NullMarked to src/main/java/org/example/server package
- Checked all files for handling null return values

* - Added @NullMarked to src/test/java/org/example package
- Checked all files for handling null return values

* - Added @NullMarked to src/test/java/org/example/config package
- Checked ConfigLoaderTest for null handling

* - Added @NullMarked to src/test/java/org/example/filter package
- Checked all tests for null handling
- Marked body in HttpRequest parameters as nullable to satisfy tests in CompressionFilterTest.
- Marked ip parameters for normalizeIp, addBlockedIp and addAllowedIp to be Nullable since they work as intended but warnings need to be suppressed.
-Added @nullable to request parameter for doFilter in LocaleFilter since it is handled.

* - Added @NullMarked to src/test/java/org/example/http package
- Checked all tests for null handling
- Marked filename parameter in detectMimeType as Nullable since it's handled.

* - Added @NullMarked to src/test/java/org/example/httpparser package
- Checked all tests for null handling
- Marked "in" parameter for setReader in HttpParser as nullable since it is handled.

* - Added @NullMarked to src/test/java/org/example/server package
- Checked ConfigurableFilterPipelineTest for null handling
- Marked the routePatterns parameter in the FilterRegistration record in FilterRegistration.java since it is safely null handled.

* Applied suggested CodeRabbit fixes to Major Issues regarding null markings and null handling.

* CodeRabbit test went against intended outcome of test. Rollbacked change to SetReader in HttpParser

* - LocaleFilteR: Moved @nullable from doFilter request parameter to resolveLocale request parameter since resolveLocale does the actual null handling.
- Removed testParserThrowErrorWhenNull test since it is made redundant by jspecify null marking.

* - Fixed compilation errors.
- Tests and compilation run without issue on my local setup now.
#107)

* Refactor StaticFileHandler and test suite to use HttpRequest and HttpResponseBuilder.

* Refactor test cases to use HttpResponseBuilder assertions and enhance URI normalization in StaticFileHandler.

* Add support for 405 responses to non-GET methods in StaticFileHandler and tests.

* Set `Allow` header to "GET" for 405 responses in `StaticFileHandler`, update test coverage (#111)

* Comprehensive Unit Tests for FilterChainImpl after implemented TerminalHandler (#113)

* Add unit test for `FilterChainImpl` to verify terminal handler invocation without filters

* Test confirms that chain.doFilter() correctly triggers the next filter in sequence and terminates at the handler.

* Test ensures TerminalHandler is bypassed when a filter blocks the chain

---------

Co-authored-by: Caroline Nordbrandt <caroline_nordbradt@hotmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 15, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 391c2054-d747-4cf7-aee9-2101bef0872f

📥 Commits

Reviewing files that changed from the base of the PR and between 76504f7 and e059fad.

⛔ Files ignored due to path filters (11)
  • src/main/resources/test.jpg is excluded by !**/*.jpg
  • www/test-files/test.gif is excluded by !**/*.gif
  • www/test-files/test.jpeg is excluded by !**/*.jpeg
  • www/test-files/test.jpg is excluded by !**/*.jpg
  • www/test-files/test.mp3 is excluded by !**/*.mp3
  • www/test-files/test.mp4 is excluded by !**/*.mp4
  • www/test-files/test.pdf is excluded by !**/*.pdf
  • www/test-files/test.png is excluded by !**/*.png
  • www/test-files/test.svg is excluded by !**/*.svg
  • www/test-files/test.wav is excluded by !**/*.wav
  • www/test-files/test.webm is excluded by !**/*.webm
📒 Files selected for processing (69)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • Dockerfile
  • PortConfigurationGuide.md
  • README.md
  • pom.xml
  • src/main/java/org/example/App.java
  • src/main/java/org/example/ConnectionFactory.java
  • src/main/java/org/example/ConnectionHandler.java
  • src/main/java/org/example/StaticFileHandler.java
  • src/main/java/org/example/TcpServer.java
  • src/main/java/org/example/config/AppConfig.java
  • src/main/java/org/example/config/ConfigLoader.java
  • src/main/java/org/example/config/package-info.java
  • src/main/java/org/example/filter/CompressionFilter.java
  • src/main/java/org/example/filter/Filter.java
  • src/main/java/org/example/filter/FilterChain.java
  • src/main/java/org/example/filter/FilterChainImpl.java
  • src/main/java/org/example/filter/IpFilter.java
  • src/main/java/org/example/filter/LocaleFilter.java
  • src/main/java/org/example/filter/LocaleFilterWithCookie.java
  • src/main/java/org/example/filter/LocaleStatsFilter.java
  • src/main/java/org/example/filter/LoggingFilter.java
  • src/main/java/org/example/filter/RequestTimeOutFilter.java
  • src/main/java/org/example/filter/package-info.java
  • src/main/java/org/example/http/HttpResponseBuilder.java
  • src/main/java/org/example/http/MimeTypeDetector.java
  • src/main/java/org/example/http/package-info.java
  • src/main/java/org/example/httpparser/HttpParseRequestLine.java
  • src/main/java/org/example/httpparser/HttpParser.java
  • src/main/java/org/example/httpparser/HttpRequest.java
  • src/main/java/org/example/httpparser/package-info.java
  • src/main/java/org/example/package-info.java
  • src/main/java/org/example/server/ConfigurableFilterPipeline.java
  • src/main/java/org/example/server/FilterRegistration.java
  • src/main/java/org/example/server/RoutePattern.java
  • src/main/java/org/example/server/TerminalHandler.java
  • src/main/java/org/example/server/package-info.java
  • src/main/resources/application.yml
  • src/main/resources/logback.xml
  • src/test/java/org/example/AppPortResolutionTest.java
  • src/test/java/org/example/ConnectionHandlerTest.java
  • src/test/java/org/example/StaticFileHandlerTest.java
  • src/test/java/org/example/TcpServerTest.java
  • src/test/java/org/example/config/ConfigLoaderTest.java
  • src/test/java/org/example/config/package-info.java
  • src/test/java/org/example/filter/CompressionFilterTest.java
  • src/test/java/org/example/filter/FilterChainImplTest.java
  • src/test/java/org/example/filter/IpFilterTest.java
  • src/test/java/org/example/filter/LocaleFilterWithCookieTest.java
  • src/test/java/org/example/filter/LocaleStatsFilterTest.java
  • src/test/java/org/example/filter/LoggingFilterTest.java
  • src/test/java/org/example/filter/RequestTimeOutFilterTest.java
  • src/test/java/org/example/filter/package-info.java
  • src/test/java/org/example/http/HttpResponseBuilderTest.java
  • src/test/java/org/example/http/MimeTypeDetectorTest.java
  • src/test/java/org/example/http/package-info.java
  • src/test/java/org/example/httpparser/HttpParseRequestLineTest.java
  • src/test/java/org/example/httpparser/HttpParserTest.java
  • src/test/java/org/example/httpparser/package-info.java
  • src/test/java/org/example/package-info.java
  • src/test/java/org/example/server/ConfigurableFilterPipelineTest.java
  • src/test/java/org/example/server/package-info.java
  • src/test/resources/application.yml
  • www/file-test.html
  • www/index.html
  • www/pageNotFound.html
  • www/test-files/test.txt
  • www/test-files/test.webp

📝 Walkthrough

Walkthrough

This pull request introduces a complete, production-ready Java HTTP web server with request filtering architecture, configuration management, static file serving, Docker containerization, and CI/CD workflows. It includes Maven-based builds, comprehensive test coverage, and documentation.

Changes

Cohort / File(s) Summary
CI/CD & Docker Infrastructure
.github/workflows/ci.yml, .github/workflows/release.yml, Dockerfile
Adds Maven-based GitHub Actions CI workflow (compile and test on push/PR to main), GitHub Actions release workflow for multi-arch Docker image publishing to GHCR, and multi-stage Dockerfile with healthcheck, non-root user, and optimized layers.
Configuration Management
src/main/java/org/example/config/AppConfig.java, src/main/java/org/example/config/ConfigLoader.java, src/main/java/org/example/config/package-info.java
Introduces AppConfig records (ServerConfig, LoggingConfig, IpFilterConfig) with defaults and validation, and ConfigLoader for thread-safe configuration loading from YAML/JSON with classpath fallback support.
Application Bootstrap & Server Core
src/main/java/org/example/App.java, src/main/java/org/example/TcpServer.java, src/main/java/org/example/ConnectionHandler.java, src/main/java/org/example/ConnectionFactory.java, src/main/java/org/example/package-info.java
Adds port resolution logic (CLI priority over config), TcpServer with virtual thread per-client handling and error management, ConnectionHandler for request routing through filters, and ConnectionFactory interface.
HTTP Protocol Handling
src/main/java/org/example/httpparser/HttpParseRequestLine.java, src/main/java/org/example/httpparser/HttpParser.java, src/main/java/org/example/httpparser/HttpRequest.java, src/main/java/org/example/httpparser/package-info.java
Introduces HTTP request line and header parsing, HttpRequest immutable request representation with mutable attributes map.
HTTP Response Building
src/main/java/org/example/http/HttpResponseBuilder.java, src/main/java/org/example/http/MimeTypeDetector.java, src/main/java/org/example/http/package-info.java
Adds HttpResponseBuilder for constructing HTTP responses with header/body management and status codes, MimeTypeDetector for content-type mapping.
Request Processing Filter Chain Architecture
src/main/java/org/example/filter/Filter.java, src/main/java/org/example/filter/FilterChain.java, src/main/java/org/example/filter/FilterChainImpl.java, src/main/java/org/example/filter/package-info.java
Defines Filter interface with lifecycle methods, FilterChain contract, and FilterChainImpl for chain-of-responsibility pattern execution.
Filter Implementations
src/main/java/org/example/filter/IpFilter.java, src/main/java/org/example/filter/CompressionFilter.java, src/main/java/org/example/filter/LoggingFilter.java, src/main/java/org/example/filter/LocaleFilterWithCookie.java, src/main/java/org/example/filter/LocaleStatsFilter.java, src/main/java/org/example/filter/RequestTimeOutFilter.java
Adds IP-based request filtering (allowlist/blocklist modes), Brotli/gzip compression with content-type awareness, request logging with timing, locale resolution from cookies/headers, locale statistics tracking, and request timeout handling with thread pool execution.
Server Infrastructure & Route Handling
src/main/java/org/example/server/TerminalHandler.java, src/main/java/org/example/server/ConfigurableFilterPipeline.java, src/main/java/org/example/server/FilterRegistration.java, src/main/java/org/example/server/RoutePattern.java, src/main/java/org/example/server/package-info.java
Introduces TerminalHandler interface, ConfigurableFilterPipeline for ordered filter execution with route-specific filtering, FilterRegistration for filter metadata, and RoutePattern for path matching with wildcard support.
Static File Serving
src/main/java/org/example/StaticFileHandler.java
Implements terminal handler for static file serving with path traversal protection, default file resolution (index.html), custom 404 pages, and MIME type detection.
Build Configuration
pom.xml
Updates Maven compiler to Java 25, adds dependencies (Jackson YAML/JSON, Bucket4j rate limiting, Brotli4j compression, Logback logging, JSpecify annotations), and plugins (Pitest mutation testing, Spotless code formatting).
Documentation & Configuration
README.md, PortConfigurationGuide.md, src/main/resources/application.yml, src/main/resources/logback.xml
Replaces README with comprehensive server guide including features, configuration formats, directory structure, MIME mappings, security details, and deployment examples. Adds port configuration guide, default application.yml with server/logging/ipFilter settings, and Logback XML configuration.
Unit Tests - Core Components
src/test/java/org/example/AppPortResolutionTest.java, src/test/java/org/example/ConnectionHandlerTest.java, src/test/java/org/example/StaticFileHandlerTest.java, src/test/java/org/example/TcpServerTest.java
Adds tests for port resolution precedence, connection handling with mocked sockets, static file serving (200/404/403 scenarios, path traversal, null-byte injection, URI sanitization), and TCP server error handling.
Unit Tests - Configuration
src/test/java/org/example/config/ConfigLoaderTest.java, src/test/java/org/example/config/package-info.java
Comprehensive tests for configuration loading (defaults, YAML parsing, field defaulting, unknown field ignoring, caching, validation, classpath fallback).
Unit Tests - HTTP Parsing
src/test/java/org/example/httpparser/HttpParseRequestLineTest.java, src/test/java/org/example/httpparser/HttpParserTest.java, src/test/java/org/example/httpparser/package-info.java
Tests for request line parsing (method, URI, version extraction and validation), header parsing with multi-value aggregation and invalid line handling.
Unit Tests - HTTP Response
src/test/java/org/example/http/HttpResponseBuilderTest.java, src/test/java/org/example/http/MimeTypeDetectorTest.java, src/test/java/org/example/http/package-info.java
Comprehensive tests for response building (status codes, headers, body handling, binary content preservation, content-length calculation), MIME type detection (case-insensitivity, unknown extensions, edge cases).
Unit Tests - Filters
src/test/java/org/example/filter/FilterChainImplTest.java, src/test/java/org/example/filter/IpFilterTest.java, src/test/java/org/example/filter/CompressionFilterTest.java, src/test/java/org/example/filter/LoggingFilterTest.java, src/test/java/org/example/filter/LocaleFilterWithCookieTest.java, src/test/java/org/example/filter/LocaleStatsFilterTest.java, src/test/java/org/example/filter/RequestTimeOutFilterTest.java, src/test/java/org/example/filter/package-info.java
Tests for filter chain execution order and interruption, IP filtering (allowlist/blocklist/missing IP), compression encoding selection and decompression, request logging with timing, locale resolution from cookies/headers, locale statistics aggregation, request timeout and error handling.
Unit Tests - Server Infrastructure
src/test/java/org/example/server/ConfigurableFilterPipelineTest.java, src/test/java/org/example/server/package-info.java
Tests for configurable filter pipeline with global and route-specific filter execution, ordering semantics, path matching, and early termination.
Static Content
www/index.html, www/pageNotFound.html, www/file-test.html, www/test-files/test.txt, src/test/resources/application.yml
Adds sample static content (index page, custom 404 error page, file viewer demo), test files, and test configuration override.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant TcpServer
    participant ConnectionHandler
    participant FilterChainImpl
    participant Filter as Filter (e.g. IpFilter, CompressionFilter)
    participant StaticFileHandler
    participant HttpResponseBuilder

    Client->>TcpServer: TCP Connection
    TcpServer->>ConnectionHandler: create(Socket)
    TcpServer->>ConnectionHandler: runConnectionHandler()
    
    ConnectionHandler->>ConnectionHandler: parseHttpRequest()
    ConnectionHandler->>FilterChainImpl: doFilter(HttpRequest, HttpResponseBuilder)
    
    loop For each Filter in chain
        FilterChainImpl->>Filter: doFilter(request, response, chain)
        Filter->>Filter: validate/transform request
        alt Filter allows
            Filter->>FilterChainImpl: chain.doFilter(request, response)
        else Filter blocks
            Filter->>HttpResponseBuilder: setStatusCode(403/400)
            Note over Filter: Chain terminates
        end
    end
    
    FilterChainImpl->>StaticFileHandler: handle(HttpRequest, HttpResponseBuilder)
    StaticFileHandler->>StaticFileHandler: normalize path & check traversal
    
    alt File exists
        StaticFileHandler->>HttpResponseBuilder: setStatusCode(200)
        StaticFileHandler->>HttpResponseBuilder: setContentType(MIME)
        StaticFileHandler->>HttpResponseBuilder: setBody(fileBytes)
    else File not found
        StaticFileHandler->>HttpResponseBuilder: setStatusCode(404)
        StaticFileHandler->>HttpResponseBuilder: setBody(pageNotFound.html)
    end
    
    ConnectionHandler->>HttpResponseBuilder: build()
    ConnectionHandler->>Client: write response bytes
Loading
sequenceDiagram
    participant Main as App.main()
    participant ConfigLoader
    participant FileSystem
    participant TcpServer
    participant ConnectionFactory

    Main->>ConfigLoader: loadOnceWithClasspathFallback(path, resource)
    ConfigLoader->>FileSystem: check external config file
    
    alt External config exists
        ConfigLoader->>FileSystem: read & parse YAML/JSON
        ConfigLoader->>ConfigLoader: applyDefaults()
        ConfigLoader->>ConfigLoader: cache result
    else External config missing
        ConfigLoader->>ConfigLoader: loadFromClasspath(resource)
        ConfigLoader->>ConfigLoader: applyDefaults()
        ConfigLoader->>ConfigLoader: cache result
    end
    
    Main->>Main: resolvePort(args, configPort)
    alt --port CLI flag present
        Main->>Main: return CLI port (validated 1-65535)
    else No CLI flag
        Main->>Main: return configPort
    end
    
    Main->>TcpServer: new TcpServer(port, connectionFactory)
    Main->>TcpServer: start()
    TcpServer->>TcpServer: bind ServerSocket(port)
    TcpServer->>TcpServer: listen for connections
    
    loop For each client connection
        TcpServer->>ConnectionFactory: create(Socket)
        TcpServer->>ConnectionFactory: handleClient(Socket)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #112: FilterChainImplTest directly implements sequential execution and chain interruption test cases for FilterChainImpl as specified in the issue.
  • #122: Dockerfile includes a HEALTHCHECK instruction that curls http://localhost:8080/, which is the exact implementation requested in the issue.

Poem

🐰 A hop through the code, a server takes flight!
Filters dance gracefully, left and right,
Static files served with path-traversal care,
Docker containers float through the air, 🐳
From config to response, the architecture's fair!

Important

Merge conflicts detected

  • Resolve merge conflict in branch feature/healthcheck
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

Migrating from UI to YAML configuration.

Use the @coderabbitai configuration command in a PR comment to get a dump of all your UI settings in YAML format. You can then edit this YAML file and upload it to the root of your repository to configure CodeRabbit programmatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.