adapterutil

package
v0.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package adapterutil holds the framework-agnostic helpers shared by every mAPI-ng web adapter (gin today; echo/chi/beego/net-http next). Each adapter's job is the same — after a request completes, extract exemplar breadcrumbs, a final status, and byte counts — so the parts that don't touch a framework's types live here once, single-sourced, rather than being copy-pasted per adapter. It imports only the stdlib plus the core client (for NoStatusReason); the core never imports this package, so there is no cycle.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClampNonNegative

func ClampNonNegative(n int64) int64

ClampNonNegative folds a negative byte count (unknown ContentLength is -1, an unwritten body Size is -1) to 0.

func ParseTraceparent

func ParseTraceparent(header string) (traceID, spanID string)

ParseTraceparent extracts the trace id and span id from a W3C traceparent header (RFC: "version-traceid-spanid-flags", e.g. "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"). It is deliberately hand-rolled so the client stays free of any OpenTelemetry dependency. Both ids are returned empty unless the header is well-formed: exactly four dash- separated parts, a 32-hex trace id and a 16-hex span id, neither all-zero (the spec's "invalid" sentinel). Best-effort: any deviation yields empties.

func ReclassifyNoStatus

func ReclassifyNoStatus(ctx context.Context, status int) (int, maping.NoStatusReason)

ReclassifyNoStatus decides whether a completed request should be reported as NO_STATUS. It reclassifies only on a real abort signal — the request context canceled (client disconnect) or its deadline fired — before the response finished: it returns (0, reason), mapping a fired deadline vs. a plain cancellation, with anything else recorded as OTHER. A live context (no cause) is left as the written status and NoStatusUnspecified, so an ordinary empty-body handler is never misreported as NO_STATUS. Panics are converted to a 500 by the host's recovery middleware, so they surface as a status, not here.

Types

type ResponseWriter

type ResponseWriter struct {
	http.ResponseWriter
	// contains filtered or unexported fields
}

ResponseWriter wraps an http.ResponseWriter to capture the two things an adapter needs after a stdlib request completes — the final status code and the total bytes written — which net/http otherwise discards. Frameworks like Gin track these on their own writer; a bare net/http handler does not, so the wrapper stands in.

Capabilities (Flusher/Hijacker/ReaderFrom) are preserved by DELEGATION rather than by conditional interface assertion, so a library that type-asserts the writer keeps working. Unconditional delegation is safe because the only case the wrapper "claims" a capability the underlying lacks is a transport that could not perform the operation anyway (e.g. hijacking an HTTP/2 conn): the delegate then degrades to http.ErrNotSupported (Hijack), a no-op (Flush), or a plain io.Copy (ReadFrom) — never a panic or silent data loss. Unwrap exposes the real writer for http.ResponseController and any caller that needs more.

func WrapResponseWriter

func WrapResponseWriter(w http.ResponseWriter) *ResponseWriter

WrapResponseWriter returns a ResponseWriter capturing status and byte count. Status defaults to http.StatusOK — net/http's implicit status when a handler writes a body without calling WriteHeader.

func (*ResponseWriter) BytesWritten

func (w *ResponseWriter) BytesWritten() int64

BytesWritten returns the total bytes forwarded through Write and ReadFrom.

func (*ResponseWriter) Flush

func (w *ResponseWriter) Flush()

Flush forwards to the underlying http.Flusher, or no-ops when the transport cannot flush (SSE without buffering support).

func (*ResponseWriter) Hijack

func (w *ResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error)

Hijack forwards to the underlying http.Hijacker, or returns http.ErrNotSupported when the transport cannot be hijacked (websockets over HTTP/2).

func (*ResponseWriter) ReadFrom

func (w *ResponseWriter) ReadFrom(src io.Reader) (int64, error)

ReadFrom delegates to the underlying io.ReaderFrom when present (preserving sendfile), otherwise falls back to io.Copy. Either way the copied bytes are counted, so RespBytes stays correct with or without the fast path.

func (*ResponseWriter) Status

func (w *ResponseWriter) Status() int

Status returns the captured status code (http.StatusOK if none was written).

func (*ResponseWriter) Unwrap

func (w *ResponseWriter) Unwrap() http.ResponseWriter

Unwrap returns the embedded writer (Go 1.20+ convention) so http.ResponseController reaches the real writer.

func (*ResponseWriter) Write

func (w *ResponseWriter) Write(b []byte) (int, error)

Write marks the header written (the default status is already 200), forwards the bytes, and counts what the underlying writer accepted.

func (*ResponseWriter) WriteHeader

func (w *ResponseWriter) WriteHeader(code int)

WriteHeader records the code on the first call only, then forwards. Later calls forward but do not overwrite the captured status, matching net/http's "superfluous WriteHeader" behavior.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL