Minimal Go utilities to parse raw HTTP requests from TCP connections and to run a small HTTP server. The codebase includes a simple HTTP server, a TCP listener that demonstrates low-level request parsing, and a tiny UDP client for quick tests.
cmd/httpserver/— small HTTP server that uses the project's request/response abstractions and supports chunked responses and simple proxying.cmd/tcplistener/— a TCP server demonstrating raw parsing of HTTP requests; prints the request line, headers, and body.cmd/udplistener/— a simple UDP client that sends user input tolocalhost:42069(useful for testing UDP behavior).internal/headers/— header parsing utilities and tests.internal/request/— HTTP request parser and helpers, used by the server and reader utilities.internal/response/— response construction helpers (status line, headers, chunked body helpers).tee/,tmp/— example/raw request files and sample payloads used for manual testing.
Requirements: Go 1.20+ (module-aware build). From the repository root:
Build all binaries:
go build ./...Run the HTTP server (listens on port 42069):
go run ./cmd/httpserverTest with curl:
curl http://localhost:42069/
curl http://localhost:42069/yourproblem # returns 400
curl http://localhost:42069/httpbin/get # proxies request to https://httpbin.org/getRun the TCP listener (prints raw parsed request details):
go run ./cmd/tcplistenerSend a raw HTTP request (Linux/macOS example using nc / netcat):
printf "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" | nc localhost 42069On Windows you can use ncat (Nmap) or other netcat ports to send raw bytes, or use curl to exercise the HTTP server.
UDP client (sends user input lines to localhost:42069):
go run ./cmd/udplistener
# Type and press Enter to send each lineRun the unit tests for internal packages:
go test ./...There are tests in internal/headers and internal/request that verify parsing behavior.
- Request parsing is implemented in
internal/requestand handles request line, headers, and optional body (usingContent-Length). cmd/httpserverdemonstrates using the parsing/response utilities to implement a small web server with chunked responses, trailers, and basic error handling.cmd/tcplistenershows how the low-level parser can be used directly on a connection for inspection and debugging.
The server expects a handler with this signature:
// type Handler func(w *response.Writer, req *request.Request)When you call server.Serve(port, myHandler), your handler receives a *response.Writer (for writing status, headers and body) and the parsed *request.Request.
Minimal example (simple 200 response):
func myHandler(w *response.Writer, req *request.Request) {
body := []byte("Hello from handler\n")
h := response.GetDefaultHeaders(len(body))
h.Replace("content-type", "text/plain")
w.WriteStatusLine(response.StatusGoodReq)
w.WriteHeaders(*h)
w.WriteBody(body)
}Chunked response example (with trailers):
func chunkHandler(w *response.Writer, req *request.Request) {
// start response
w.WriteStatusLine(response.StatusGoodReq)
// set headers for chunked transfer
h := response.GetDefaultHeaders(0)
h.Delete("content-length")
h.Set("Transfer-Encoding", "chunked")
h.Set("Trailer", "X-Content-SHA256")
w.WriteHeaders(*h)
// write chunks
for i := 0; i < 3; i++ {
chunk := []byte(fmt.Sprintf("chunk %d\n", i))
w.WriteChunkedBody(chunk)
}
w.WriteChunkedBodyDone()
// write trailers
trailers := headers.NewHeaders()
trailers.Set("X-Content-SHA256", "dummysum")
w.WriteHeaders(*trailers)
}Notes:
- Use
req.RequestLineandreq.Headersto inspect method, path and incoming headers. - To send an error response from inside a handler you can write a
server.HandlerErrordirectly to the underlying writer, e.g.:
herr := &server.HandlerError{StatusCode: response.StatusBadReq, Message: "bad request"}
herr.Write(w.Writer)- Check
tee/andtmp/for sample raw HTTP request payloads that can be fed intotcplisteneror used withnc. - Try the
/httpbin/*path on the HTTP server to see chunked proxying and trailers in action.