Raw-HTTP - HTTP/1.1 Server Built from TCP Sockets
Raw-HTTP - HTTP/1.1 Server Built from TCP Sockets
Raw-HTTP is a lightweight HTTP/1.1 server built entirely from raw TCP sockets in Go. No frameworks, no libraries - just socket programming and HTTP protocol implementation from scratch. Features path parameters, graceful shutdown, buffer pooling, and production-grade performance: 11,000+ RPS.
View on GitHub → | Live Demo →
Built With Raw-HTTP
I built a URL shortener service using this server to prove it works in real applications:
The URL shortener demonstrates path parameters, static file serving, form handling, and production deployment - all running on raw TCP sockets.
Why I Built This
Most developers use frameworks like Express or Flask without understanding what happens underneath. I built this to learn HTTP at the protocol level - parsing requests from TCP sockets, managing connections, implementing TLS encryption, buffer pooling, graceful shutdown, and optimizing performance from first principles.
Core Features
- Raw TCP Handling: Parses HTTP requests directly from socket connections
- Path Parameters: Dynamic routing with
/users/:idsyntax - Query String Parsing: Automatic extraction of URL parameters
- Static File Serving: Proper MIME type detection and delivery
- Keep-Alive Support: HTTP/1.1 connection reuse for 2x performance
- Buffer Pooling: sync.Pool optimization reduces GC pressure
- Form & JSON Parsing: Handles both URL-encoded and JSON bodies
- Graceful Shutdown: Handles SIGINT/SIGTERM with connection draining
- Panic Recovery: Handler crashes don't take down the server
- HTTPS/TLS Support: Optional encrypted connections with certificate-based security
- Response Helpers: Built-in helpers for 201, 204, 400, 401, 403, 405, 429, 500, 502, 503
- Custom 404 Pages: Serve custom HTML for not found routes
- Security Basics: Path traversal protection and request limits
Installation
As a dependency:
go get github.com/codetesla51/raw-http@v1.0.1
Build from source:
git clone https://github.com/codetesla51/raw-http.git cd raw-http go build -o server main.go ./server
Quick Start
package main
import (
"log"
"github.com/codetesla51/raw-http/server"
)
func main() {
srv := server.NewServer(":8080")
srv.Register("GET", "/ping", func(req *server.Request) ([]byte, string) {
return server.CreateResponseBytes("200", "text/plain", "OK", []byte("pong"))
})
srv.Register("GET", "/users/:id", func(req *server.Request) ([]byte, string) {
userID := req.PathParams["id"]
return server.CreateResponseBytes("200", "text/plain", "OK",
[]byte("User: "+userID))
})
srv.Register("POST", "/api/data", func(req *server.Request) ([]byte, string) {
name := req.Body["name"]
if name == "" {
return server.Serve400("name is required")
}
return server.Serve201("created: " + name)
})
if err := srv.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
Usage Examples
curl http://localhost:8080/ping # Returns: pong curl http://localhost:8080/users/42 # Returns: User: 42 curl -X POST -d "name=john" http://localhost:8080/api/data # Returns: created: john curl https://localhost:8443/ping -k # HTTPS request (use -k to skip certificate verification)
Advanced Features
Path Parameters
srv.Register("GET", "/users/:id", func(req *server.Request) ([]byte, string) {
userID := req.PathParams["id"] // "123" from /users/123
return server.CreateResponseBytes("200", "text/plain", "OK", []byte(userID))
})
srv.Register("GET", "/posts/:postId/comments/:commentId",
func(req *server.Request) ([]byte, string) {
postID := req.PathParams["postId"]
commentID := req.PathParams["commentId"]
// ...
})
Query Parameters
srv.Register("GET", "/search", func(req *server.Request) ([]byte, string) {
q := req.Query["q"] // /search?q=golang
page := req.Query["page"] // /search?q=golang&page=2
// ...
})
Configuration
cfg := &server.Config{
ReadTimeout: 60 * time.Second,
WriteTimeout: 30 * time.Second,
MaxBodySize: 50 * 1024 * 1024, // 50MB
EnableKeepAlive: true,
EnableLogging: true,
}
srv := server.NewServerWithConfig(":8080", cfg)
srv.Register("GET", "/ping", handler)
srv.ListenAndServe()
Response Helpers
srv.Register("POST", "/login", func(req *server.Request) ([]byte, string) {
if req.Body["password"] == "" {
return server.Serve400("password required")
}
if !authenticate(req.Body["user"], req.Body["password"]) {
return server.Serve401("invalid credentials")
}
return server.Serve201("logged in")
})
Available helpers: Serve201, Serve204, Serve400, Serve401, Serve403, Serve405, Serve429, Serve500, Serve502, Serve503
HTTPS/TLS Support
srv := server.NewServer(":8080")
srv.EnableTLS(":8443", "server.crt", "server.key")
srv.Register("GET", "/ping", handler)
srv.ListenAndServe() // Serves HTTP on 8080 and HTTPS on 8443
Self-signed certificates included for development. For production, use Let's Encrypt:
certbot certonly --standalone -d yourdomain.com cp /etc/letsencrypt/live/yourdomain.com/fullchain.pem server.crt cp /etc/letsencrypt/live/yourdomain.com/privkey.pem server.key
Performance Benchmarks
Benchmarks on 8-core system:
Scenario Concurrency Requests/sec Latency GET /ping 100 5,601 17.9ms GET /ping 500 11,042 45.3ms POST with body 100 5,773 17.3ms
Key insights: HTTP/1.1 keep-alive nearly doubles throughput (5k → 11k RPS). Buffer pooling reduces GC overhead. Optimal concurrency range: 100-500 connections.
Technical Implementation
- Buffer Pooling: Three sync.Pool instances (chunk, request, response buffers) reduce GC pressure
- TCP Connection Management: HTTP/1.1 keep-alive for connection reuse
- Custom HTTP Parser: Zero-dependency request parsing with chunked reading
- Graceful Shutdown: SIGINT/SIGTERM handling with 2-second grace period
- Panic Recovery: Per-connection panic handling prevents server crashes
- TLS/SSL Layer: Optional HTTPS encryption with goroutine-per-connection model
- Path Traversal Protection: Blocks directory traversal attacks on static files
- Goroutine-per-connection: Leverages Go's concurrency model
- MIME Detection: Comprehensive content-type handling
- Memory Efficient: Streams request bodies instead of full buffering
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Server Struct │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Router │ │ TLS Config │ │ Graceful Shutdown │ │
│ └──────┬──────┘ └─────────────┘ └─────────────────────┘ │
└─────────┼───────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Connection Handler │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Buffer Pool │ │ Request │ │ Keep-Alive Loop │ │
│ │ (sync.Pool)│ │ Parser │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Buffer Pooling Details
Three sync.Pool instances reduce garbage collection pressure:
Pool Buffer Size Purpose chunkBufferPool 4KB Reading from TCP connection requestBufferPool 8KB Accumulating request headers responseBufferPool Dynamic Building HTTP responses
Buffers larger than 16KB are discarded to prevent memory bloat.
Request Object
Handlers receive *server.Request:
Field Type Description Method string HTTP method (GET, POST, PUT, DELETE) Path string Request path without query string PathParams map[string]string URL parameters from route (:id) Query map[string]string Query string parameters Body map[string]string Parsed request body Headers map[string]string HTTP headers Browser string Detected browser name
Project Structure
raw-http/ ├── server/ │ ├── server.go Core HTTP server logic │ └── server_test.go Test suite (21 tests) ├── pages/ Static files and templates │ ├── index.html │ ├── 404.html Custom 404 page │ └── styles.css ├── server.crt Self-signed certificate ├── server.key Private key └── main.go Example application
Testing
go test ./server/... -v
21 tests cover HTTP parsing, route matching, path parameters, response formatting, error handling, and integration tests with real TCP connections.
What I Learned
- How HTTP requests are structured at the protocol level
- TCP connection lifecycle and keep-alive mechanics
- Memory optimization with buffer pooling and sync.Pool
- Graceful shutdown patterns in Go (signal handling, connection draining)
- Panic recovery and error isolation in concurrent systems
- TLS/SSL encryption and certificate management
- Security considerations (DoS protection, path traversal, HTTPS)
- Go networking primitives and goroutine patterns
- The critical importance of connection reuse for performance
- How low-level implementation details impact throughput
Limitations
This is a learning project, not production software:
- Not production-tested (use for learning/small projects only)
- Single process (no clustering support)
- No middleware system (implement yourself if needed)
- No observability (no built-in metrics/tracing)
- ~5k connection ceiling (performance degrades at high concurrency)
For production applications, use Go's net/http package.
Why This Matters
Most web development happens at the framework level. Building from TCP sockets up reveals what frameworks abstract away - HTTP parsing, connection management, buffer pooling, graceful shutdown, TLS encryption, and the networking fundamentals that determine performance.
The journey from 250 RPS to 11,000+ RPS demonstrates how understanding these low-level details enables meaningful optimization.
Built by Uthman | @codetesla51