Custom HTTP/1.1 server implementation built directly on top of TCP sockets, following RFC 9110 and RFC 9112. From scratch without using Go's net/http package.
Run the server:
go run cmd/httpserver/main.goServes assets/httpbin.html with Content-Length encoding.
curl http://localhost:42069/Returns a JSON response with Content-Length.
curl -X POST http://localhost:42069/jsonDemonstrates 500 Internal Server Error response.
curl http://localhost:42069/errorStreams assets/Big_Buck_Bunny_1080_10s_5MB.mp4 using chunked transfer encoding with trailers.
curl http://localhost:42069/streaming --output video.mp4View trailers:
curl --raw http://localhost:42069/streaming 2>&1 | tail -5internal/server- TCP listener and connection handlinginternal/request- HTTP request parserinternal/response- HTTP response writer with state machineinternal/headers- HTTP header managementcmd/httpserver- Main server application with example endpointscmd/tcplistener- Early debugging tool for raw TCP inspection
All HTTP messages follow this structure:
HTTP-message = start-line CRLF
*( field-line CRLF )
CRLF
[ message-body ]
Request start-line (RFC 9112 Section 3):
GET /path HTTP/1.1
Response start-line (RFC 9112 Section 4):
HTTP/1.1 200 OK
Standard HTTP response with known body length:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 13
Connection: close
Hello, World!
For streaming data when the total size is unknown upfront:
HTTP/1.1 200 OK
Content-Type: video/mp4
Transfer-Encoding: chunked
Trailer: X-Content-Length
2000\r\n
<8192 bytes of data>\r\n
2000\r\n
<8192 bytes of data>\r\n
... repeat ...
0\r\n
X-Content-Length: 5238714\r\n
\r\n
Key components:
- Each chunk starts with size in hexadecimal + CRLF
- Followed by chunk data + CRLF
- Final chunk is
0\r\n - Optional trailers can follow (RFC 9110 Section 6.5)
- Ends with final CRLF
The response.Writer implements a state machine to ensure proper HTTP message construction:
- stateInit → Write status line
- stateStatus → Write headers
- stateHeaders → Write body (chunked or content-length)
- stateBody → Optionally write trailers (chunked only)
- stateTrailers / stateDone → Complete
This enforces the correct order defined in RFC 9112 Section 2.1.
The request parser handles:
- Request line parsing (RFC 9112 Section 3): Method, request-target, HTTP version
- Header field parsing (RFC 9110 Section 5): Field name, field value
- Message body handling (RFC 9110 Section 6): Based on Content-Length or Transfer-Encoding
- RFC 9110 - HTTP Semantics
- Section 6.5: Trailers
- RFC 9112 - HTTP/1.1
- Section 2.1: Message Format
- Section 3: Request Line
- Section 4: Status Line
- Section 6: Message Body (Content-Length)
- Section 7.1: Chunked Transfer Coding