Package echo
Overview
Package echo is an HTTP request and response service, like httpbin: it reflects requests back and produces whatever response a client asks for, so clients, proxies and the funnyhaha.technology/rfc packages can be tried out with curl.
$ curl -i https://echo.example/status/401
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json
Www-Authenticate: Bearer realm="echo"
{"title":"Unauthorized","status":401}
The routes are listed on NewAPI. The binary is in cmd/echo, and its image is built by the Dockerfile.
Subpackages
| Package | Description |
|---|---|
funnyhaha.technology/echo/cmd/echo | Command echo serves the echo API. |
Index
- Constants
- Variables
type APIBuildertype APIDepstype DelayOpFunctype DelayParamstype DelayResulttype EchoJSONOpFunctype EchoJSONParamstype EchoJSONResulttype EchoOpFunctype EchoParamstype EchoResulttype StatusOpFunctype StatusParamstype StatusResulttype Ticktype TicksOpFunctype TicksParams
Constants
const (
MaxDelay = 10 * time.Second
MaxTicks = 100
MinInterval = 10 * time.Millisecond
MaxInterval = 10 * time.Second
MaxBodyBytes = 1 << 20
)Limits on what a client can ask for.
Variables
var (
ErrInvalidCode = errors.New("status code must be from 400 to 599")
ErrInvalidDelay = errors.New("delay must be whole seconds from 0 to 10")
ErrInvalidCount = errors.New("count must be from 1 to 100")
ErrInvalidInterval = errors.New("interval must be a duration from 10ms to 10s")
ErrMissingBody = errors.New("request body is missing")
)The errors the operations and wire requests declare.
Types
APIBuilder source
type APIBuilder struct {
// contains filtered or unexported fields
}APIBuilder configures the API's handler. Its With methods return a modified copy.
NewAPI source
func NewAPI(deps APIDeps) APIBuilderNewAPI returns a builder for the API, which serves:
- /anything and /anything/{path...}, for any method but TRACE: the request reflected back as JSON, without the Forwarded and X-Forwarded-* fields.
- POST /json: the request's JSON body reflected back.
- /status/{code}: an RFC 9457 problem with any 4xx or 5xx status code and the header fields it needs. ?retry_after=N adds Retry-After to a 429 or 503.
- GET /delay/{seconds}: a response after up to 10 seconds.
- GET /sse?count=N&interval=D: N server-sent events, D apart.
- GET /meta: the service's build information.
Every route allows CORS requests from any origin, without credentials. It panics if a dependency is missing.
(APIBuilder) Handler source
func (b APIBuilder) Handler() http.HandlerHandler returns the API's handler.
(APIBuilder) WithLogger source
func (b APIBuilder) WithLogger(logger *slog.Logger) APIBuilderWithLogger sets the logger for request logs.
(APIBuilder) WithTrustedProxies source
func (b APIBuilder) WithTrustedProxies(prefixes ...netip.Prefix) APIBuilderWithTrustedProxies trusts the X-Forwarded-For entries written by proxies in these prefixes, for the client IP that /anything reports and the request log records.
APIDeps source
type APIDeps struct {
Echo EchoOpFunc
EchoJSON EchoJSONOpFunc
Status StatusOpFunc
Delay DelayOpFunc
Ticks TicksOpFunc
}APIDeps are the operations the API calls, one per route.
DelayOpFunc source
type DelayOpFunc func(context.Context, DelayParams) (DelayResult, error)DelayOpFunc waits for the delay, or until its context is cancelled.
Delay source
func Delay(after func(time.Duration) <-chan time.Time) DelayOpFuncDelay returns a DelayOpFunc that waits on after, which is time.After outside tests.
DelayParams source
type DelayParams struct {
Delay time.Duration
}DelayParams are the params of a DelayOpFunc.
DelayResult source
type DelayResult struct {
Delay time.Duration
}DelayResult is the result of a DelayOpFunc.
EchoJSONOpFunc source
type EchoJSONOpFunc func(context.Context, EchoJSONParams) (EchoJSONResult, error)EchoJSONOpFunc reflects a JSON value.
EchoJSON source
func EchoJSON() EchoJSONOpFuncEchoJSON returns an EchoJSONOpFunc that reflects the value unchanged.
EchoJSONParams source
type EchoJSONParams struct {
// Value is a decoded JSON value.
Value any
}EchoJSONParams are the params of an EchoJSONOpFunc.
EchoJSONResult source
type EchoJSONResult struct {
Value any
}EchoJSONResult is the result of an EchoJSONOpFunc.
EchoOpFunc source
type EchoOpFunc func(context.Context, EchoParams) (EchoResult, error)EchoOpFunc reflects a request.
Echo source
func Echo() EchoOpFuncEcho returns an EchoOpFunc that reflects the request unchanged.
EchoParams source
type EchoParams struct {
Method string
Path string
Query url.Values
Header http.Header
Body []byte
ClientIP netip.Addr
}EchoParams are the params of an EchoOpFunc: the parts of a request it reflects.
EchoResult source
type EchoResult struct {
Method string
Path string
Query url.Values
Header http.Header
Body []byte
ClientIP netip.Addr
}EchoResult is the result of an EchoOpFunc: what's reflected back.
StatusOpFunc source
type StatusOpFunc func(context.Context, StatusParams) (StatusResult, error)StatusOpFunc builds the status for a code, with the header fields RFC 9110 requires of it. It returns ErrInvalidCode for a code it can't.
Statuses source
func Statuses(bearer rfc6750.Challenge, basic rfc7617.Challenge, upgrade rfc9110.Protocol) StatusOpFuncStatuses returns a StatusOpFunc. The statuses whose header fields RFC 9110 requires get them from its arguments:
- 401 has bearer's challenge in WWW-Authenticate (Section 15.5.2).
- 405 has "Allow: GET" (Section 15.5.6).
- 407 has basic's challenge in Proxy-Authenticate (Section 15.5.8).
- 416 has "Content-Range: bytes */0" (Section 15.5.17).
- 426 has upgrade in Upgrade (Section 15.5.22).
428, 429, 431 and 511 come from RFC 6585, and every other code from rfc9110.NewStatus.
StatusParams source
type StatusParams struct {
// Code is a 4xx or 5xx status code.
Code int
// RetryAfter, if positive, adds Retry-After to a 429 or 503.
RetryAfter time.Duration
}StatusParams are the params of a StatusOpFunc.
StatusResult source
type StatusResult struct {
Status rfc9110.Status
}StatusResult is the result of a StatusOpFunc. It's an HTTP status, because that's this service's subject matter.
Tick source
type Tick struct {
N int
}Tick is one event of a TicksOpFunc's stream, numbered from 1.
TicksOpFunc source
type TicksOpFunc func(context.Context, TicksParams) (iter.Seq2[Tick, error], error)TicksOpFunc streams Count ticks, Interval apart. The iterator stops when its context is cancelled, yielding the context's error, and can be ranged over only once.
Ticks source
func Ticks(after func(time.Duration) <-chan time.Time) TicksOpFuncTicks returns a TicksOpFunc that waits on after, which is time.After outside tests, between ticks. The first tick is sent at once.
TicksParams source
type TicksParams struct {
Count int
Interval time.Duration
}TicksParams are the params of a TicksOpFunc.