A tiny, educational HTTP/1.1 server written from scratch in Go. It implements its own request parser, headers handling, and response writer over raw TCP.
- Raw TCP listener with per-connection goroutines
- HTTP/1.1 request parsing: request-line, headers, and optional body
- Response writer with status line, headers, and body
- Simple routing via a
switchon the request target - Example binaries:
httpserver: serves example routes on port42069tcplistener: prints parsed requests to stdout
- Go
1.24.5(as declared ingo.mod)
Run the sample HTTP server:
go run ./cmd/httpserverAlternatively, build and run:
go build -o bin/httpserver ./cmd/httpserver
./bin/httpserverYou should see the server start on port 42069.
/→ 200 OK with an HTML page/yourproblem→ 400 Bad Request with a plaintext error message/myproblem→ 500 Internal Server Error with a plaintext error message
curl -i http://localhost:42069/
curl -i http://localhost:42069/yourproblem
curl -i http://localhost:42069/myproblemcmd/
httpserver/ # Example HTTP server using the internal packages
tcplistener/ # Raw TCP listener that prints parsed requests
internal/
headers/ # Header map + parser
request/ # HTTP/1.1 request parser (line, headers, body)
response/ # Response writer + default headers
server/ # TCP server, connection loop, and handler glue
- Run all tests:
go test ./...The server package exposes a minimal API to accept connections and handle requests.
Handler signature:
type Handler func(w *response.Writer, req *request.Request) *server.HandlerErrorStarting a server:
srv, err := server.Serve(42069, func(w *response.Writer, req *request.Request) *server.HandlerError {
switch req.RequestLine.RequestTarget {
case "/":
body := []byte("hello from bare metal")
headers := response.GetDefaultHeaders(len(body))
headers.Replace("Content-Type", "text/plain")
_ = w.WriteStatusLine(response.StatusOkCode)
_ = w.WriteHeaders(headers)
_, _ = w.WriteBody(body)
return nil
default:
return &server.HandlerError{
Code: response.StatusBadRequestCode,
Message: "unknown route\n",
}
}
})
if err != nil { panic(err) }
defer srv.Close()HandlerError short-circuits normal handling and writes an error response with the given status code and message.
- HTTP version: only
HTTP/1.1requests are accepted - Methods supported by parser:
GET,POST,DELETE,PUT,PATCH - Body handling: requires a correct
Content-Length; connection is closed after response - Default headers include
Content-Length,Connection: close, andContent-Type: text/plain - Routing is intentionally minimal (a
switchincmd/httpserver/main.go) - Port is currently hard-coded to
42069incmd/httpserver/main.go
For debugging the request parser:
go run ./cmd/tcplistenerIt accepts connections on :42069 and prints the parsed request-line, headers, and body.
This project was built while following ThePrimeagen’s course “Learn the HTTP Protocol in Go” on Boot.dev. Huge thanks for the excellent deep-dive from TCP to HTTP.