@marcel91200 / caddy Mirror · read-only / commits / 30c14084
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: Fixes for header and header_regexp directives (#3061)

* Fix crash when specifying "*" to header directive. Fixes #3060 * Look Host header in header and header_regexp. Also, if more than one header is provided, header_regexp now looks for extra headers values to reflect the behavior from header. Fixes #3059 * Fix parsing of named header_regexp in Caddyfile. See #3059

Gilbert Gilb's committed Feb 20, 2020 at 18:55 UTC 30c14084abda8565215972fdffc5bbc41708b32b
2 files changed +86 -10
modules/caddyhttp/matchers.go
+42 -7
@@ -363,11 +363,24 @@ func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
363 return nil
364 }
365
366 +// Like req.Header.Get(), but that works with Host header.
367 +// go's http module swallows "Host" header.
368 +func getHeader(r *http.Request, field string) []string {
369 + field = textproto.CanonicalMIMEHeaderKey(field)
370 +
371 + if field == "Host" {
372 + return []string{r.Host}
373 + }
374 +
375 + return r.Header[field]
376 +}
377 +
378 // Match returns true if r matches m.
379 func (m MatchHeader) Match(r *http.Request) bool {
380 for field, allowedFieldVals := range m {
369 - actualFieldVals, fieldExists := r.Header[textproto.CanonicalMIMEHeaderKey(field)]
370 - if allowedFieldVals != nil && len(allowedFieldVals) == 0 && fieldExists {
381 + actualFieldVals := getHeader(r, field)
382 +
383 + if allowedFieldVals != nil && len(allowedFieldVals) == 0 && actualFieldVals != nil {
384 // a non-nil but empty list of allowed values means
385 // match if the header field exists at all
386 continue
@@ -377,6 +390,8 @@ func (m MatchHeader) Match(r *http.Request) bool {
390 for _, actualFieldVal := range actualFieldVals {
391 for _, allowedFieldVal := range allowedFieldVals {
392 switch {
393 + case allowedFieldVal == "*":
394 + match = true
395 case strings.HasPrefix(allowedFieldVal, "*") && strings.HasSuffix(allowedFieldVal, "*"):
396 match = strings.Contains(actualFieldVal, allowedFieldVal[1:len(allowedFieldVal)-1])
397 case strings.HasPrefix(allowedFieldVal, "*"):
@@ -412,11 +427,22 @@ func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
427 *m = make(map[string]*MatchRegexp)
428 }
429 for d.Next() {
415 - var field, val string
416 - if !d.Args(&field, &val) {
430 + var first, second, third string
431 + if !d.Args(&first, &second) {
432 return d.ArgErr()
433 }
419 - (*m)[field] = &MatchRegexp{Pattern: val}
434 +
435 + var name, field, val string
436 + if d.Args(&third) {
437 + name = first
438 + field = second
439 + val = third
440 + } else {
441 + field = first
442 + val = second
443 + }
444 +
445 + (*m)[field] = &MatchRegexp{Pattern: val, Name: name}
446 }
447 return nil
448 }
@@ -424,8 +450,17 @@ func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
450 // Match returns true if r matches m.
451 func (m MatchHeaderRE) Match(r *http.Request) bool {
452 for field, rm := range m {
427 - repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
428 - match := rm.Match(r.Header.Get(field), repl)
453 + actualFieldVals := getHeader(r, field)
454 +
455 + match := false
456 + fieldVal:
457 + for _, actualFieldVal := range actualFieldVals {
458 + repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
459 + if rm.Match(actualFieldVal, repl) {
460 + match = true
461 + break fieldVal
462 + }
463 + }
464 if !match {
465 return false
466 }
modules/caddyhttp/matchers_test.go
+44 -3
@@ -375,6 +375,7 @@ func TestHeaderMatcher(t *testing.T) {
375 for i, tc := range []struct {
376 match MatchHeader
377 input http.Header // make sure these are canonical cased (std lib will do that in a real request)
378 + host string
379 expect bool
380 }{
381 {
@@ -417,8 +418,30 @@ func TestHeaderMatcher(t *testing.T) {
418 input: http.Header{"Field1": []string{"foo"}, "Field2": []string{"kapow"}},
419 expect: false,
420 },
421 + {
422 + match: MatchHeader{"field1": []string{"*"}},
423 + input: http.Header{"Field1": []string{"foo"}},
424 + expect: true,
425 + },
426 + {
427 + match: MatchHeader{"field1": []string{"*"}},
428 + input: http.Header{"Field2": []string{"foo"}},
429 + expect: false,
430 + },
431 + {
432 + match: MatchHeader{"host": []string{"localhost"}},
433 + input: http.Header{},
434 + host: "localhost",
435 + expect: true,
436 + },
437 + {
438 + match: MatchHeader{"host": []string{"localhost"}},
439 + input: http.Header{},
440 + host: "caddyserver.com",
441 + expect: false,
442 + },
443 } {
421 - req := &http.Request{Header: tc.input}
444 + req := &http.Request{Header: tc.input, Host: tc.host}
445 actual := tc.match.Match(req)
446 if actual != tc.expect {
447 t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input)
@@ -487,6 +510,7 @@ func TestHeaderREMatcher(t *testing.T) {
510 for i, tc := range []struct {
511 match MatchHeaderRE
512 input http.Header // make sure these are canonical cased (std lib will do that in a real request)
513 + host string
514 expect bool
515 expectRepl map[string]string
516 }{
@@ -506,6 +530,23 @@ func TestHeaderREMatcher(t *testing.T) {
530 expect: true,
531 expectRepl: map[string]string{"name.1": "bar"},
532 },
533 + {
534 + match: MatchHeaderRE{"Field": &MatchRegexp{Pattern: "^foo.*$", Name: "name"}},
535 + input: http.Header{"Field": []string{"barfoo", "foobar"}},
536 + expect: true,
537 + },
538 + {
539 + match: MatchHeaderRE{"host": &MatchRegexp{Pattern: "^localhost$", Name: "name"}},
540 + input: http.Header{},
541 + host: "localhost",
542 + expect: true,
543 + },
544 + {
545 + match: MatchHeaderRE{"host": &MatchRegexp{Pattern: "^local$", Name: "name"}},
546 + input: http.Header{},
547 + host: "localhost",
548 + expect: false,
549 + },
550 } {
551 // compile the regexp and validate its name
552 err := tc.match.Provision(caddy.Context{})
@@ -520,7 +561,7 @@ func TestHeaderREMatcher(t *testing.T) {
561 }
562
563 // set up the fake request and its Replacer
523 - req := &http.Request{Header: tc.input, URL: new(url.URL)}
564 + req := &http.Request{Header: tc.input, URL: new(url.URL), Host: tc.host}
565 repl := caddy.NewReplacer()
566 ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
567 req = req.WithContext(ctx)
@@ -579,7 +620,7 @@ func TestVarREMatcher(t *testing.T) {
620 expect: true,
621 },
622 {
582 - desc: "matching agaist value of var set by the VarsMiddleware and referenced by its placeholder succeeds",
623 + desc: "matching against value of var set by the VarsMiddleware and referenced by its placeholder succeeds",
624 match: MatchVarsRE{"{http.vars.Var1}": &MatchRegexp{Pattern: "[vV]ar[0-9]"}},
625 input: VarsMiddleware{"Var1": "var1Value"},
626 expect: true,