Skip to content

96 Create page for testing filytypes#26

Closed
Martin-E-Karlsson wants to merge 41 commits intomainfrom
96-create-page-for-testing-filytypes
Closed

96 Create page for testing filytypes#26
Martin-E-Karlsson wants to merge 41 commits intomainfrom
96-create-page-for-testing-filytypes

Conversation

@Martin-E-Karlsson
Copy link

@Martin-E-Karlsson Martin-E-Karlsson commented Mar 1, 2026

  • A HTML page with JavaScript logic to simplify viewing a selection of test files.
  • Created directory for test files and populated with files that are either royalty-free or created for this test purpose.

Summary by CodeRabbit

Release Notes

  • New Features

    • Java-based HTTP web server with static file serving and configurable root directory
    • Port configuration via CLI arguments, YAML configuration files, or defaults
    • IP filtering with allowlist/blocklist modes for access control
    • Gzip response compression for optimized data transfer
    • Request timeout enforcement and automatic 500 error handling
    • Locale detection from Accept-Language headers and cookies
  • Documentation

    • Added comprehensive README with features, configuration, and deployment instructions
    • Added port configuration guide with priority levels
  • Deployment

    • Docker support with multi-architecture image publishing (amd64, arm64)
    • Automated CI/CD pipelines for Maven builds and Docker releases

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 11 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.
- 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
Merging changes to main branch to solve potential merge issues before
pull request
@coderabbitai
Copy link

coderabbitai bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

