๐ŸŒฌ๏ธ Breeze docs

HTTP client

Breeze's outbound HTTP client, built on gnet โ€” the same event-loop engine the server runs on.

c := client.New()
defer c.Close()

resp, err := c.Get("http://auth-service/verify")
if err != nil {
	return err
}
if resp.OK() {
	fmt.Println(resp.String())
}

With headers โ€” what Fleet tracing uses to inject trace context:

req := client.NewRequest("POST", url, body).
	SetHeader("Content-Type", "application/json").
	SetHeader("Traceparent", tc.String())

resp, err := c.Do(req)

Why gnet, not net/http

The server side of Breeze is built on gnet. Sharing the engine for outbound calls means both directions use one non-blocking I/O model and one connection-pooling strategy, rather than an event-loop server sitting beside net/http's goroutine-per-connection machinery for every outgoing call. The cost: gnet is a raw TCP byte-stream engine with no notion of HTTP, so request serialisation and response parsing are implemented in this package rather than inherited from the standard library โ€” which is where the limitations below come from.

Limitations โ€” read these first

This client exists for service-to-service JSON traffic. For anything else, net/http is the right answer with no penalty.

LimitationDetail
HTTP/1.1 onlyno HTTP/2 โ€” far past diminishing returns for JSON between services
TLS via crypto/tlsthe handshake is done by tls.Dial, then the connection is handed to gnet with Enroll
No chunked requestschunked responses are decoded; requests always carry Content-Length
No redirect followinga 3xx is returned as-is
Bodies fully bufferedwrong tool for SSE or large downloads
No pipeliningone in-flight request per connection

Config

c := client.New(client.Config{
	Timeout:             10 * time.Second,
	MaxIdleConnsPerHost: 128,
})
FieldDefaultMeaning
Timeout30sbounds the whole call: connect + write + read
MaxIdleConnsPerHost64idle-connection budget per upstream host
DialTimeout5sbounds establishing the connection
MaxResponseBytes32 MiBcaps the response body
UserAgent"breeze-client/1"sent unless the request sets its own
TLSConfignilreplaces the default tls.Config

MaxIdleConnsPerHost defaults to 64 rather than net/http's 2 โ€” 2 is sized for a browser calling many hosts once, 64 for a service calling the same few upstreams on every request. Timeout covers the whole call rather than each phase, since a per-phase timeout lets a slow upstream spend the budget three times over.

Requests and responses

req := client.NewRequest("POST", "https://api.example.com/users", body)
req.SetHeader("Content-Type", "application/json") // replaces
req.AddHeader("X-Tag", "b")                       // appends
val, ok := req.GetHeader("Content-Type")
hdr := req.Header()                               // the http.Header
req = req.WithContext(ctx)                        // cancellation

SetHeader, AddHeader and WithContext return the request, so they chain.

type Response struct {
	Status int
	Header http.Header
	Body   []byte
}

resp.OK()      // 2xx โ€” nil-safe
resp.String()  // body as a string โ€” nil-safe

Both methods are nil-safe, so resp.OK() on an error path is false rather than a panic โ€” the natural shape (check the error, then check the status) reads cleanly when the second check cannot itself fail.

Convenience methods

c.Get(url)
c.Post(url, contentType, body)
c.PostJSON(url, body)   // Content-Type: application/json
c.Do(req)               // the general form

Sentinel errors

ErrorWhen
ErrNilRequestDo(nil)
ErrNoURLthe request's URL is empty or whitespace
ErrResponseTooLargethe body exceeded MaxResponseBytes

Lifecycle

c := client.New()
defer c.Close()

The gnet engine starts lazily, on the first request, not by New โ€” a Client constructed and never used costs a struct, which is what makes it reasonable for a library to hold one as a field. Close shuts the engine down and releases pooled connections. A Client is safe for concurrent use; that is the point of the per-host pools. c.Config() returns the effective configuration with defaults applied.

Who uses it

  • Fleet tracing โ€” span export to the aggregator, with Traceparent injected via SetHeader. See Fleet Tracing.
  • MCP live tools โ€” reading a running service's dashboard API.

Both are the shape this client is for: JSON, service-to-service, small bodies, the same few upstreams on every call.

Generated documentation for the Breeze framework ยท built with SvelteKit, fully static.