mirror of
https://github.com/caddyserver/caddy.git
synced 2026-10-06 12:51:55 -04:00
when a request exceeds the request_body max_size limit, the request_body handler wraps the http.MaxBytesError into a caddyhttp.HandlerError carrying status 413, but the {http.request.body} and {http.request.body_base64} placeholders ran io.Copy with the error ignored, so they silently returned the truncated prefix as though it were the complete body. docs promise a 413 for reads past max_size, and silently truncating is a bad failure mode in templates and vars_regexp where the body value drives decisions. see #7691 and the narrow follow-up prescribed when #7692 was closed
reading the body now returns a dedicated RequestBodyLimitError marker instead of a generic HandlerError, and only when the read failure is actually the max_size limit. the consumers (template placeholder function, vars and vars_regexp matchers) recognize exactly that marker and wrap it in a status carrying handler error so the oversized request fails with 413, while every unrelated error value keeps its old behavior: templates still render it as text and the vars matchers still match on its error text. the surfaced error is stripped of its generated id and stack trace so a placeholder stringified into a response body cannot leak the call stack
negative regressions prove an unrelated HandlerError in templates, vars and vars_regexp does not start controlling request handling, plus integration tests for the template and vars_regexp 413 paths
ai assisted (GLM agent) under Abdel's direction and local verification
Signed-off-by: Abdel <hktitof@gmail.com>
143 lines
3.2 KiB
Go
143 lines
3.2 KiB
Go
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/caddyserver/caddy/v2/caddytest"
|
|
)
|
|
|
|
func TestBrowse(t *testing.T) {
|
|
tester := caddytest.NewTester(t)
|
|
tester.InitServer(`
|
|
{
|
|
skip_install_trust
|
|
admin localhost:2999
|
|
http_port 9080
|
|
https_port 9443
|
|
grace_period 1ns
|
|
}
|
|
http://localhost:9080 {
|
|
file_server browse
|
|
}
|
|
`, "caddyfile")
|
|
|
|
req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
|
|
if err != nil {
|
|
t.Fail()
|
|
return
|
|
}
|
|
tester.AssertResponseCode(req, 200)
|
|
}
|
|
|
|
func TestRespondWithJSON(t *testing.T) {
|
|
tester := caddytest.NewTester(t)
|
|
tester.InitServer(`
|
|
{
|
|
skip_install_trust
|
|
admin localhost:2999
|
|
http_port 9080
|
|
https_port 9443
|
|
grace_period 1ns
|
|
}
|
|
localhost {
|
|
respond {http.request.body}
|
|
}
|
|
`, "caddyfile")
|
|
|
|
res, _ := tester.AssertPostResponseBody("https://localhost:9443/",
|
|
nil,
|
|
bytes.NewBufferString(`{
|
|
"greeting": "Hello, world!"
|
|
}`), 200, `{
|
|
"greeting": "Hello, world!"
|
|
}`)
|
|
if res.Header.Get("Content-Type") != "application/json" {
|
|
t.Errorf("expected Content-Type to be application/json, but was %s", res.Header.Get("Content-Type"))
|
|
}
|
|
}
|
|
|
|
func TestRequestBodyPlaceholderRespectsMaxSizeInTemplate(t *testing.T) {
|
|
tester := caddytest.NewTester(t)
|
|
tester.InitServer(`
|
|
{
|
|
skip_install_trust
|
|
admin localhost:2999
|
|
http_port 9080
|
|
https_port 9443
|
|
grace_period 1ns
|
|
}
|
|
http://localhost:9080 {
|
|
request_body {
|
|
max_size 10
|
|
}
|
|
respond "{{placeholder \"http.request.body\"}}"
|
|
templates
|
|
}
|
|
`, "caddyfile")
|
|
|
|
req, err := http.NewRequest(http.MethodPost, "http://localhost:9080/", bytes.NewBufferString("abcdefghijklm"))
|
|
if err != nil {
|
|
t.Fatalf("creating request: %v", err)
|
|
}
|
|
res := tester.AssertResponseCode(req, http.StatusRequestEntityTooLarge)
|
|
res.Body.Close()
|
|
}
|
|
|
|
func TestRequestBodyPlaceholderRespectsMaxSizeInVarsRegexp(t *testing.T) {
|
|
tester := caddytest.NewTester(t)
|
|
tester.InitServer(`
|
|
{
|
|
skip_install_trust
|
|
admin localhost:2999
|
|
http_port 9080
|
|
https_port 9443
|
|
grace_period 1ns
|
|
}
|
|
http://localhost:9080 {
|
|
request_body {
|
|
max_size 10
|
|
}
|
|
@bigbody {
|
|
vars_regexp {http.request.body} "^.{20}"
|
|
}
|
|
handle @bigbody {
|
|
respond "the body was too long matched"
|
|
}
|
|
respond "no match"
|
|
}
|
|
`, "caddyfile")
|
|
|
|
req, err := http.NewRequest(http.MethodPost, "http://localhost:9080/", bytes.NewBufferString("abcdefghijklmnopqrstuvwxyz"))
|
|
if err != nil {
|
|
t.Fatalf("creating request: %v", err)
|
|
}
|
|
res := tester.AssertResponseCode(req, http.StatusRequestEntityTooLarge)
|
|
res.Body.Close()
|
|
}
|
|
|
|
func TestRequestBodyPlaceholderDirectRespondExpansion(t *testing.T) {
|
|
tester := caddytest.NewTester(t)
|
|
tester.InitServer(`
|
|
{
|
|
skip_install_trust
|
|
admin localhost:2999
|
|
http_port 9080
|
|
https_port 9443
|
|
grace_period 1ns
|
|
}
|
|
http://localhost:9080 {
|
|
request_body {
|
|
max_size 10
|
|
}
|
|
respond "{http.request.body}"
|
|
}
|
|
`, "caddyfile")
|
|
|
|
// direct placeholder consumers expand the sanitized request-body limit
|
|
// marker to its text instead of failing the request with 413; only the
|
|
// templates and vars matchers propagate the 413 status
|
|
tester.AssertPostResponseBody("http://localhost:9080/", nil, bytes.NewBufferString("abcdefghijklm"), http.StatusOK, "request body: http: request body too large")
|
|
}
|