This is a fully functional HTTP/1.1 server implemented from scratch using only low-level networking primitives provided by the Go standard library. It was created as a systems-level learning project to explore how web servers operate at the lowest level, including TCP socket management, manual HTTP parsing, and concurrent connection handling
This project was built to:
- Deepen understanding of how HTTP and TCP networking work under the hood.
- Strengthen systems programming skills, especially around concurrency, sockets, and I/O
- Practice Go by focusing on idiomatic structuring, testing, and low-level networking construct
- Manual TCP server using
net.ListenandAccept - Manual HTTP/1.1 request parsing (method, path, headers)
- Supports basic HTTP routes:
GET /— Health checkGET /echo/<msg>— Echoes message in response bodyGET /user-agent— ReturnsUser-AgentheaderGET /files/<filename>— Return a file contentPOST /files/<filename>— Saves request body to disk
- Gzip compression for clients that support it (
Accept-Encoding: gzip) - Concurrency using goroutines (one per connection)
- Persistent connections
- Proper request body reading and
Content-Lengthhandling - 404 Not Found for unknown routes
- Graceful connection closing (
Connection: close, EOF handling) - Prevents directory traversal (e.g.,
../) - Includes structured logging (standard
slogpackage) for connections and route handling. - No third-party dependencies — only Go standard library
- clone the repository
git clone https://github.com/raphico/go-http-server-scratch.git
cd go-http-server-scratch- Run the server
go run cmd/server/main.go- Try it out
curl http://localhost:4221/
curl http://localhost:4221/echo/hello
curl -v --header "User-Agent: foobar/1.2.3" http://localhost:4221/user-agent
curl -v -H "Accept-Encoding: gzip" http://localhost:4221/echo/abc | hexdump -C
curl -v --data "12345" -H "Content-Type: application/octet-stream" http://localhost:4221/files/file_123
curl -i http://localhost:4221/files/foo
curl -i http://localhost:4221/files/non_existant_file| File/Folder | Purpose |
|---|---|
cmd/server/main.go |
Application entry point |
internal/server/server.go |
TCP socket and connection management |
internal/protocol/request.go |
HTTP request parsing |
internal/protocol/response.go |
HTTP response generation |
internal/protocol/header.go |
HTTP header management |
internal/handler |
Handler functions |
internal/mux/mux.go |
Request router (multiplexer) |
internal/compress |
Gzip compression support |
- Idiomatic Go Design
- The use of small, focused packages(like mux, handler, protocol, server) to separation functionality into logical units
- The use of structs to model real-world entities(like Request, Response, Header, Mux, Server) with associated methods(e.g. Set, Write, Send, HandleFunc) to manipulate and mange these entities
- Emulated Go’s http package architecture with structs + methods + interface-based handlers
- Networking & Protocol internals
- What TCP sockets are: one endpoint of a two-way communication link made up of an IP address and a Port number
- Built a manual TCP server using the net package
- manually parsed HTTP/1.1 request and constructed valid responses
- Implemented persistent connections with graceful connection closing (via EOF or Connection: close)
- Added Gzip compression based on Accept-Encoding
- Buffered I/O efficiency
- Used
bufio.Readerto efficiently read TCP connections in chunks - Learned that reusing the buffered Reader across requests improves efficiency and reduces allocation overhead
- Used a
Strings.Builderto construct response strings efficiently, avoiding repeated allocation with+andfmt.Sprintf
- Used
- Concurrency with Goroutines
- Leveraged goroutines to handle multiple connections concurrently.
- Understood that goroutines are lightweight, non-blocking, and managed by Go’s runtime scheduler
- Also learned that the main goroutine must stay alive (e.g., via loops or proper synchronization with channels) or the server will exit prematurely
- Design patterns and architecture
- Applied dependency injection for a more decoupled design
- Used factory pattern for initialization of Responses, Multiplexer, and Server
- Avoided hardcoding status codes, routing logic, methods, for flexibility and clarity
- Designed a basic router with Method-based pattern parsing(
GET /pathsimilar to the mux in the http package). Static and prefix-matching for dynamic paths. Fallback to a NotFoundHandler for unmatched routes - Security & File handling
- Protected against directory traversal attacks using
filePath.EvalSymlinksand absolute paths comparison - Used robust error handling in all file operations
- Protected against directory traversal attacks using
- Memory & Performance
- Used pointers to avoid unnecessary copying and to mutate structs directly
- Understood Go's handling of slices and memory allocation (especially for []byte bodies)
- Integrated Go’s slog for structured, JSON-based logging of requests, connections, and errors.
Inspired by @coder-crafters HTTP Server Challenge All implementation written from scratch by me.
