6cf1317341
- make concurrency limiting the scheduler.Scheduler's responsibility - eliminate the separate concurrency limit middleware - move concurrencyLimit logic into scheduler.FIFO to maintain backwards compatibility - add HTTPError from #834 Updates #834
64 lines
1.7 KiB
Go
64 lines
1.7 KiB
Go
package shared
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
// HTTPError is an error that carries a complete HTTP response. A producer (e.g.
|
|
// a scheduler shedding a request) returns one of these; a renderer (e.g.
|
|
// router.SendError) writes the status, headers, and body verbatim instead of
|
|
// mapping the error to a generic status. It is the seam that lets a component
|
|
// shed a request with a rich response (e.g. a 429 with rate-limit headers and a
|
|
// JSON hint body) without the renderer knowing the producer's internals.
|
|
type HTTPError interface {
|
|
error
|
|
StatusCode() int
|
|
Header() http.Header
|
|
Body() []byte
|
|
}
|
|
|
|
// ConcurrencyLimitError is an HTTPError for a 429 concurrency-limit rejection.
|
|
// Zero-value fields fall back to sensible defaults: a 1-second Retry-After and a
|
|
// JSON hint body.
|
|
type ConcurrencyLimitError struct {
|
|
// RetryAfter, when > 0, is sent as the Retry-After header (in seconds).
|
|
// Defaults to 1.
|
|
RetryAfter int
|
|
|
|
// Message overrides the JSON body's "error" field. Defaults to
|
|
// "Too many requests".
|
|
Message string
|
|
}
|
|
|
|
func (e ConcurrencyLimitError) Error() string { return "concurrency limit reached" }
|
|
|
|
func (e ConcurrencyLimitError) StatusCode() int { return http.StatusTooManyRequests }
|
|
|
|
func (e ConcurrencyLimitError) Header() http.Header {
|
|
h := http.Header{}
|
|
h.Set("Content-Type", "application/json")
|
|
h.Set("Retry-After", e.retryAfter())
|
|
return h
|
|
}
|
|
|
|
func (e ConcurrencyLimitError) Body() []byte {
|
|
b, _ := json.Marshal(map[string]string{"error": e.message()})
|
|
return b
|
|
}
|
|
|
|
func (e ConcurrencyLimitError) retryAfter() string {
|
|
if e.RetryAfter > 0 {
|
|
return strconv.Itoa(e.RetryAfter)
|
|
}
|
|
return "1"
|
|
}
|
|
|
|
func (e ConcurrencyLimitError) message() string {
|
|
if e.Message != "" {
|
|
return e.Message
|
|
}
|
|
return "Too many requests"
|
|
}
|