A lightweight, HTTP/1.1-compliant web server implementation in Java built from scratch using non-blocking I/O. This project demonstrates understanding of network protocols, socket programming, and event-driven architecture without relying on established frameworks.
This is a custom HTTP server designed to be:
- Crash-proof: Robust error handling ensures the server never crashes
- High-performance: Event-driven, non-blocking I/O using Java NIO for optimal resource usage
- Single-threaded: Efficient handling of multiple connections in a single thread
- HTTP/1.1 compliant: Proper handling of chunked/unchunked requests and responses
- Production-ready: Tested with siege achieving 100% availability under stress
- Multi-Server Support: Run multiple virtual servers on different ports with host-based routing
- Static File Serving: Serve static files with automatic MIME type detection
- CGI Support: Execute Python and Node.js scripts via CGI
- Flexible Routing: YAML-based route configuration with pattern matching
- HTTP Methods: Support for GET, POST, DELETE methods
- Session Management: Built-in session handling with cookies
- Custom Error Pages: Configure custom error pages per server (400, 403, 404, 405, 413, 500)
- HTTP Redirects: Support for 301/302 redirects
- Directory Listing: Optional directory browsing
- Request Size Limits: Configurable client body size limits
- Virtual Hosts: Host-based request routing
- File Uploads: Handle file uploads with body size validation
- Chunked Encoding: Support for both chunked and unchunked transfer encoding
- Request Timeouts: Automatic timeout for long-running requests
- Java 17 or higher
- Maven 3.6+ (optional, falls back to javac if not available)
- Python 3 (optional, for CGI scripts)
- Node.js (optional, for CGI scripts)
- Clone the repository:
git clone <repository-url>
cd JAVA-LOCALSERVER- Run the server:
./run.shThe server will compile and start automatically, listening on the ports configured in application.yml.
The server is configured via src/main/resources/application.yml. The configuration file uses YAML format to define servers, routes, and behavior.
servers:
- name: server1
hosts: [localhost]
ports: [8080, 8443]
default: true
clientMaxBodySize: 5MB
sessionId: session-id # Optional
errorPages:
403: /errors/403.html
404: /errors/404.html
500: /errors/500.html
routes:
- name: static-files
path: /files
methods: [GET]
root: /files
indexFile: index.html
directoryListing: true| Option | Type | Description |
|---|---|---|
name |
String | Server identifier |
hosts |
Array | Virtual host names (e.g., localhost, example.com) |
ports |
Array | Port numbers to listen on |
default |
Boolean | Whether this is the default server for its ports |
clientMaxBodySize |
String | Maximum request body size (e.g., 5MB, 10GB) |
sessionId |
String | Optional session ID validation |
errorPages |
Map | Custom error pages mapping status codes to paths |
routes |
Array | Route configurations |
| Option | Type | Description |
|---|---|---|
name |
String | Route identifier |
path |
String | URL path pattern |
methods |
Array | Allowed HTTP methods |
root |
String | Root directory for static files (relative to resources) |
indexFile |
String | Default file to serve for directories |
directoryListing |
Boolean | Enable/disable directory browsing |
redirect |
Object | Redirect configuration (status, to) |
cgi |
Object | CGI interpreter mapping (extension: path) |
- name: static-files
path: /files
methods: [GET]
root: /files
indexFile: index.html
directoryListing: true- name: python-scripts
path: /cgi-bin
methods: [GET, POST]
root: /scripts
cgi:
py: /usr/bin/python3
js: /usr/bin/node- name: old-path
path: /old-files
methods: [GET]
redirect:
status: 301
to: /new-filesWith the default configuration:
# Access files in /images directory
curl http://localhost:8080/images/photo.jpg
# Access files with directory listing
curl http://localhost:8080/files/Place your scripts in src/main/resources/scripts/:
# Execute Python CGI script
curl http://localhost:8081/python/script.py
# Execute Node.js CGI script
curl http://localhost:8081/nodejs/app.js
# POST request to CGI
curl -X POST -d "data=value" http://localhost:8081/cgi-bin/process.py# Server 1 (default server)
curl http://localhost:8080/gallery/
# Server 2 with specific host
curl -H "Host: test2.com" http://localhost:8081/python/
# Server 3 with redirect
curl -L http://localhost:8888/JAVA-LOCALSERVER/
src/main/java/com/http/
Main.java # Entry point
application/
Application.java # Main application class
configuration/ # Configuration parsing
connectors/ # Network I/O handling
router/ # Request routing
session/ # Session management
http/
request/ # HTTP request parsing
response/ # HTTP response generation
enums/ # HTTP enums (methods, status)
cookie/ # Cookie handling
url/ # URL parsing
utils/ # Utilities (MIME types, etc.)
src/main/resources/
application.yml # Server configuration
- Server Initialization: The
Applicationclass parsesapplication.ymland initializes connectors and routers - Connection Handling: The
Connectoruses Java NIO for non-blocking I/O - Request Processing: Incoming requests are parsed and routed based on host/port/path
- Route Matching: The
Routermatches requests to configured routes - Response Generation: Responses are generated based on route type (static, CGI, redirect)
- Session Management: Sessions are tracked via cookies
- Error Handling: Custom error pages are served based on configuration
The server uses a single thread with Java NIO's Selector to handle multiple connections concurrently:
- Non-blocking I/O: All socket operations are non-blocking, preventing any single connection from blocking others
- Event-driven: The selector monitors multiple channels for readiness (accept, read, write)
- Efficient: Handles thousands of concurrent connections with minimal resource overhead
- Scalable: No thread-per-connection overhead
Client Connection → Read Handler → Header Parser → Chunk Parser →
Request Parser → Router → Response Generation → Write Handler
- Connector (
src/main/java/com/http/application/connectors/Connector.java:24): Main event loop usingSelector - HeaderParser: Parses HTTP headers from incoming byte streams
- ChunkParser: Handles chunked transfer encoding
- RequestParser: Constructs
HttpRequestobjects - Router (
src/main/java/com/http/application/router/Router.java:29): Routes requests to appropriate handlers - CgiHandler: Executes CGI scripts using
ProcessBuilder - StaticResourceResolver: Serves static files with proper MIME types
The server has been tested using siege benchmark tool and achieves excellent performance:
Test 1: 50 concurrent users, 100 repetitions each (5000 transactions)
Transactions: 10000 hits
Availability: 100.00 %
Elapsed time: 6.34 secs
Response time: 31.48 ms
Transaction rate: 1577.29 trans/sec
Throughput: 0.66 MB/sec
Concurrency: 49.65
Failed transactions: 0
Test 2: 100 concurrent users, 200 repetitions each (20000 transactions)
Transactions: 20000 hits
Availability: 100.00 %
Elapsed time: 13.79 secs
Response time: 51.87 ms
Transaction rate: 1450.33 trans/sec
Throughput: 1.22 MB/sec
Concurrency: 75.23
Failed transactions: 0
- Availability: 100% (exceeds 99.5% target)
- Zero failures: No crashed connections or errors
- High throughput: 1400+ requests/second
- Low latency: Average response time under 52ms
- Handles high concurrency: 100+ concurrent users without degradation
# Basic benchmark test
siege -b -c 50 -r 100 http://localhost:8080/
# High concurrency stress test
siege -b -c 100 -r 200 http://localhost:8080/
# Timed test (30 seconds)
siege -b -t30s -c 50 http://localhost:8080/This server implements key HTTP/1.1 features:
- Chunked Transfer Encoding: Both sending and receiving
- Persistent Connections: Keep-alive support
- Virtual Hosting: Host header-based routing
- Method Support: GET, POST, DELETE
- Status Codes: Proper HTTP status codes (200, 301, 302, 400, 403, 404, 405, 413, 500)
- Content Negotiation: MIME type detection and Content-Type headers
- Request/Response Headers: Full header parsing and generation
- Error Handling: Graceful error responses with custom error pages
Sessions are automatically managed via cookies. Configure per-server:
sessionId: my-session-idPrevent large uploads by setting limits:
clientMaxBodySize: 5MB # Supports KB, MB, GBMultiple servers can share ports with host-based routing:
servers:
- name: site1
hosts: [example.com]
ports: [80]
- name: site2
hosts: [test.com]
ports: [80]CGI scripts receive standard environment variables:
REQUEST_METHODQUERY_STRINGCONTENT_TYPECONTENT_LENGTHPATH_INFOSERVER_PROTOCOL
Change the port in application.yml or stop the conflicting service.
- Verify interpreter path in configuration
- Check script permissions
- Ensure scripts are in the configured root directory
- Check that the route path matches the request URL
- Verify the root directory exists
- Ensure HTTP method is allowed