Skip to content

Alex-SA1/Static-Web-Server

Repository files navigation

Static Web Server

This project is built from the wish to deepen my knowledge in networking and software development and to understand better how the web works under the hood.

Description

The project is a minimalist implementation of a static web server which works on protocol HTTP/1.1 and has the ability to serve multiple clients.

Features

  • Concurrency (fixed size thread pool with explicit flow control)

Acceptor thread - accepts connections, assigns a connection handler to each of them, submits the tasks to the executor

Bounded Thread Pool - keeps a fixed number of worker threads to prevent out-of-memory problems

Bounded Work Queue - buffers bursts of traffic

Rejection Policy (Caller Runs) - when the server is overloaded, the acceptor processes requests to provide an automatic backpressure (naturally slow down new connections)

Normal Load -> queue is mostly empty, the workers are processing requests at an acceptable rate

High Load -> queue is starting to fill, workers are busy accepting requests one after one at a high rate

Overload -> queue is full, all workers are busy and the policy kicks in telling the acceptor thread to start handle requests instead of accepting them so that the accept rate decreases and the system starts to stabilize

  • Static Files Serving

  • Accepted request methods

  1. GET

The client can make GET requests for static files stored on the server (.html, .css, .js, .jpg, .jpeg, .png, .gif, .svg)

  1. POST

Being a static server, it does not have capabilities of rendering dynamic pages but it offers an echo endpoint for testing the POST requests. The client can send POST requests to the echo endpoint (/api/echo) and it will respond with the data from the request body to prove that the server parses, processes and stores even the POST requests.

Content-Type: application/json, application/x-www-form-urlencoded, text/plain

  • Request Size Limits
  1. Request Line up to 8kb
  2. Request Header Field Value up to 8kb
  3. Request Header Field Name up to 256b
  4. Number of Request Headers up to 100
  • Routing

The server offers the capability of creating custom routes for resources.

e.g. the client can access the resource example.html by sending a GET request with the path /example.html or can register the resource under the name /something/example and request it by sending a GET request with the path /something/example

  • Proper Status Code for Responses (e.g. 200 OK, 400 Bad Request, 404 Not Found)

  • Keep-Alive Connections (reusing a connection for multiple requests, if header Connection: close is not specified)

  • Security Measures

  1. Block Path Traversal (when the client makes a request to a path that contains path traversal sequences like ../ it will get a response with a 404 Not Found)
  2. Correct MIME types for served files
  3. Security Headers (X-Content-Type-Options, Referrer-Policy, X-Frame-Options, Content-Security-Policy)
  4. Restricted file serving (the allowed files to be served are the ones with the registered extensions)
  5. Minimum protection measures for DoS (Denial of Service) - request size limits specified above, limit the number of requests per connection, socket timeout so that a slow client does not keep a worker busy for too long, use of a safe library for Regex operations to mitigate ReDoS

Technical Details & Build Explanation

Programming language - Java v25.0.1

SDK - openjdk-25

Build System - Gradle Kotlin DSL

Gradle JVM - openjdk-25

External Libraries

Docker Build

Multi-stage build (Build Stage + Runtime Stage)

  • Build Stage

Image: gradle:9.2-jdk 25

The role of this stage is to copy all necessary gradle files from the project folder into the image workdir, install project dependencies specified in the gradle build script and run a custom gradle task which creates a Fat JAR of the project (JAR file that contains the Java program and all dependencies).

  • Runtime Stage

Image: eclipse-temurin:25-jdk

The role of this stage is to copy the JAR from the Build Stage and run it.

The project contains a .env file where should be set two variables:

  1. PORT (the port number on which the server will start running)
  2. DOCUMENT_ROOT (the path to the folder from where the static files will be served)

The folder /static from the project root folder will be mount on the Docker container at the location specified in DOCUMENT_ROOT so that all files uploaded in the /static folder to be accessible from the container.

The project can be also run without Docker (by installing the dependencies and running the Main class) because all configurations made for Docker container compatibility have default values relative to the local system.

Run

  • Clone the project repository

  • Create a .env file with the same format as .env.example and set the variables e.g.

    PORT=80 DOCUMENT_ROOT=/var/www/html

  • Run the following command

docker compose up --build
  • If you want to upload files on the server -> before running the project, place your files inside of the /static folder according to the rules from that folder (you will find README files inside of every subfolder with the rules)
  • If you want to register routes for your files -> before running the project, open the Main.java file (/src/main/java/org/example/Main.java) and register your routes after line 21 and before the method from line 23 as in the given example from line 21 with the following method
server.getRouter().registerRoute("/example", "/example.html");

About

Minimalist HTTP/1.1 web server implementation

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors