internal/server/strip_response_headers_form_test.go
Rama main · réplica de lectura del repositorio original.
// SPDX-License-Identifier: Apache-2.0
package server
import (
"net/http"
"net/url"
"strings"
"testing"
"github.com/X4Applegate/caddyui/internal/models"
)
// The host form used to show two "Strip response headers" inputs bound to two
// different columns — the response-side twin of issue #167. The duplicate input
// is gone and strip_resp_headers is canonical, but the legacy
// strip_response_headers POST value is still accepted for one release so an old
// bookmarked form or an API client posting the v2.9.168 name does not silently
// lose data.
func TestParseProxyHostFormConsolidatesStripResponseHeaders(t *testing.T) {
cases := []struct {
name string
form url.Values
want string
reason string
}{
{
name: "canonical field alone",
form: url.Values{"strip_resp_headers": {"X-Powered-By, Server"}},
want: "X-Powered-By, Server",
},
{
name: "legacy field alone is still accepted",
form: url.Values{"strip_response_headers": {"X-Legacy-Only"}},
want: "X-Legacy-Only",
reason: "an old bookmarked form must not silently drop the value",
},
{
name: "both posted are merged, not duplicated",
form: url.Values{
"strip_resp_headers": {"Server"},
"strip_response_headers": {"X-Powered-By"},
},
want: "Server, X-Powered-By",
reason: "neither value may be discarded",
},
{
name: "same name in both collapses",
form: url.Values{
"strip_resp_headers": {"X-Powered-By"},
"strip_response_headers": {"x-powered-by"},
},
want: "X-Powered-By",
reason: "HTTP header names are case-insensitive",
},
{
name: "neither posted stays empty",
form: url.Values{},
want: "",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
form := url.Values{}
for k, v := range tc.form {
form[k] = v
}
// parseProxyHostForm needs a minimally valid host.
form.Set("domains", "example.com")
form.Set("forward_scheme", "http")
form.Set("forward_host", "backend")
form.Set("forward_port", "8080")
req := httptestPostForm(t, form)
ph, err := parseProxyHostForm(req)
if err != nil {
t.Fatalf("parseProxyHostForm: %v", err)
}
if ph.StripRespHeaders != tc.want {
msg := ""
if tc.reason != "" {
msg = " (" + tc.reason + ")"
}
t.Errorf("StripRespHeaders = %q, want %q%s",
ph.StripRespHeaders, tc.want, msg)
}
// The legacy column must never be written again.
if ph.StripResponseHeaders != "" {
t.Errorf("legacy StripResponseHeaders = %q, want empty",
ph.StripResponseHeaders)
}
})
}
}
func httptestPostForm(t *testing.T, form url.Values) *http.Request {
t.Helper()
req, err := http.NewRequest(http.MethodPost, "/hosts", strings.NewReader(form.Encode()))
if err != nil {
t.Fatalf("new request: %v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
return req
}
// The global "Globally stripped response headers" setting applied itself to
// every host, and it used to read the legacy column. After the consolidation it
// must apply to the canonical field, prepending the global names and
// deduplicating against the per-host list.
func TestGlobalStripResponseHeadersAppliesToCanonicalField(t *testing.T) {
cases := []struct {
name string
global string
perHost string
want string
whyNotes string
}{
{
name: "per-host empty takes the global list",
global: "Server",
perHost: "",
want: "Server",
},
{
name: "global applied ahead of per-host",
global: "Server",
perHost: "X-Powered-By",
want: "Server, X-Powered-By",
whyNotes: "both must be stripped",
},
{
name: "overlap is deduplicated",
global: "Server, X-Powered-By",
perHost: "X-Powered-By",
want: "Server, X-Powered-By",
whyNotes: "a header named in both used to be emitted twice",
},
{
name: "overlap dedupe is case-insensitive",
global: "X-Powered-By",
perHost: "x-powered-by",
want: "X-Powered-By",
whyNotes: "HTTP header names are case-insensitive",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := models.MergeHeaderNameLists(tc.global, tc.perHost)
if got != tc.want {
msg := ""
if tc.whyNotes != "" {
msg = " (" + tc.whyNotes + ")"
}
t.Errorf("merge(global=%q, perHost=%q) = %q, want %q%s",
tc.global, tc.perHost, got, tc.want, msg)
}
})
}
}