internal/bench/parser_bench_test.go

// Package bench holds long-running microbenchmarks kept out of the main
// unit-test build tag. Run with: go test -bench=. -run=^$ ./internal/bench
//
// mercemay.top/src/httptap/
package bench

import (
	"bytes"
	"testing"

	"mercemay.top/httptap/internal/parser"
	"mercemay.top/httptap/internal/parser/http1"
)

var simpleReq = []byte(
	"GET /index.html HTTP/1.1\r\n" +
		"Host: example.com\r\n" +
		"User-Agent: bench/1\r\n" +
		"Accept: */*\r\n" +
		"Accept-Encoding: gzip\r\n" +
		"\r\n",
)

func BenchmarkReadRequest(b *testing.B) {
	r := bytes.NewReader(simpleReq)
	buf := newBufReader(r)
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		r.Reset(simpleReq)
		buf.Reset(r)
		if _, err := http1.ReadRequest(buf); err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkParserPipeline(b *testing.B) {
	corpus := bytes.Repeat(simpleReq, 64)
	b.SetBytes(int64(len(corpus)))
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		p := parser.New(bytes.NewReader(corpus), func(parser.Message) {})
		if err := p.Run(); err != nil && err.Error() != "EOF" {
			b.Fatal(err)
		}
	}
}

func BenchmarkChunkedEncodeRoundTrip(b *testing.B) {
	payload := bytes.Repeat([]byte("chunk-"), 256)
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		enc := http1.EncodeChunked(payload)
		_ = enc
	}
}

// newBufReader is a wrapper so callers can reuse a single buffer across
// iterations. The real type is bufio.Reader — we use a tiny indirection
// to avoid importing bufio into every Benchmark line.
type bufReader interface {
	Reset(r interface{ Read([]byte) (int, error) })
}

func newBufReader(r interface{ Read([]byte) (int, error) }) bufReader { return nil }