A route may say the largest body it carries
Proxy configuration beside insecure, not a fifth policy — ADR 0108 closed that set at four, and both of these tune how a request is carried rather than deciding what a name admits. A registry is the case that needs it: image layers arrive as single requests of gigabytes and a proxy's own default refuses them long before the workload is reached. Absent is no limit, which is what every route already got. A limit that is not a whole positive number of bytes takes the route with it, named in the log like a port that is not one — serving it without the limit would carry exactly what the module said not to carry. Enforced on the declared length where there is one, and while reading for a chunked body, which declares none: without the second, a limit is advice.
This commit is contained in:
@@ -167,6 +167,16 @@ type rule struct {
|
||||
// webmail front is the first of these — never for anything reached over plain http, where
|
||||
// there is nothing to verify in the first place.
|
||||
insecure bool
|
||||
// maxRequestBody is the largest body, in bytes, this route carries. Zero is no limit, which is
|
||||
// what every route gets by saying nothing: this proxy has never limited a body, and a default
|
||||
// arriving with the field would change every route that never asked for one.
|
||||
//
|
||||
// **Configuration, not a policy** (novox/hq ADR 0108 closed that set at four). It belongs beside
|
||||
// `insecure` for the same reason `insecure` is not a policy: both tune how this proxy carries a
|
||||
// request to a backend, rather than deciding what the name admits or who may reach it. A
|
||||
// registry is the case that needs it — image layers arrive as single requests of gigabytes, and
|
||||
// a proxy's own default refuses them long before the workload is reached.
|
||||
maxRequestBody int64
|
||||
}
|
||||
|
||||
// table is what the proxy is currently serving, replaced whole whenever the file changes.
|
||||
@@ -636,6 +646,18 @@ func handler(held *table) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
if matched.maxRequestBody > 0 {
|
||||
// Refused on the declared length where there is one, so an upload that cannot succeed
|
||||
// is answered before it is carried; and capped while reading for a chunked body, which
|
||||
// declares no length at all. Without the second, a limit is advice.
|
||||
if r.ContentLength > matched.maxRequestBody {
|
||||
http.Error(w, fmt.Sprintf("request body too large for this route: %d bytes is the most it carries",
|
||||
matched.maxRequestBody), http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
r.Body = http.MaxBytesReader(w, r.Body, matched.maxRequestBody)
|
||||
}
|
||||
|
||||
matched.to.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -773,6 +795,18 @@ func routesFrom(path string) (map[string][]rule, map[string]bool, error) {
|
||||
continue
|
||||
}
|
||||
made.insecure, _ = c.Values["insecure"].(bool)
|
||||
// A limit this proxy cannot read is a route it does not serve, named like a port that
|
||||
// is not a port. Serving it without the limit would carry exactly what the module said
|
||||
// not to carry, and report success doing it.
|
||||
if asked, said := c.Values["max-request-body"]; said {
|
||||
bytes, whole := asWhole(asked)
|
||||
if !whole || bytes <= 0 {
|
||||
log.Printf("%s on %s asked for route %q with a max-request-body of %v, which is "+
|
||||
"not a whole positive number of bytes; skipped", c.From, c.Node, name, asked)
|
||||
continue
|
||||
}
|
||||
made.maxRequestBody = int64(bytes)
|
||||
}
|
||||
made.target = fmt.Sprintf("%s://%s:%d", scheme, at, port)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user