A comprehensive Java HTTP web server implementation is introduced with configuration management, a filter pipeline architecture, static file serving capabilities, HTTP parsing/response building infrastructure, Docker containerization, and CI/CD automation. The addition spans core server components, filter implementations, configuration classes, extensive test coverage, and static resources.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/ci.yml, .github/workflows/release.yml
Adds Maven-based Java CI workflow for compilation and testing on main branch. Introduces Docker image build and push workflow for multi-architecture (amd64, arm64) container publishing to GitHub Packages on release events.
Build & Containerization
Dockerfile, pom.xml
Dockerfile implements multi-stage Maven build with Java 25 runtime, exposes port 8080, and sets up dedicated app user. pom.xml updates Maven compiler to Java 25, adds bucket4j rate limiting and testing dependencies (mockito, awaitility, jackson, brotli4j), and introduces pitest and spotless plugins for mutation testing and code formatting.
Documentation
README.md, PortConfigurationGuide.md
README heavily rewritten from minimal tutorial to comprehensive documentation covering features, requirements, quick start, configuration, directory structure, security, and troubleshooting. PortConfigurationGuide details three-tier port resolution priority (CLI, config file, default 8080) with examples.
Configuration & Bootstrapping
src/main/java/org/example/App.java, src/main/resources/application.yml
App.java now performs configuration-driven port initialization with CLI override support and server startup via TcpServer. application.yml defines defaults for server port (8080), rootDir, logging level, and IP filter settings.
Core Server Components
src/main/java/org/example/TcpServer.java, src/main/java/org/example/ConnectionHandler.java, src/main/java/org/example/StaticFileHandler.java, src/main/java/org/example/ConnectionFactory.java
TcpServer listens on configured port and spawns virtual threads per client. ConnectionHandler orchestrates request processing with filter pipeline and delegates static serving. StaticFileHandler serves files with path traversal protection and custom error pages. ConnectionFactory defines contract for handler creation.
HTTP Infrastructure
src/main/java/org/example/httpparser/HttpParser.java, src/main/java/org/example/httpparser/HttpParseRequestLine.java, src/main/java/org/example/httpparser/HttpRequest.java, src/main/java/org/example/http/HttpResponseBuilder.java, src/main/java/org/example/http/MimeTypeDetector.java
HttpParser and HttpParseRequestLine parse HTTP request lines and headers with validation. HttpRequest immutable container holds method, path, version, headers, and attributes. HttpResponseBuilder constructs HTTP responses with status codes, headers, and body handling. MimeTypeDetector maps file extensions to MIME types.
Filter Framework
src/main/java/org/example/filter/Filter.java, src/main/java/org/example/filter/FilterChain.java, src/main/java/org/example/filter/FilterChainImpl.java
Defines Filter lifecycle interface (init, doFilter, destroy) and FilterChain contract. FilterChainImpl sequentially processes filters with optional terminal handler.
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/LocaleFilter.java, src/main/java/org/example/filter/LocaleFilterWithCookie.java, src/main/java/org/example/filter/RequestTimeOutFilter.java
IpFilter blocks/allows by IP in BLOCKLIST/ALLOWLIST modes. CompressionFilter applies gzip when appropriate. LoggingFilter records request metrics. LocaleFilter resolves locale from Accept-Language header or cookie. RequestTimeOutFilter enforces execution timeout with 504/503 error handling.
Configuration Classes
src/main/java/org/example/config/AppConfig.java, src/main/java/org/example/config/ConfigLoader.java
AppConfig defines immutable records for server, logging, and IP filter configuration with defaults and validation. ConfigLoader provides thread-safe lazy loading from filesystem or classpath with fallback handling.
Server Infrastructure
src/main/java/org/example/server/ConfigurableFilterPipeline.java, src/main/java/org/example/server/FilterRegistration.java, src/main/java/org/example/server/RoutePattern.java
ConfigurableFilterPipeline executes global and route-specific filters in order. FilterRegistration encapsulates filter with order and route patterns. RoutePattern provides path matching with wildcard support.
Comprehensive Test Suite
src/test/java/org/example/*.java, src/test/java/org/example/config/*.java, src/test/java/org/example/filter/*.java, src/test/java/org/example/http/*.java, src/test/java/org/example/httpparser/*.java, src/test/java/org/example/server/*.java
Extensive JUnit 5 tests covering port resolution, connection handling, file serving, filter behavior (IP filtering, compression, logging, locale resolution, timeouts), HTTP parsing, response building, MIME detection, and filter pipeline orchestration.
Test Configuration
src/test/resources/application.yml
Defines test-specific configuration with server port 3030 and DEBUG logging level.
Static Web Resources
www/index.html, www/pageNotFound.html, www/file-test.html, www/test-files/test.txt
HTML pages for index, 404 error, and interactive file type viewer. Test text file for validation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant TcpServer
    participant ConnectionHandler
    participant HttpParser
    participant FilterChain as Filter Pipeline
    participant StaticFileHandler
    participant HttpResponseBuilder

    Client->>TcpServer: Connect on port 8080
    TcpServer->>ConnectionHandler: Create per connection
    ConnectionHandler->>HttpParser: Parse HTTP request
    HttpParser-->>ConnectionHandler: HttpRequest object
    ConnectionHandler->>FilterChain: Apply filters (IP, Compression, Logging, etc.)
    FilterChain->>FilterChain: Execute filters in sequence
    FilterChain-->>ConnectionHandler: Filtered HttpResponseBuilder
    ConnectionHandler->>StaticFileHandler: Resolve & serve requested file
    StaticFileHandler->>HttpResponseBuilder: Build response (200/404/403)
    HttpResponseBuilder-->>StaticFileHandler: Assembled HTTP bytes
    StaticFileHandler-->>ConnectionHandler: Response ready
    ConnectionHandler-->>TcpServer: Send response bytes
    TcpServer-->>Client: HTTP response (200/404/etc)
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

A web server springs forth with filters so fleet,
Configuration guides each request to meet,
Compression zips, locales dance with delight,
Docker containers package it tight,
From Maven's command to the cloud above,
A rabbit's creation, built with much love! 🐰

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title references creating a page for testing file types but contains a typo ('filytypes' instead of 'filetypes'), and is only partially descriptive of the full changeset which includes infrastructure (CI/CD workflows, Docker, configuration system, filters, HTTP parsing, and extensive documentation). Correct the typo to 'filetypes' and consider whether the title should reflect the broader scope of this changeset, such as 'Add web server infrastructure, configuration, filters, and file-type testing page' or a more comprehensive descriptor.
Docstring Coverage ⚠️ Warning Docstring coverage is 9.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (11)
PortConfigurationGuide.md-26-29 (1)

26-29: ⚠️ Potential issue | 🟡 Minor

Fix YAML indentation in the example.

The port key must be indented under server: to be valid YAML. The current example would create a top-level port key instead of server.port.

📝 Proposed fix
 ```yaml
 server:
-port: 9090
+  port: 9090
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @PortConfigurationGuide.md around lines 26 - 29, The YAML example has
incorrect indentation so port is defined at top-level instead of under
server; update the example so the port key is indented two spaces beneath
server (i.e., make server: the parent and port: a child) so the documented
configuration reflects server.port correctly; verify the block shows server:
on one line and port: 9090 indented under it.


</details>

</blockquote></details>
<details>
<summary>.github/workflows/release.yml-13-13 (1)</summary><blockquote>

`13-13`: _⚠️ Potential issue_ | _🟡 Minor_

**Update `actions/checkout` to a valid version.**

`actions/checkout@v6.0.2` does not exist. The latest release is `v6.0.1`. Use either `v6` for automatic patch updates or pin to `v6.0.1`.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml at line 13, Replace the invalid
actions/checkout@v6.0.2 reference in the workflow with a valid tag; update the
checkout step that currently uses "actions/checkout@v6.0.2" to either the
floating major tag "actions/checkout@v6" for automatic patch updates or pin it
to the known release "actions/checkout@v6.0.1" so the workflow uses a valid,
published version.


</details>

</blockquote></details>
<details>
<summary>www/file-test.html-320-326 (1)</summary><blockquote>

`320-326`: _⚠️ Potential issue_ | _🟡 Minor_

**Add missing-file handling for PDF/video/audio previews.**

Image/text paths show a clear “file not found” fallback, but PDF/video/audio do not. Missing assets currently degrade to broken players/embeds without consistent feedback.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @www/file-test.html around lines 320 - 326, The PDF/video/audio branches
(category === 'pdf', 'video', 'audio' that set contentArea.innerHTML with
,


</details>

</blockquote></details>
<details>
<summary>README.md-2-2 (1)</summary><blockquote>

`2-2`: _⚠️ Potential issue_ | _🟡 Minor_

**Resolve markdownlint findings in this README block.**

The current content still has lint-visible structure issues (heading increment, extra heading spacing, and fenced blocks missing language tags), which will keep docs quality checks noisy.



Also applies to: 37-37, 39-39, 80-80, 87-87, 134-134, 234-234, 283-283, 340-340

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against the current code and only fix it if needed.

In @README.md at line 2, Fix the markdownlint issues around the README heading
"### Class JUV25G" by normalizing heading levels (ensure proper incremental
heading hierarchy, e.g., use "##" or "####" to match surrounding sections),
remove extra blank lines after headings (no double spacing), and add language
tags to all fenced code blocks (e.g., json, bash, etc.); apply the same
adjustments to the other occurrences referenced (the other blocks containing the
same heading pattern or fenced blocks) so the document conforms to markdownlint
rules.


</details>

</blockquote></details>
<details>
<summary>README.md-48-49 (1)</summary><blockquote>

`48-49`: _⚠️ Potential issue_ | _🟡 Minor_

**Fix Quick Start commands (`git clone` typo + directory mismatch).**

At Line 48, command is duplicated (`git clone git clone ...`). At Line 49, the directory name should match the actual cloned repo folder.

<details>
<summary>Diff suggestion</summary>

```diff
-git clone git clone https://github.com/ithsjava25/project-webserver-juv25g.git
-cd project-webserver
+git clone https://github.com/ithsjava25/project-webserver-juv25g.git
+cd project-webserver-juv25g
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 48 - 49, Fix the Quick Start commands by removing the
duplicated word in the clone command and making the cd target match the cloned
repository folder; replace the line containing "git clone git clone
https://github.com/ithsjava25/project-webserver-juv25g.git" with a single "git
clone ..." and update the following "cd project-webserver" to "cd
project-webserver-juv25g" so the commands correctly clone and enter the cloned
repo directory.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/ConnectionHandlerTest.java-31-35 (1)</summary><blockquote>

`31-35`: _⚠️ Potential issue_ | _🟡 Minor_

**Reset global `ConfigLoader` state after this test class.**

This class mutates global cached config in setup but never clears it, which can make later tests order-dependent.


<details>
<summary>💡 Suggested fix</summary>

```diff
-import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
@@
     `@BeforeAll`
     static void setupConfig() {
         ConfigLoader.resetForTests();
         ConfigLoader.loadOnce(Path.of("nonexistent-test-config.yml"));
     }
+
+    `@AfterAll`
+    static void tearDownConfig() {
+        ConfigLoader.resetForTests();
+    }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/ConnectionHandlerTest.java` around lines 31 - 35,
The setupConfig method mutates the global cached config and doesn't clear it
afterwards; add an `@AfterAll` static teardown method in ConnectionHandlerTest
(e.g., tearDownConfig or resetConfig) that calls ConfigLoader.resetForTests() to
restore global state after the test class, ensuring ConfigLoader.resetForTests()
is invoked following setupConfig's ConfigLoader.loadOnce(...) to avoid
order-dependent tests.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/filter/LoggingFilter.java-38-41 (1)</summary><blockquote>

`38-41`: _⚠️ Potential issue_ | _🟡 Minor_

**Copy-paste error in comment.**

The comment says "No initialization needed" but this is the `destroy()` method.


<details>
<summary>📝 Suggested fix</summary>

```diff
     `@Override`
     public void destroy() {
-        //No initialization needed
+        //No cleanup needed
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/LoggingFilter.java` around lines 38 - 41,
Update the incorrect copy-paste comment inside the LoggingFilter.destroy()
method: replace "//No initialization needed" with a correct remark such as "//No
cleanup needed" or "//No resources to release" (or remove the comment entirely)
so the comment accurately describes tear-down behavior in the destroy() method
of class LoggingFilter.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/filter/CompressionFilterTest.java-161-161 (1)</summary><blockquote>

`161-161`: _⚠️ Potential issue_ | _🟡 Minor_

**Test uses malformed JSON.**

The JSON string `"{\"data\": " + "\"value\",".repeat(200) + "}"` produces invalid JSON with a trailing comma before the closing brace. While this doesn't affect the compression test's validity, using valid JSON would be more realistic.


<details>
<summary>📝 Suggested fix for valid JSON</summary>

```diff
-        String jsonData = "{\"data\": " + "\"value\",".repeat(200) + "}";
+        String jsonData = "{\"data\": [" + "\"value\",".repeat(199) + "\"value\"]}";
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/filter/CompressionFilterTest.java` at line 161, The
test in CompressionFilterTest constructs malformed JSON via the jsonData
variable ("{\"data\": " + "\"value\",".repeat(200) + "}") which leaves a
trailing comma; update jsonData to produce valid JSON (e.g., create an array or
join repeated values without a trailing comma) so the test uses well-formed JSON
while preserving the same payload size for compression assertions; locate the
jsonData construction in CompressionFilterTest and replace the string building
with a valid JSON creation approach (for example build a JSON array or use
String.join on repeated elements) so the compression behavior remains unchanged.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/TcpServer.java-7-7 (1)</summary><blockquote>

`7-7`: _⚠️ Potential issue_ | _🟡 Minor_

**Unused import.**

`PrintWriter` is imported but not used in this file.


<details>
<summary>🧹 Remove unused import</summary>

```diff
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.PrintWriter;
 import java.net.ServerSocket;
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/TcpServer.java` at line 7, Remove the unused import
of PrintWriter from the TcpServer source: open the TcpServer class and delete
the line importing java.io.PrintWriter since no methods or fields in TcpServer
reference PrintWriter (this cleans up imports and avoids compiler warnings).
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/http/HttpResponseBuilder.java-84-87 (1)</summary><blockquote>

`84-87`: _⚠️ Potential issue_ | _🟡 Minor_

**Handle `null` in `setHeaders` safely.**

`setHeaders(null)` currently throws. A null-safe branch makes this API more robust for callers.


<details>
<summary>💡 Proposed fix</summary>

```diff
     public void setHeaders(Map<String, String> headers) {
         this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-        this.headers.putAll(headers);
+        if (headers != null) {
+            this.headers.putAll(headers);
+        }
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/http/HttpResponseBuilder.java` around lines 84 -
87, The setHeaders method in HttpResponseBuilder throws when called with null;
update setHeaders(Map<String, String> headers) to handle null safely by
initializing this.headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER) and
only calling putAll when headers != null (or else leave it empty), ensuring the
headers field is always a case-insensitive TreeMap even when the input is null.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/StaticFileHandler.java-26-33 (1)</summary><blockquote>

`26-33`: _⚠️ Potential issue_ | _🟡 Minor_

**Guard null/blank URI before sanitization.**

Line 28 dereferences `uri` immediately. A null URI will throw and abort the response path.


<details>
<summary>💡 Proposed fix</summary>

```diff
     private void handleGetRequest(String uri) throws IOException {
+        if (uri == null || uri.isBlank()) {
+            uri = "/";
+        }
         // Sanitize URI
         int q = uri.indexOf('?');
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/StaticFileHandler.java` around lines 26 - 33,
handleGetRequest currently dereferences uri immediately; add a null/blank guard
at the start of handleGetRequest to avoid NPEs by validating if (uri == null ||
uri.trim().isEmpty()) and then fail-fast: either throw an IOException with a
clear message (e.g. "empty or null URI") or return an appropriate error response
before performing any substring/replace operations on uri; update callers if
necessary so the response path remains consistent.
```

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (21)</summary><blockquote>

<details>
<summary>Dockerfile (1)</summary><blockquote>

`1-6`: **Consider combining Maven RUN commands to reduce image layers.**

The two separate `RUN mvn` commands create unnecessary intermediate layers. Combining them reduces the final build stage size.



<details>
<summary>♻️ Proposed optimization</summary>

```diff
 FROM maven:3-eclipse-temurin-25-alpine AS build
 WORKDIR /build
 COPY src/ src/
 COPY pom.xml pom.xml
-RUN mvn compile
-RUN mvn dependency:copy-dependencies -DincludeScope=compile
+RUN mvn compile dependency:copy-dependencies -DincludeScope=compile
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 1 - 6, The Dockerfile has two separate RUN layers
for Maven ("RUN mvn compile" and "RUN mvn dependency:copy-dependencies
-DincludeScope=compile"); combine these into a single RUN so both commands
execute in one layer (e.g., use && to chain "mvn compile" and "mvn
dependency:copy-dependencies -DincludeScope=compile") to reduce image layers and
final image size while keeping the same build steps.
```

</details>

</blockquote></details>
<details>
<summary>.github/workflows/ci.yml (1)</summary><blockquote>

`16-19`: **Note: This step relies on pre-installed Maven.**

The `mvn help:evaluate` command runs before `setup-java`, relying on Ubuntu's pre-installed Maven/Java. This works on `ubuntu-latest` but creates an implicit dependency. Consider adding a comment or documenting this assumption.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 16 - 19, The CI step named "Get Java
Version" uses the mvn help:evaluate command before setup-java, which implicitly
relies on a pre-installed Maven/Java; update the workflow to either run
setup-java (or install Maven) before this step or add a clear comment
documenting the implicit dependency so future maintainers know mvn help:evaluate
requires pre-installed Maven/Java; reference the step name "Get Java Version",
the command "mvn help:evaluate", and the "setup-java" step when making this
change.
```

</details>

</blockquote></details>
<details>
<summary>www/file-test.html (1)</summary><blockquote>

`298-298`: **Prefer DOM/text APIs over `innerHTML` for file path rendering.**

At Line 298, `innerHTML` is unnecessary and makes future unsafe interpolation easier to introduce. Use `textContent` and a created `<span>` node instead.

<details>
<summary>Diff suggestion</summary>

```diff
-    filePathDisplay.innerHTML = `<span>${path}</span>`;
+    filePathDisplay.textContent = '';
+    const pathSpan = document.createElement('span');
+    pathSpan.textContent = path;
+    filePathDisplay.appendChild(pathSpan);
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@www/file-test.html` at line 298, Replace the unsafe innerHTML assignment to
filePathDisplay.innerHTML with DOM/text APIs: create a span via
document.createElement('span'), set its textContent to the path variable, clear
or remove existing children of filePathDisplay, and append the new span node;
this ensures you use filePathDisplay and path but avoid innerHTML to prevent
accidental HTML injection and make rendering safe.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/httpparser/HttpParseRequestLineTest.java (1)</summary><blockquote>

`24-24`: **Use explicit UTF-8 in test input encoding.**

These `getBytes()` calls are platform-default dependent; use `StandardCharsets.UTF_8` for deterministic tests.



Also applies to: 41-41, 53-53, 64-64

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/httpparser/HttpParseRequestLineTest.java` at line
24, Replace platform-default byte encoding usages in HttpParseRequestLineTest by
calling getBytes(StandardCharsets.UTF_8) on testString to make tests
deterministic; update every occurrence like the one inside the test class (e.g.,
the InputStream construction in HttpParseRequestLineTest where you currently
call testString.getBytes()) to use StandardCharsets.UTF_8. Ensure you import
java.nio.charset.StandardCharsets if not already present and apply the same
change to the other occurrences mentioned (lines analogous to 41, 53, 64).
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/httpparser/HttpParserTest.java (1)</summary><blockquote>

`16-22`: **Align header parsing test with parser contract.**

At Line 17, this header test includes the request line and passes because non-header lines are skipped. Consider parsing request line first (or using headers-only input) so the test asserts header parsing behavior explicitly.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/httpparser/HttpParserTest.java` around lines 16 -
22, The test TestHttpParserForHeaders currently feeds a full HTTP request
(including the request line) to httpParser.parseHttp which lets the parser skip
non-header lines and masks header-only behavior; update the test to either (1)
feed headers-only input (start with "Host: ..." and end with "\r\n") before
calling httpParser.setReader and httpParser.parseHttp so assertions verify
header parsing explicitly, or (2) first invoke the parser method that consumes
the request line (or call a dedicated parseRequestLine method) on the
request-line input and then call parseHttp with the remaining headers-only
InputStream; adjust assertions accordingly in HttpParserTest and keep references
to TestHttpParserForHeaders, httpParser.setReader, and httpParser.parseHttp to
locate the change.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/config/ConfigLoaderTest.java (2)</summary><blockquote>

`142-152`: **Clean up test formatting.**

Minor formatting improvements: add space after comma on line 145 and remove excess blank lines at end of method.

<details>
<summary>🧹 Proposed cleanup</summary>

```diff
     `@Test`
     `@DisplayName`("missing external file should fallback to classpath")
-    void fallback_to_classpath_when_external_file_missing(){
-        AppConfig appConfig = ConfigLoader.loadOnceWithClasspathFallback(tempDir.resolve("missing.yml"),"application.yml");
+    void fallback_to_classpath_when_external_file_missing() {
+        AppConfig appConfig = ConfigLoader.loadOnceWithClasspathFallback(tempDir.resolve("missing.yml"), "application.yml");
 
         assertThat(appConfig.server().port()).isEqualTo(3030);
-
-
-
-
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/config/ConfigLoaderTest.java` around lines 142 -
152, In the test method fallback_to_classpath_when_external_file_missing, fix
formatting by adding a space after the comma in the call to
ConfigLoader.loadOnceWithClasspathFallback (i.e. tempDir.resolve("missing.yml"),
"application.yml") and remove the extra blank lines at the end of the method so
the closing brace follows the assertThat assertion directly; keep AppConfig and
assertThat usage unchanged.
```

</details>

---

`130-130`: **Fix method name formatting.**

The method name `invalid_port_should_Throw_Exception    ()` has extraneous spaces before the parentheses.

<details>
<summary>🧹 Proposed fix</summary>

```diff
-    void invalid_port_should_Throw_Exception    () throws Exception {
+    void invalid_port_should_Throw_Exception() throws Exception {
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/config/ConfigLoaderTest.java` at line 130, The test
method declaration invalid_port_should_Throw_Exception    () contains extra
spaces before the parentheses; locate the method in ConfigLoaderTest (method
name invalid_port_should_Throw_Exception) and remove the trailing spaces so the
signature reads invalid_port_should_Throw_Exception() (keeping the same name
casing) to correct the formatting.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/http/MimeTypeDetectorTest.java (1)</summary><blockquote>

`145-145`: **Translate comment to English.**

Line 145 contains a Swedish comment: "Parametriserad test för många filtyper på en gång" (Parameterized test for many file types at once).

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/http/MimeTypeDetectorTest.java` at line 145,
Replace the Swedish comment "Parametriserad test för många filtyper på en gång"
in MimeTypeDetectorTest (around the parameterized test declaration) with its
English translation "Parameterized test for many file types at once" so the
comment is in English and clearly documents the parameterized test.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/config/ConfigLoader.java (1)</summary><blockquote>

`117-119`: **Consider adding synchronization to `resetForTests()`.**

While `cached` is volatile, setting it to `null` without synchronization could theoretically race with `loadOnce` calls. For test reliability, consider synchronizing:

<details>
<summary>🔧 Proposed fix</summary>

```diff
 public static void resetForTests() {
+    synchronized (ConfigLoader.class) {
         cached = null;
+    }
 }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/config/ConfigLoader.java` around lines 117 - 119,
The resetForTests() method sets the volatile field cached to null without
synchronization and can race with loadOnce; update resetForTests() to perform
the null assignment inside the same synchronization used by loadOnce (e.g.,
synchronize on the ConfigLoader class or the specific lock object that loadOnce
uses) so both methods use the same monitor when touching cached.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/http/HttpResponseBuilderTest.java (1)</summary><blockquote>

`42-42`: **Translate comments to English for consistency.**

Several comments in this file are in Swedish (e.g., "UTF-8 content length för olika strängar", "för auto-append behavior", "Parametriserad test för många filtyper på en gång"). Consider translating them to English for international accessibility.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/http/HttpResponseBuilderTest.java` at line 42,
Translate all Swedish comments in HttpResponseBuilderTest to English for
consistency; locate comment strings like "UTF-8 content length för olika
strängar", "för auto-append behavior", and "Parametriserad test för många
filtyper på en gång" inside the HttpResponseBuilderTest class and replace them
with clear English equivalents (e.g., "UTF-8 content length for various
strings", "for auto-append behavior", "Parameterized test for many file types at
once") while preserving the original meaning and punctuation.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/config/AppConfig.java (1)</summary><blockquote>

`23-24`: **Remove development comments before merging.**

Lines 23-24 contain Swedish comments ("← LÄGG TILL", "← UPPDATERA DENNA RAD") that appear to be development notes and should be removed.

<details>
<summary>🧹 Proposed cleanup</summary>

```diff
-        IpFilterConfig ipFilterConfig = (ipFilter == null ? IpFilterConfig.defaults() : ipFilter.withDefaultsApplied());  // ← LÄGG TILL
-        return new AppConfig(serverConfig, loggingConfig, ipFilterConfig);  // ← UPPDATERA DENNA RAD
+        IpFilterConfig ipFilterConfig = (ipFilter == null ? IpFilterConfig.defaults() : ipFilter.withDefaultsApplied());
+        return new AppConfig(serverConfig, loggingConfig, ipFilterConfig);
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/config/AppConfig.java` around lines 23 - 24, Remove
the inline Swedish development comments on the two modified lines so the code
reads cleanly: keep the IpFilterConfig creation using (ipFilter == null ?
IpFilterConfig.defaults() : ipFilter.withDefaultsApplied()) and the return new
AppConfig(serverConfig, loggingConfig, ipFilterConfig) but delete the trailing
comments "← LÄGG TILL" and "← UPPDATERA DENNA RAD"; ensure identifiers
IpFilterConfig, ipFilter, withDefaultsApplied, AppConfig, serverConfig, and
loggingConfig remain unchanged.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/filter/RequestTimeOutFilterTest.java (1)</summary><blockquote>

`49-52`: **Consider reducing slow-path sleep duration to speed up test suite.**

A shorter delay still above the timeout threshold validates the same behavior while cutting test runtime.


<details>
<summary>⏱️ Suggested tweak</summary>

```diff
         FilterChain slowChain = (request, response) -> {
             try {
-                Thread.sleep(600);
+                Thread.sleep(250);
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
             }
         };
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/filter/RequestTimeOutFilterTest.java` around lines
49 - 52, In RequestTimeOutFilterTest update the slow-path sleep in the slowChain
lambda (the Thread.sleep(...) inside the FilterChain slowChain) to a much
smaller value that is still above the configured timeout (e.g., timeout + 10ms)
so the test still triggers the timeout behavior but runs faster; locate the
slowChain lambda in RequestTimeOutFilterTest and replace the 600ms sleep with a
minimal margin above the test's timeout value.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/filter/LocaleFilter.java (1)</summary><blockquote>

`67-70`: **Null check for headers is unnecessary.**

Based on `HttpRequest.getHeaders()` implementation (see `src/main/java/org/example/httpparser/HttpRequest.java` lines 27-28), it always returns a non-null map (either `Map.copyOf(headers)` or `Collections.emptyMap()`). The null check on line 68 can be simplified.


<details>
<summary>♻️ Suggested simplification</summary>

```diff
         Map<String, String> headers = request.getHeaders();
-        if (headers == null || headers.isEmpty()) {
+        if (headers.isEmpty()) {
             return DEFAULT_LOCALE;
         }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/LocaleFilter.java` around lines 67 - 70, In
LocaleFilter (method using request.getHeaders()), remove the redundant null
check because HttpRequest.getHeaders() always returns a non-null Map; replace
the conditional that currently checks "headers == null || headers.isEmpty()"
with a single "headers.isEmpty()" check (or directly rely on headers.isEmpty()
to return DEFAULT_LOCALE) to simplify the logic while keeping the existing
DEFAULT_LOCALE fallback.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/filter/CompressionFilterTest.java (1)</summary><blockquote>

`113-131`: **Reflection-based body extraction is fragile but acceptable.**

Using reflection to access private fields (`bytebody`, `body`) is brittle and will break if field names change. Consider using `HttpResponseBuilder.getBodyBytes()` or `getByteBody()` public methods instead.


<details>
<summary>♻️ Use public API instead of reflection</summary>

```diff
     private byte[] getBodyFromResponse(HttpResponseBuilder response) {
-        try {
-            var field = response.getClass().getDeclaredField("bytebody");
-            field.setAccessible(true);
-            byte[] bytebody = (byte[]) field.get(response);
-
-            if (bytebody != null) {
-                return bytebody;
-            }
-
-            var bodyField = response.getClass().getDeclaredField("body");
-            bodyField.setAccessible(true);
-            String body = (String) bodyField.get(response);
-            return body.getBytes(StandardCharsets.UTF_8);
-
-        } catch (Exception e) {
-            throw new RuntimeException("Failed to get body", e);
-        }
+        byte[] byteBody = response.getByteBody();
+        if (byteBody != null) {
+            return byteBody;
+        }
+        return response.getBody().getBytes(StandardCharsets.UTF_8);
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/filter/CompressionFilterTest.java` around lines 113
- 131, The test's getBodyFromResponse uses fragile reflection to read private
fields "bytebody" and "body"; replace this with public API calls on
HttpResponseBuilder (e.g., getBodyBytes(), getByteBody(), or getBody()) instead:
call the available public method that returns the byte[] first, return it if
non-null, otherwise call the String-returning getter and convert to UTF-8 bytes,
and keep the same exception behavior; update the method to reference the
HttpResponseBuilder getters instead of response.getClass().getDeclaredField(...)
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/filter/Filter.java (1)</summary><blockquote>

`7-14`: **Consider adding default implementations for lifecycle methods.**

Most filter implementations in this PR have empty `init()` and `destroy()` methods. Adding default no-op implementations would reduce boilerplate.


<details>
<summary>♻️ Suggested refactor</summary>

```diff
 public interface Filter {
-    void init();
+    default void init() {}

     void doFilter(HttpRequest request, HttpResponseBuilder response, FilterChain chain);

-    void destroy();
+    default void destroy() {}
 }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/Filter.java` around lines 7 - 14, Add no-op
default implementations for the lifecycle methods in the Filter interface:
change init() and destroy() to default methods that do nothing while keeping
doFilter(HttpRequest, HttpResponseBuilder, FilterChain) abstract; this reduces
boilerplate in existing Filter implementations (classes implementing Filter can
keep or remove their empty overrides). Ensure you update the interface method
signatures for init and destroy to use the default keyword (Java 8+), leaving
doFilter and the types HttpRequest, HttpResponseBuilder, FilterChain unchanged.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/filter/FilterChainImpl.java (1)</summary><blockquote>

`21-25`: **Consider defensive copy of the filters list.**

The constructor stores the `filters` list by reference. If the caller modifies the list after construction, it could cause unexpected behavior. A defensive copy would be safer.


<details>
<summary>♻️ Suggested defensive copy</summary>

```diff
     public FilterChainImpl(List<Filter> filters,
                            BiConsumer<HttpRequest, HttpResponseBuilder> terminalHandler) {
-        this.filters = filters;
+        this.filters = List.copyOf(filters);
         this.terminalHandler = terminalHandler;
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/FilterChainImpl.java` around lines 21 - 25,
The constructor FilterChainImpl(List<Filter> filters, BiConsumer<HttpRequest,
HttpResponseBuilder> terminalHandler) stores the incoming filters list by
reference which allows external mutation; make a defensive copy when assigning
to the filters field (e.g., create a new ArrayList from the provided list or
Collections.unmodifiableList copy) so subsequent external changes don't affect
FilterChainImpl, and preserve/null-check behavior as needed for the
terminalHandler and filters parameters.
```

</details>

</blockquote></details>
<details>
<summary>src/test/java/org/example/filter/LoggingFilterTest.java (1)</summary><blockquote>

`28-28`: **Consider creating a fresh filter instance per test.**

The `filter` field is instantiated once at class level. While `LoggingFilter` appears stateless, it's a best practice in unit tests to create fresh instances in `@BeforeEach` to prevent accidental state sharing between tests.


<details>
<summary>♻️ Suggested refactor</summary>

```diff
-    LoggingFilter filter = new LoggingFilter();
+    LoggingFilter filter;
     Logger logger;

     `@BeforeEach`
     void setup(){
+        filter = new LoggingFilter();
         logger = Logger.getLogger(LoggingFilter.class.getName());
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/test/java/org/example/filter/LoggingFilterTest.java` at line 28, The test
currently reuses a single LoggingFilter instance declared at class level; change
the setup to create a fresh instance before each test by removing the
class-level instantiation and initializing a new LoggingFilter in a `@BeforeEach`
method (referencing the LoggingFilter class and the LoggingFilterTest test
class) so each test gets its own filter instance to avoid accidental shared
state.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/TcpServer.java (1)</summary><blockquote>

`37-43`: **Exception handling could be simplified.**

The `handleClient` method wraps any exception from `try(client)` in a `RuntimeException`, but this complicates debugging. Since `processRequest` already handles errors internally, the re-thrown exception here would typically only occur on socket close failure.


<details>
<summary>♻️ Suggested simplification</summary>

```diff
     protected void handleClient(Socket client) {
-        try(client){
+        try (client) {
             processRequest(client);
         } catch (Exception e) {
-            throw new RuntimeException("Failed to close socket", e);
+            System.err.println("Error handling client: " + e.getMessage());
         }
     }
```
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/TcpServer.java` around lines 37 - 43, The catch in
handleClient is unnecessary and hides socket-close failures by wrapping them in
a RuntimeException; remove the catch block so the try-with-resources around the
Socket client simply calls processRequest(client) and lets any close exceptions
surface or be handled elsewhere, or alternatively replace the catch with a
simple log (e.g., logger.warn("Socket close failed", e)) instead of throwing a
RuntimeException; update the handleClient method (referencing method name
handleClient, parameter Socket client, and call to processRequest) accordingly.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/server/ConfigurableFilterPipeline.java (1)</summary><blockquote>

`30-55`: **Consider precomputing ordered registrations once in constructor.**

Global/route splitting and sorting are repeated per request even though `registrations` is immutable. Precomputing stable ordered structures can reduce request-path overhead.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/server/ConfigurableFilterPipeline.java` around
lines 30 - 55, Move the global/route split and sort out of the per-request path
and into the class constructor so we do not repeat work on every request: in
ConfigurableFilterPipeline add final fields (e.g., precomputedGlobalFilters:
List<Filter> and precomputedRouteRegistrations: List<FilterRegistration>),
populate them once in the constructor by iterating over registrations,
separating reg.isGlobal() into precomputedGlobalFilters (store reg.filter()) and
non-global into precomputedRouteRegistrations (store the registrations), sort
both using Comparator.comparingInt(FilterRegistration::order) (or sort filters
by the same order), and then in the request handling code replace the
per-request global/route building with using precomputedGlobalFilters and
iterating precomputedRouteRegistrations and calling
matchesAny(reg.routePatterns(), request.getPath()) to append reg.filter() for
matching route registrations.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/filter/CompressionFilter.java (1)</summary><blockquote>

`99-101`: **Prefer structured logger over `System.err` for compression failures.**

Using a logger here keeps diagnostics consistent with the rest of server-side error handling and operational tooling.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/CompressionFilter.java` around lines 99 -
101, Replace the System.err.println in the CompressionFilter catch block with
the class logger: ensure CompressionFilter has a logger (e.g., private static
final Logger logger = LoggerFactory.getLogger(CompressionFilter.class)) and
change the catch(IOException e) body to logger.error("CompressionFilter: gzip
compression failed", e) so the exception and stack trace are captured by the
structured logging system; if your project uses a different logging facade, use
that logger type and the logger.error(message, throwable) signature accordingly.
```

</details>

</blockquote></details>
<details>
<summary>src/main/java/org/example/filter/LocaleFilterWithCookie.java (1)</summary><blockquote>

`77-81`: **Optional: remove dead null checks for request headers.**

With `src/main/java/org/example/httpparser/HttpRequest.java` (constructor + `getHeaders()`), headers are always non-null (`Collections.emptyMap()` fallback), so these branches are unreachable.


<details>
<summary>💡 Proposed cleanup</summary>

```diff
     private String extractLocaleFromCookie(HttpRequest request) {
         Map<String, String> headers = request.getHeaders();
-        if (headers == null) {
-            return null;
-        }
@@
     private String extractLocaleFromHeader(HttpRequest request) {
         Map<String, String> headers = request.getHeaders();
-        if (headers == null) {
-            return null;
-        }
```
</details>


Also applies to: 115-118

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/filter/LocaleFilterWithCookie.java` around lines 77
- 81, The null-check branches in LocaleFilterWithCookie.extractLocaleFromCookie
are dead because HttpRequest's constructor and getHeaders() always return a
non-null Map (Collections.emptyMap fallback); remove the headers==null guard and
any early returns that assume headers can be null, and simplify the method to
operate directly on request.getHeaders(); update any other occurrences in
LocaleFilterWithCookie (lines referenced around 115-118) that perform the same
redundant null checks. Ensure you keep references to HttpRequest.getHeaders()
and the extractLocaleFromCookie method name when making the change so reviewers
can find the edit.
```

</details>

</blockquote></details>

</blockquote></details>

---

<details>
<summary>ℹ️ Review info</summary>

**Configuration used**: Organization UI

**Review profile**: CHILL

**Plan**: Pro

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 5f18f918876b7d42570e93cc228d7d811caa0591 and 088e9261f008fbb7bfb59af52b012c2d24286057.

</details>

<details>
<summary>⛔ Files ignored due to path filters (12)</summary>

* `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`
* `www/test.jpg` is excluded by `!**/*.jpg`

</details>

<details>
<summary>📒 Files selected for processing (53)</summary>

* `.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/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/LoggingFilter.java`
* `src/main/java/org/example/filter/RequestTimeOutFilter.java`
* `src/main/java/org/example/http/HttpResponseBuilder.java`
* `src/main/java/org/example/http/MimeTypeDetector.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/server/ConfigurableFilterPipeline.java`
* `src/main/java/org/example/server/FilterRegistration.java`
* `src/main/java/org/example/server/RoutePattern.java`
* `src/main/resources/application.yml`
* `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/filter/CompressionFilterTest.java`
* `src/test/java/org/example/filter/IpFilterTest.java`
* `src/test/java/org/example/filter/LocaleFilterTest.java`
* `src/test/java/org/example/filter/LocaleFilterWithCookieTest.java`
* `src/test/java/org/example/filter/LoggingFilterTest.java`
* `src/test/java/org/example/filter/RequestTimeOutFilterTest.java`
* `src/test/java/org/example/http/HttpResponseBuilderTest.java`
* `src/test/java/org/example/http/MimeTypeDetectorTest.java`
* `src/test/java/org/example/httpparser/HttpParseRequestLineTest.java`
* `src/test/java/org/example/httpparser/HttpParserTest.java`
* `src/test/java/org/example/server/ConfigurableFilterPipelineTest.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`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@Martin-E-Karlsson
Copy link
Author

Martin-E-Karlsson commented Mar 1, 2026

Det verkar som att jag begick ett misstag i att jag gjorde en merge på förändringarna i main in i 96-create-page-for-testing-filytypes branch innan jag pushade. De enda filerna pull-requesten syftar på är www/file-test.html samt www/test-files/ med alla filer där inuti.

Borde pull requesten göras om eller ändras?

@Martin-E-Karlsson
Copy link
Author

Pull requested verkar vara gjord i ett separat repository. Nu stängd.

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.