@marcel91200 / caddy Mirror · read-only / commits / cc63c580
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

caddyhttp: Support placeholders in header matcher values (close #3916)

Matthew Holt committed Feb 11, 2021 at 16:27 UTC cc63c5805e1587d7e5901deaad3f5bac944a2cf6
2 files changed +28 -3
modules/caddyhttp/matchers.go
+7 -3
@@ -513,7 +513,8 @@ func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
513
514 // Match returns true if r matches m.
515 func (m MatchHeader) Match(r *http.Request) bool {
516 - return matchHeaders(r.Header, http.Header(m), r.Host)
516 + repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
517 + return matchHeaders(r.Header, http.Header(m), r.Host, repl)
518 }
519
520 // getHeaderFieldVals returns the field values for the given fieldName from input.
@@ -530,7 +531,7 @@ func getHeaderFieldVals(input http.Header, fieldName, host string) []string {
531 // matchHeaders returns true if input matches the criteria in against without regex.
532 // The host parameter should be obtained from the http.Request.Host field since
533 // net/http removes it from the header map.
533 -func matchHeaders(input, against http.Header, host string) bool {
534 +func matchHeaders(input, against http.Header, host string, repl *caddy.Replacer) bool {
535 for field, allowedFieldVals := range against {
536 actualFieldVals := getHeaderFieldVals(input, field, host)
537 if allowedFieldVals != nil && len(allowedFieldVals) == 0 && actualFieldVals != nil {
@@ -546,6 +547,9 @@ func matchHeaders(input, against http.Header, host string) bool {
547 fieldVals:
548 for _, actualFieldVal := range actualFieldVals {
549 for _, allowedFieldVal := range allowedFieldVals {
550 + if repl != nil {
551 + allowedFieldVal = repl.ReplaceAll(allowedFieldVal, "")
552 + }
553 switch {
554 case allowedFieldVal == "*":
555 match = true
@@ -985,7 +989,7 @@ func (rm ResponseMatcher) Match(statusCode int, hdr http.Header) bool {
989 if !rm.matchStatusCode(statusCode) {
990 return false
991 }
988 - return matchHeaders(hdr, rm.Headers, "")
992 + return matchHeaders(hdr, rm.Headers, "", nil)
993 }
994
995 func (rm ResponseMatcher) matchStatusCode(statusCode int) bool {
modules/caddyhttp/matchers_test.go
+21
@@ -397,6 +397,9 @@ func TestPathREMatcher(t *testing.T) {
397 }
398
399 func TestHeaderMatcher(t *testing.T) {
400 + repl := caddy.NewReplacer()
401 + repl.Set("a", "foobar")
402 +
403 for i, tc := range []struct {
404 match MatchHeader
405 input http.Header // make sure these are canonical cased (std lib will do that in a real request)
@@ -490,8 +493,26 @@ func TestHeaderMatcher(t *testing.T) {
493 input: http.Header{"Must-Not-Exist": []string{"do not match"}},
494 expect: false,
495 },
496 + {
497 + match: MatchHeader{"Foo": []string{"{a}"}},
498 + input: http.Header{"Foo": []string{"foobar"}},
499 + expect: true,
500 + },
501 + {
502 + match: MatchHeader{"Foo": []string{"{a}"}},
503 + input: http.Header{"Foo": []string{"asdf"}},
504 + expect: false,
505 + },
506 + {
507 + match: MatchHeader{"Foo": []string{"{a}*"}},
508 + input: http.Header{"Foo": []string{"foobar-baz"}},
509 + expect: true,
510 + },
511 } {
512 req := &http.Request{Header: tc.input, Host: tc.host}
513 + ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
514 + req = req.WithContext(ctx)
515 +
516 actual := tc.match.Match(req)
517 if actual != tc.expect {
518 t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input)