diff --git a/README.md b/README.md index 04cc3b80..86871819 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ docker pull ghcr.io/ithsjava25/project-webserver-juv25d:latest ## Step 3 – Run the container ```bash -docker run -p 3000:3000 ghcr.io/ithsjava25/project-webserver-juv25d:latest +docker run -p 8080:8080 ghcr.io/ithsjava25/project-webserver-juv25d:latest ``` --- @@ -139,10 +139,10 @@ docker run -p 3000:3000 ghcr.io/ithsjava25/project-webserver-juv25d:latest ## Step 4 – Open in browser ``` -http://localhost:3000 +http://localhost:8080 ``` -The server runs on port **3000**. +The server runs on port **8080**. --- @@ -160,10 +160,10 @@ org.juv25d.App 3. Open: ``` -http://localhost:3000 +http://localhost:8080 ``` -Note: Running the packaged JAR directly with `java -jar` is not supported, as the project is not built as a fat JAR (external dependencies such as SnakeYAML are not bundled). +Note: The project is packaged as a fat JAR using the Maven Shade Plugin, so you can run it with `java -jar target/app.jar`. --- @@ -327,7 +327,7 @@ Example: ```yaml server: - port: 3000 + port: 8080 root-dir: static logging: diff --git a/pom.xml b/pom.xml index f88056db..b87ce149 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.juv25d JavaHttpServer - 1.0.0-beta + 1.0-SNAPSHOT 25 @@ -58,19 +58,6 @@ maven-install-plugin 3.1.4 - - org.apache.maven.plugins - maven-jar-plugin - 3.5.0 - - - - true - org.juv25d.App - - - - org.apache.maven.plugins maven-resources-plugin @@ -171,6 +158,29 @@ + + org.apache.maven.plugins + maven-shade-plugin + 3.5.1 + + + package + + shade + + + app + false + false + + + org.juv25d.App + + + + + + diff --git a/src/main/java/org/juv25d/plugin/StaticFilesPlugin.java b/src/main/java/org/juv25d/plugin/StaticFilesPlugin.java index 2fe3de15..00ae8012 100644 --- a/src/main/java/org/juv25d/plugin/StaticFilesPlugin.java +++ b/src/main/java/org/juv25d/plugin/StaticFilesPlugin.java @@ -29,3 +29,4 @@ public void handle(HttpRequest request, HttpResponse response) throws IOExceptio response.setBody(staticResponse.body()); } } + diff --git a/src/main/resources/static/README.md b/src/main/resources/static/README.md index 2e895331..42b35656 100644 --- a/src/main/resources/static/README.md +++ b/src/main/resources/static/README.md @@ -1,47 +1,406 @@ -# PLACEHOLDER +# 🚀 Java HTTP Server – Team juv25d -# Creating a New Filter +A lightweight, modular HTTP server built from scratch in Java. -Filters allow you to intercept and modify HTTP requests *before* they reach the plugin, and modify responses *before* they are sent back to the client. -They are executed in sequence through a `FilterChain`. +This project demonstrates how web servers and backend frameworks work internally — without using Spring, Tomcat, or other high-level frameworks. + +The server is distributed as a Docker image via GitHub Container Registry (GHCR). + +--- + +# 📌 Project Purpose + +The goal of this project is to deeply understand: + +- How HTTP works +- How requests are parsed +- How responses are constructed +- How middleware (filters) operate +- How backend frameworks structure request lifecycles +- How static file serving works +- How architectural decisions are documented (ADR) +- How Java services are containerized with Docker + +This is an educational backend architecture project. + +--- + +# ⚙ Requirements + +- Java 21+ (uses Virtual Threads via Project Loom) +- Docker (for running the official container image) + +--- + +# 🏗 Architecture Overview + +## Request Lifecycle + +``` +Client + ↓ +ServerSocket + ↓ +ConnectionHandler (Virtual Thread) + ↓ +Pipeline + ↓ +FilterChain + ↓ +Plugin + ↓ +HttpResponseWriter + ↓ +Client +``` + +--- + +## 🧩 Core Components + +### Server +- Listens on a configurable port +- Accepts incoming socket connections +- Spawns a virtual thread per request (`Thread.ofVirtual()`) + +### ConnectionHandler +- Parses the HTTP request using `HttpParser` +- Creates a default `HttpResponse` +- Executes the `Pipeline` + +### Pipeline +- Holds global filters +- Holds route-specific filters +- Creates and executes a `FilterChain` +- Executes the active plugin + +### Filters +Used for cross-cutting concerns such as: +- Logging +- Authentication +- Rate limiting +- Validation +- Compression +- Security headers + +### Plugin +Responsible for generating the final HTTP response. + +### HttpParser +Custom HTTP request parser that: +- Parses request line +- Parses headers +- Handles `Content-Length` +- Extracts path and query parameters + +### HttpResponseWriter +Responsible for: +- Writing status line +- Writing headers +- Automatically setting `Content-Length` +- Writing response body + +--- + +# 🐳 Running the Server (Official Method) + +The official way to run the server is via Docker using GitHub Container Registry. + +Docker must be installed and running. + +--- + +## Step 1 – Login to GHCR + +```bash +docker login ghcr.io -u +``` + +Use your GitHub Personal Access Token (classic) as password. + +--- + +## Step 2 – Pull the latest image + +```bash +docker pull ghcr.io/ithsjava25/project-webserver-juv25d:latest +``` + +--- + +## Step 3 – Run the container + +```bash +docker run -p 3000:3000 ghcr.io/ithsjava25/project-webserver-juv25d:latest +``` + +--- + +## Step 4 – Open in browser + +``` +http://localhost:3000 +``` + +The server runs on port **3000**. + +--- + +# 🛠 Running in Development (IDE) + +For development purposes, you can run the server directly from your IDE: + +1. Open the project. +2. Run the class: + +``` +org.juv25d.App +``` + +3. Open: + +``` +http://localhost:3000 +``` + +Note: The project is packaged as a fat JAR using the Maven Shade Plugin, so you can run it with `java -jar target/JavaHttpServer-1.0.2-beta.jar`. + +--- + +# 🌐 Static File Serving + +The `StaticFilesPlugin` serves files from: + +``` +src/main/resources/static/ +``` + +### Example Mapping + +| File | URL | +|------|------| +| index.html | `/` | +| css/styles.css | `/css/styles.css` | +| js/app.js | `/js/app.js` | + +### Security Features + +- Path traversal prevention +- MIME type detection +- 404 handling +- 403 handling +- Clean URLs (no `/static/` prefix) + +For full architectural reasoning, see: + +➡ `docs/adr/ADR-001-static-file-serving-architecture.md` + +--- + +# 🔄 Creating a Filter + +Filters intercept requests before they reach the plugin. A filter can: -- Inspect or modify the incoming `HttpRequest` -- Inspect or modify the outgoing `HttpResponse` -- Stop the chain (e.g., return a 403 or 429) -- Allow the chain to continue by calling `chain.doFilter(req, res)` + +- Inspect or modify `HttpRequest` +- Inspect or modify `HttpResponse` +- Stop the chain (e.g., return 403) +- Continue processing by calling `chain.doFilter(req, res)` --- ## Filter Interface -All filters must implement: - ```java public interface Filter { void doFilter(HttpRequest req, HttpResponse res, FilterChain chain) throws IOException; } ``` -Example: LoggingFilter -## Creating a filter +--- + +## Example: LoggingFilter + +```java +public class LoggingFilter implements Filter { + @Override + public void doFilter(HttpRequest req, HttpResponse res, FilterChain chain) throws IOException { + System.out.println(req.method() + " " + req.path()); + chain.doFilter(req, res); + } +} +``` + +--- + +## Registering a Global Filter + +```java +pipeline.addGlobalFilter(new LoggingFilter(), 100); +``` + +Lower order values execute first. + +--- + +# 🎯 Route-Specific Filters + +Route filters only execute when the request path matches a pattern. + +### Supported Patterns + +- `/api/*` → matches paths starting with `/api/` +- `/login` → exact match +- `/admin/*` → wildcard support (prefix-based) + +--- + +## Example + +```java +pipeline.addRouteFilter(new JwtAuthFilter(), 100, "/api/*"); +``` + +--- + +## Execution Flow + +``` +Client → Filter 1 → Filter 2 → ... → Plugin → Response → Client +``` + +--- + +# 🧠 Creating a Plugin + +Plugins generate the final HTTP response. + +They run after all filters have completed. + +--- + +## Plugin Interface + +```java +public interface Plugin { + void handle(HttpRequest req, HttpResponse res) throws IOException; +} +``` + +--- + +## Example: HelloPlugin + +```java +public class HelloPlugin implements Plugin { + + @Override + public void handle(HttpRequest req, HttpResponse res) throws IOException { + res.setStatusCode(200); + res.setStatusText("OK"); + res.setHeader("Content-Type", "text/plain"); + res.setBody("Hello from juv25d server".getBytes()); + } +} +``` + +--- + +## Registering a Plugin + +```java +pipeline.setPlugin(new HelloPlugin()); +``` + +--- + +# ⚙ Configuration + +Configuration is loaded from: + +``` +application-properties.yml +``` + +Example: + +```yaml +server: + port: 3000 + root-dir: static + +logging: + level: INFO +``` + +--- + +# 📦 Features + +- Custom HTTP request parser (`HttpParser`) +- Custom HTTP response writer (`HttpResponseWriter`) +- Mutable HTTP response model +- Filter chain architecture +- Plugin system +- Static file serving +- MIME type resolution +- Path traversal protection +- Virtual threads (Project Loom) +- YAML configuration (SnakeYAML) +- Dockerized distribution +- Published container image (GHCR) + +--- + +# 📚 Documentation & Architecture Decisions + +Additional technical documentation is available in the `docs/` directory. + +## Architecture Decision Records (ADR) + +Contains architectural decisions and their reasoning. + +``` +docs/adr/ +``` + +Main index: + +``` +docs/adr/README.md +``` -- Create a new class in src/main/java/.../filters/ -- Implement the Filter interface -- Add your logic inside doFilter -- Decide whether to continue the chain or stop it +Includes: -## Register your filter (src/org.example/App.java) +- Static file serving architecture +- ADR template +- Future architecture decisions + +--- + +## Technical Notes + +Advanced filter configuration examples: -Register your filter using: ``` - Pipeline pipeline = new Pipeline(); - pipeline.addFilter(new LoggingFilter()); +docs/notes/ ``` -## Filter execution flow +--- + +# 🎓 Educational Value + +This project demonstrates: + +- How web servers work internally +- How middleware pipelines are implemented +- How static file serving works +- How architectural decisions are documented +- How Java services are containerized and distributed + +--- + +# 👥 Team juv25d -Client → - Filter 1 → Filter 2 → ... → Filter N → - Plugin → Response → - back through filters → Client +Built as a learning project to deeply understand HTTP, backend systems, and modular server architecture.