@marcel91200 / caddy Mirror · read-only
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 11, 01:00 UTC

master
go 119 lines 3.3 KB
Raw
1 // Copyright 2015 Matthew Holt and The Caddy Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package caddyhttp
16
17 import (
18 "net/http"
19 "strconv"
20 "strings"
21
22 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
23 )
24
25 // ResponseMatcher is a type which can determine if an
26 // HTTP response matches some criteria.
27 type ResponseMatcher struct {
28 // If set, one of these status codes would be required.
29 // A one-digit status can be used to represent all codes
30 // in that class (e.g. 3 for all 3xx codes).
31 StatusCode []int `json:"status_code,omitempty"`
32
33 // If set, each header specified must be one of the
34 // specified values, with the same logic used by the
35 // [request header matcher](/docs/json/apps/http/servers/routes/match/header/).
36 Headers http.Header `json:"headers,omitempty"`
37 }
38
39 // Match returns true if the given statusCode and hdr match rm.
40 func (rm ResponseMatcher) Match(statusCode int, hdr http.Header) bool {
41 if !rm.matchStatusCode(statusCode) {
42 return false
43 }
44 return matchHeaders(hdr, rm.Headers, "", []string{}, nil)
45 }
46
47 func (rm ResponseMatcher) matchStatusCode(statusCode int) bool {
48 if rm.StatusCode == nil {
49 return true
50 }
51 for _, code := range rm.StatusCode {
52 if StatusCodeMatches(statusCode, code) {
53 return true
54 }
55 }
56 return false
57 }
58
59 // ParseNamedResponseMatcher parses the tokens of a named response matcher.
60 //
61 // @name {
62 // header <field> [<value>]
63 // status <code...>
64 // }
65 //
66 // Or, single line syntax:
67 //
68 // @name [header <field> [<value>]] | [status <code...>]
69 func ParseNamedResponseMatcher(d *caddyfile.Dispenser, matchers map[string]ResponseMatcher) error {
70 d.Next() // consume matcher name
71 definitionName := d.Val()
72
73 if _, ok := matchers[definitionName]; ok {
74 return d.Errf("matcher is defined more than once: %s", definitionName)
75 }
76
77 matcher := ResponseMatcher{}
78 for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
79 switch d.Val() {
80 case "header":
81 if matcher.Headers == nil {
82 matcher.Headers = http.Header{}
83 }
84
85 // reuse the header request matcher's unmarshaler
86 headerMatcher := MatchHeader(matcher.Headers)
87 err := headerMatcher.UnmarshalCaddyfile(d.NewFromNextSegment())
88 if err != nil {
89 return err
90 }
91
92 matcher.Headers = http.Header(headerMatcher)
93 case "status":
94 if matcher.StatusCode == nil {
95 matcher.StatusCode = []int{}
96 }
97
98 args := d.RemainingArgs()
99 if len(args) == 0 {
100 return d.ArgErr()
101 }
102
103 for _, arg := range args {
104 if len(arg) == 3 && strings.HasSuffix(arg, "xx") {
105 arg = arg[:1]
106 }
107 statusNum, err := strconv.Atoi(arg)
108 if err != nil {
109 return d.Errf("bad status value '%s': %v", arg, err)
110 }
111 matcher.StatusCode = append(matcher.StatusCode, statusNum)
112 }
113 default:
114 return d.Errf("unrecognized response matcher %s", d.Val())
115 }
116 }
117 matchers[definitionName] = matcher
118 return nil
119 }