|
1 | | -# 🚀 Create Your First Java Program |
| 1 | +# ⚡ HTTP Web Server ⚡ |
| 2 | +### **Class JUV25G** | Lightweight • Configurable • Secure |
2 | 3 |
|
3 | | -Java has evolved to become more beginner-friendly. This guide walks you through creating a simple program that prints “Hello World,” using both the classic syntax and the new streamlined approach introduced in Java 21. |
| 4 | +<div align="center"> |
| 5 | + |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +*A modern, high-performance HTTP web server built from scratch in Java* |
| 11 | + |
| 12 | +[Features](#features) • [Quick Start](#quick-start) • [Configuration](#configuration) |
4 | 13 |
|
5 | 14 | --- |
6 | 15 |
|
7 | | -## ✨ Classic Java Approach |
| 16 | +</div> |
8 | 17 |
|
9 | | -Traditionally, Java requires a class with a `main` method as the entry point: |
| 18 | +## ✨ Features |
10 | 19 |
|
11 | | -```java |
12 | | -public class Main { |
13 | | - public static void main(String[] args) { |
14 | | - System.out.println("Hello World"); |
15 | | - } |
16 | | -} |
| 20 | +- 🚀 **High Performance** - Virtual threads for handling thousands of concurrent connections |
| 21 | +- 📁 **Static File Serving** - HTML, CSS, JavaScript, images, PDFs, fonts, and more |
| 22 | +- 🎨 **Smart MIME Detection** - Automatic Content-Type headers for 20+ file types |
| 23 | +- ⚙️ **Flexible Configuration** - YAML or JSON config files with sensible defaults |
| 24 | +- 🔒 **Security First** - Path traversal protection and input validation |
| 25 | +- 🐳 **Docker Ready** - Multi-stage Dockerfile for easy deployment |
| 26 | +- ⚡ **HTTP/1.1 Compliant** - Proper status codes, headers, and responses |
| 27 | +- 🎯 **Custom Error Pages** - Branded 404 pages and error handling |
| 28 | + |
| 29 | +## 📋 Requirements |
| 30 | + |
| 31 | +| Tool | Version | Purpose | |
| 32 | +|------|---------|---------| |
| 33 | +| ☕ **Java** | 21+ | Runtime environment | |
| 34 | +| 📦 **Maven** | 3.6+ | Build tool | |
| 35 | +| 🐳 **Docker** | Latest | Optional - for containerization | |
| 36 | + |
| 37 | +## Quick Start |
| 38 | + |
| 39 | +``` |
| 40 | +┌─────────────────────────────────────────────┐ |
| 41 | +│ Ready to launch your web server? │ |
| 42 | +│ Follow these simple steps! │ |
| 43 | +└─────────────────────────────────────────────┘ |
17 | 44 | ``` |
18 | 45 |
|
19 | | -This works across all Java versions and forms the foundation of most Java programs. |
| 46 | +### 1. Clone the repository |
| 47 | +```bash |
| 48 | +git clone git clone https://github.com/ithsjava25/project-webserver-juv25g.git |
| 49 | +cd project-webserver |
| 50 | +``` |
20 | 51 |
|
21 | | ---- |
| 52 | +### 2. Build the project |
| 53 | +```bash |
| 54 | +mvn clean compile |
| 55 | +``` |
| 56 | + |
| 57 | +### 3. Run the server |
| 58 | + |
| 59 | +**Option A: Run directly with Maven (recommended for development)** |
| 60 | +```bash |
| 61 | +mvn exec:java@run |
| 62 | +``` |
| 63 | + |
| 64 | +**Option B: Run compiled classes directly** |
| 65 | +```bash |
| 66 | +mvn clean compile |
| 67 | +java -cp target/classes org.example.App |
| 68 | +``` |
| 69 | + |
| 70 | +**Option C: Using Docker** |
| 71 | +```bash |
| 72 | +docker build -t webserver . |
| 73 | +docker run -p 8080:8080 -v $(pwd)/www:/www webserver |
| 74 | +``` |
| 75 | + |
| 76 | +The server will start on the default port **8080** and serve files from the `www/` directory. |
| 77 | + |
| 78 | +### 4. Access in browser |
| 79 | +Open your browser and navigate to: |
| 80 | +``` |
| 81 | +http://localhost:8080 |
| 82 | +``` |
22 | 83 |
|
23 | | -## 🆕 Java 25: Unnamed Class with Instance Main Method |
| 84 | +## Configuration |
24 | 85 |
|
25 | | -In newer versions like **Java 25**, you can use **Unnamed Classes** and an **Instance Main Method**, which allows for a much cleaner syntax: |
| 86 | +The server can be configured using a YAML or JSON configuration file located at: |
| 87 | +``` |
| 88 | +src/main/resources/application.yml |
| 89 | +``` |
| 90 | + |
| 91 | +### Configuration File Format (YAML) |
| 92 | + |
| 93 | +```yaml |
| 94 | +server: |
| 95 | + port: 8080 |
| 96 | + rootDir: "./www" |
| 97 | + |
| 98 | +logging: |
| 99 | + level: "INFO" |
| 100 | +``` |
| 101 | +
|
| 102 | +### Configuration File Format (JSON) |
26 | 103 |
|
27 | | -```java |
28 | | -void main() { |
29 | | - System.out.println("Hello World"); |
| 104 | +```json |
| 105 | +{ |
| 106 | + "server": { |
| 107 | + "port": 8080, |
| 108 | + "rootDir": "./www" |
| 109 | + }, |
| 110 | + "logging": { |
| 111 | + "level": "INFO" |
| 112 | + } |
30 | 113 | } |
31 | 114 | ``` |
32 | 115 |
|
33 | | -### 💡 Why is this cool? |
| 116 | +### Configuration Options |
| 117 | + |
| 118 | +| Property | Type | Default | Description | |
| 119 | +|----------|------|---------|-------------| |
| 120 | +| `server.port` | Integer | `8080` | Port number the server listens on (1-65535) | |
| 121 | +| `server.rootDir` | String | `"./www"` | Root directory for serving static files | |
| 122 | +| `logging.level` | String | `"INFO"` | Logging level (INFO, DEBUG, WARN, ERROR) | |
| 123 | + |
| 124 | +### Default Values |
| 125 | + |
| 126 | +If no configuration file is provided or values are missing, the following defaults are used: |
| 127 | + |
| 128 | +- **Port:** 8080 |
| 129 | +- **Root Directory:** ./www |
| 130 | +- **Logging Level:** INFO |
| 131 | + |
| 132 | +## Directory Structure |
| 133 | + |
| 134 | +``` |
| 135 | +project-webserver/ |
| 136 | +├── src/ |
| 137 | +│ ├── main/ |
| 138 | +│ │ ├── java/ |
| 139 | +│ │ │ └── org/example/ |
| 140 | +│ │ │ ├── App.java # Main entry point |
| 141 | +│ │ │ ├── TcpServer.java # TCP server implementation |
| 142 | +│ │ │ ├── ConnectionHandler.java # HTTP request handler |
| 143 | +│ │ │ ├── StaticFileHandler.java # Static file server |
| 144 | +│ │ │ ├── config/ # Configuration classes |
| 145 | +│ │ │ ├── http/ # HTTP response builder & MIME detection |
| 146 | +│ │ │ ├── httpparser/ # HTTP request parser |
| 147 | +│ │ │ └── filter/ # Filter chain (future feature) |
| 148 | +│ │ └── resources/ |
| 149 | +│ │ └── application.yml # Configuration file |
| 150 | +│ └── test/ # Unit tests |
| 151 | +├── www/ # Web root (static files) |
| 152 | +│ ├── index.html |
| 153 | +│ ├── pageNotFound.html # Custom 404 page |
| 154 | +│ └── ... # Other static files |
| 155 | +├── pom.xml |
| 156 | +└── README.md |
| 157 | +``` |
| 158 | + |
| 159 | +## Serving Static Files |
| 160 | + |
| 161 | +Place your static files in the `www/` directory (or the directory specified in `server.rootDir`). |
| 162 | + |
| 163 | +### Supported File Types |
| 164 | + |
| 165 | +The server automatically detects and serves the correct `Content-Type` for: |
| 166 | + |
| 167 | +**Text & Markup:** |
| 168 | +- HTML (`.html`, `.htm`) |
| 169 | +- CSS (`.css`) |
| 170 | +- JavaScript (`.js`) |
| 171 | +- JSON (`.json`) |
| 172 | +- XML (`.xml`) |
| 173 | +- Plain text (`.txt`) |
| 174 | + |
| 175 | +**Images:** |
| 176 | +- PNG (`.png`) |
| 177 | +- JPEG (`.jpg`, `.jpeg`) |
| 178 | +- GIF (`.gif`) |
| 179 | +- SVG (`.svg`) |
| 180 | +- WebP (`.webp`) |
| 181 | +- ICO (`.ico`) |
| 182 | + |
| 183 | +**Documents:** |
| 184 | +- PDF (`.pdf`) |
| 185 | + |
| 186 | +**Fonts:** |
| 187 | +- WOFF (`.woff`) |
| 188 | +- WOFF2 (`.woff2`) |
| 189 | +- TrueType (`.ttf`) |
| 190 | +- OpenType (`.otf`) |
| 191 | + |
| 192 | +**Media:** |
| 193 | +- MP4 video (`.mp4`) |
| 194 | +- WebM video (`.webm`) |
| 195 | +- MP3 audio (`.mp3`) |
| 196 | +- WAV audio (`.wav`) |
| 197 | + |
| 198 | +Unknown file types are served as `application/octet-stream`. |
| 199 | + |
| 200 | +## URL Handling |
| 201 | + |
| 202 | +The server applies the following URL transformations: |
| 203 | + |
| 204 | +| Request URL | Resolved File | |
| 205 | +|-------------|---------------| |
| 206 | +| `/` | `index.html` | |
| 207 | +| `/about` | `about.html` | |
| 208 | +| `/contact` | `contact.html` | |
| 209 | +| `/styles.css` | `styles.css` | |
| 210 | +| `/page.html` | `page.html` | |
| 211 | + |
| 212 | +**Note:** URLs ending with `/` are resolved to `index.html`, and URLs without an extension get `.html` appended automatically. |
| 213 | + |
| 214 | +## Error Pages |
| 215 | + |
| 216 | +### 404 Not Found |
| 217 | + |
| 218 | +If a requested file doesn't exist, the server returns: |
| 219 | +1. `pageNotFound.html` (if it exists in the web root) |
| 220 | +2. Otherwise: Plain text "404 Not Found" |
| 221 | + |
| 222 | +To customize your 404 page, create `www/pageNotFound.html`. |
| 223 | + |
| 224 | +### 403 Forbidden |
| 225 | + |
| 226 | +Returned when a path traversal attack is detected (e.g., `GET /../../etc/passwd`). |
| 227 | + |
| 228 | +## Security Features |
| 229 | + |
| 230 | +### Path Traversal Protection |
| 231 | + |
| 232 | +The server validates all file paths to prevent directory traversal attacks: |
| 233 | + |
| 234 | +``` |
| 235 | +✅ Allowed: /index.html |
| 236 | +✅ Allowed: /docs/guide.pdf |
| 237 | +❌ Blocked: /../../../etc/passwd |
| 238 | +❌ Blocked: /www/../../../secret.txt |
| 239 | +``` |
| 240 | + |
| 241 | +All blocked requests return `403 Forbidden`. |
34 | 242 |
|
35 | | -- ✅ No need for a `public class` declaration |
36 | | -- ✅ No `static` keyword required |
37 | | -- ✅ Great for quick scripts and learning |
| 243 | +## Running Tests |
38 | 244 |
|
39 | | -To compile and run this, use: |
| 245 | +```bash |
| 246 | +mvn test |
| 247 | +``` |
| 248 | + |
| 249 | +Test coverage includes: |
| 250 | +- HTTP request parsing |
| 251 | +- Response building |
| 252 | +- MIME type detection |
| 253 | +- Configuration loading |
| 254 | +- Static file serving |
| 255 | +- Path traversal security |
| 256 | + |
| 257 | +## Building for Production |
| 258 | + |
| 259 | +### Using Docker (recommended) |
40 | 260 |
|
41 | 261 | ```bash |
42 | | -java --source 25 HelloWorld.java |
| 262 | +docker build -t webserver . |
| 263 | +docker run -d -p 8080:8080 -v $(pwd)/www:/www --name my-webserver webserver |
43 | 264 | ``` |
44 | 265 |
|
45 | | ---- |
| 266 | +### Running on a server |
| 267 | + |
| 268 | +```bash |
| 269 | +# Compile the project |
| 270 | +mvn clean compile |
| 271 | + |
| 272 | +# Run with nohup for background execution |
| 273 | +nohup java -cp target/classes org.example.App > server.log 2>&1 & |
| 274 | + |
| 275 | +# Or use systemd (create /etc/systemd/system/webserver.service) |
| 276 | +``` |
| 277 | + |
| 278 | +## Examples |
| 279 | + |
| 280 | +### Example 1: Serving a Simple Website |
| 281 | + |
| 282 | +**Directory structure:** |
| 283 | +``` |
| 284 | +www/ |
| 285 | +├── index.html |
| 286 | +├── styles.css |
| 287 | +├── app.js |
| 288 | +└── images/ |
| 289 | + └── logo.png |
| 290 | +``` |
| 291 | + |
| 292 | +**Access:** |
| 293 | +- Homepage: `http://localhost:8080/` |
| 294 | +- Stylesheet: `http://localhost:8080/styles.css` |
| 295 | +- Logo: `http://localhost:8080/images/logo.png` |
| 296 | + |
| 297 | +### Example 2: Custom Port |
| 298 | + |
| 299 | +**application.yml:** |
| 300 | +```yaml |
| 301 | +server: |
| 302 | + port: 3000 |
| 303 | + rootDir: "./public" |
| 304 | +``` |
| 305 | +
|
| 306 | +Access at: `http://localhost:3000/` |
| 307 | + |
| 308 | +### Example 3: Different Web Root |
| 309 | + |
| 310 | +**application.yml:** |
| 311 | +```yaml |
| 312 | +server: |
| 313 | + rootDir: "./dist" |
| 314 | +``` |
| 315 | + |
| 316 | +Server will serve files from `dist/` instead of `www/`. |
| 317 | + |
| 318 | +## Architecture |
| 319 | + |
| 320 | +### Request Flow |
| 321 | + |
| 322 | +1. **TcpServer** accepts incoming TCP connections |
| 323 | +2. **ConnectionHandler** creates a virtual thread for each request |
| 324 | +3. **HttpParser** parses the HTTP request line and headers |
| 325 | +4. **StaticFileHandler** resolves the file path and reads the file |
| 326 | +5. **HttpResponseBuilder** constructs the HTTP response with correct headers |
| 327 | +6. Response is written to the client socket |
| 328 | + |
| 329 | +### Filter Chain (Future Feature) |
| 330 | + |
| 331 | +The project includes a filter chain interface for future extensibility: |
| 332 | +- Request/response filtering |
| 333 | +- Authentication |
| 334 | +- Logging |
| 335 | +- Compression |
| 336 | + |
| 337 | +## Troubleshooting |
| 338 | + |
| 339 | +### Port already in use |
| 340 | +``` |
| 341 | +Error: Address already in use |
| 342 | +``` |
| 343 | +**Solution:** Change the port in `application.yml` or kill the process using port 8080: |
| 344 | +```bash |
| 345 | +# Linux/Mac |
| 346 | +lsof -ti:8080 | xargs kill -9 |
| 347 | +
|
| 348 | +# Windows |
| 349 | +netstat -ano | findstr :8080 |
| 350 | +taskkill /PID <PID> /F |
| 351 | +``` |
| 352 | + |
| 353 | +### File not found but file exists |
| 354 | +**Solution:** Check that the file is in the correct directory (`www/` by default) and that the filename matches exactly (case-sensitive on Linux/Mac). |
| 355 | + |
| 356 | +### Binary files (images/PDFs) are corrupted |
| 357 | +**Solution:** This should not happen with the current implementation. The server uses `byte[]` internally to preserve binary data. If you see this issue, please report it as a bug. |
| 358 | + |
| 359 | +## Contributing |
| 360 | + |
| 361 | +1. Fork the repository |
| 362 | +2. Create a feature branch (`git checkout -b feature/new-feature`) |
| 363 | +3. Commit your changes (`git commit -m 'Add new feature'`) |
| 364 | +4. Push to the branch (`git push origin feature/new-feature`) |
| 365 | +5. Open a Pull Request |
| 366 | + |
| 367 | +<div align="center"> |
46 | 368 |
|
47 | | -## 📚 Learn More |
| 369 | +### 👨💻 Built by Class JUV25G |
48 | 370 |
|
49 | | -This feature is part of Java’s ongoing effort to streamline syntax. You can explore deeper in [Baeldung’s guide to Unnamed Classes and Instance Main Methods](https://www.baeldung.com/java-21-unnamed-class-instance-main). |
| 371 | +**Made with ❤️ and ☕ in Sweden** |
| 372 | +</div> |
0 commit comments