mirror of
https://github.com/tailscale/tailscale.git
synced 2026-07-15 10:03:09 -04:00
* ipn/localapi,ipnlocal,feature/acme,client/local: honour Retry-After on cert rate-limit serveCert now responds with 429 + Retry-After when the underlying ACME error is a rate limit, instead of a generic 500. client/local surfaces this as a typed RateLimitedError with the parsed hint so callers can back off intelligently. Updates tailscale/corp#42164 Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> * tsweb,feature/acme,ipn/localapi,ipnlocal: generalise cert error → HTTP mapping via tsweb.HTTPStatuser Introduces a tsweb.HTTPStatuser interface, any error can implement to describe its intended HTTP response (code, message, headers). Moves CertRateLimitedError from ipnlocal to feature/acme where it's constructed, and it now uses HTTPStatuser to return 429 + Retry-After. serveCert now checks for tsweb.HTTPStatuser rather than the specific error type, so it no longer needs to know about the ACME rate-limit type. Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk> --------- Signed-off-by: chaosinthecrd <tom@tmlabs.co.uk>
37 lines
911 B
Go
37 lines
911 B
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package acme
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"tailscale.com/tsweb"
|
|
)
|
|
|
|
// certRateLimitedError is returned when the upstream ACME CA rate-limited
|
|
// the issuance. It exists so cert-fetching failures can surface as HTTP
|
|
// 429 responses via [tsweb.HTTPStatuser].
|
|
type certRateLimitedError struct {
|
|
retryAfter time.Duration
|
|
underlying error
|
|
}
|
|
|
|
func (e certRateLimitedError) Error() string { return e.underlying.Error() }
|
|
func (e certRateLimitedError) Unwrap() error { return e.underlying }
|
|
|
|
// HTTPStatus implements [tsweb.HTTPStatuser].
|
|
func (e certRateLimitedError) HTTPStatus() tsweb.HTTPError {
|
|
h := http.Header{}
|
|
if e.retryAfter > 0 {
|
|
h.Set("Retry-After", strconv.Itoa(int(e.retryAfter.Seconds())))
|
|
}
|
|
return tsweb.HTTPError{
|
|
Code: http.StatusTooManyRequests,
|
|
Msg: e.Error(),
|
|
Header: h,
|
|
}
|
|
}
|