@marcel91200 / caddy Mirror · read-only / commits / 2459c292
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

caddyfile: Improve Dispenser.NextBlock() to support nesting

Matthew Holt committed Sep 10, 2019 at 19:21 UTC 2459c292a4d6fb0552eb9be3cecd955093ed853b
14 files changed +97 -60
caddyconfig/caddyfile/dispenser.go
+54 -21
@@ -123,18 +123,39 @@ func (d *Dispenser) NextLine() bool {
123
124 // NextBlock can be used as the condition of a for loop
125 // to load the next token as long as it opens a block or
126 -// is already in a block. It returns true if a token was
127 -// loaded, or false when the block's closing curly brace
128 -// was loaded and thus the block ended. Nested blocks are
129 -// not supported.
130 -func (d *Dispenser) NextBlock() bool {
131 - if d.nesting > 0 {
132 - d.Next()
126 +// is already in a block nested more than initialNestingLevel.
127 +// In other words, a loop over NextBlock() will iterate
128 +// all tokens in the block assuming the next token is an
129 +// open curly brace, until the matching closing brace.
130 +// The open and closing brace tokens for the outer-most
131 +// block will be consumed internally and omitted from
132 +// the iteration.
133 +//
134 +// Proper use of this method looks like this:
135 +//
136 +// for nesting := d.Nesting(); d.NextBlock(nesting); {
137 +// }
138 +//
139 +// However, in simple cases where it is known that the
140 +// Dispenser is new and has not already traversed state
141 +// by a loop over NextBlock(), this will do:
142 +//
143 +// for d.NextBlock(0) {
144 +// }
145 +//
146 +// As with other token parsing logic, a loop over
147 +// NextBlock() should be contained within a loop over
148 +// Next(), as it is usually prudent to skip the initial
149 +// token.
150 +func (d *Dispenser) NextBlock(initialNestingLevel int) bool {
151 + if d.nesting > initialNestingLevel {
152 + if !d.Next() {
153 + return false // should be EOF error
154 + }
155 if d.Val() == "}" {
156 d.nesting--
135 - return false
157 }
137 - return true
158 + return d.nesting > initialNestingLevel
159 }
160 if !d.nextOnSameLine() { // block must open on same line
161 return false
@@ -143,19 +164,18 @@ func (d *Dispenser) NextBlock() bool {
164 d.cursor-- // roll back if not opening brace
165 return false
166 }
146 - d.Next()
167 + d.Next() // consume open curly brace
168 if d.Val() == "}" {
148 - // open and then closed right away
149 - return false
169 + return false // open and then closed right away
170 }
171 d.nesting++
172 return true
173 }
174
155 -// Nested returns true if the token is currently nested
156 -// inside a block (i.e. an open curly brace was consumed).
157 -func (d *Dispenser) Nested() bool {
158 - return d.nesting > 0
175 +// Nesting returns the current nesting level. Necessary
176 +// if using NextBlock()
177 +func (d *Dispenser) Nesting() int {
178 + return d.nesting
179 }
180
181 // Val gets the text of the current token. If there is no token
@@ -230,19 +250,32 @@ func (d *Dispenser) RemainingArgs() []string {
250 // NewFromNextTokens returns a new dispenser with a copy of
251 // the tokens from the current token until the end of the
252 // "directive" whether that be to the end of the line or
233 -// the end of a block that starts at the end of the line.
253 +// the end of a block that starts at the end of the line;
254 +// in other words, until the end of the segment.
255 func (d *Dispenser) NewFromNextTokens() *Dispenser {
256 tkns := []Token{d.Token()}
257 for d.NextArg() {
258 tkns = append(tkns, d.Token())
259 }
239 - for d.NextBlock() {
240 - for d.Nested() {
260 + var openedBlock bool
261 + for nesting := d.Nesting(); d.NextBlock(nesting); {
262 + if !openedBlock {
263 + // because NextBlock() consumes the initial open
264 + // curly brace, we rewind here to append it, since
265 + // our case is special in that we want to include
266 + // all the tokens including surrounding curly braces
267 + // for a new dispenser to have
268 + d.Prev()
269 tkns = append(tkns, d.Token())
242 - d.NextBlock()
270 + d.Next()
271 + openedBlock = true
272 }
273 + tkns = append(tkns, d.Token())
274 + }
275 + if openedBlock {
276 + // include closing brace accordingly
277 + tkns = append(tkns, d.Token())
278 }
245 - tkns = append(tkns, d.Token())
279 return NewDispenser(tkns)
280 }
281
caddyconfig/caddyfile/dispenser_test.go
+1 -1
@@ -148,7 +148,7 @@ func TestDispenser_NextBlock(t *testing.T) {
148 d := newTestDispenser(input)
149
150 assertNextBlock := func(shouldLoad bool, expectedCursor, expectedNesting int) {
151 - if loaded := d.NextBlock(); loaded != shouldLoad {
151 + if loaded := d.NextBlock(0); loaded != shouldLoad {
152 t.Errorf("NextBlock(): Should return %v but got %v", shouldLoad, loaded)
153 }
154 if d.cursor != expectedCursor {
caddyconfig/httpcaddyfile/builtins.go
+1 -1
@@ -107,7 +107,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
107 }
108
109 var hasBlock bool
110 - for h.NextBlock() {
110 + for h.NextBlock(0) {
111 hasBlock = true
112
113 switch h.Val() {
caddyconfig/httpcaddyfile/handlers.go
+1 -1
@@ -28,7 +28,7 @@ func (st *ServerType) parseMatcherDefinitions(d *caddyfile.Dispenser) (map[strin
28 matchers := make(map[string]map[string]json.RawMessage)
29 for d.Next() {
30 definitionName := d.Val()
31 - for d.NextBlock() {
31 + for nesting := d.Nesting(); d.NextBlock(nesting); {
32 matcherName := d.Val()
33 mod, err := caddy.GetModule("http.matchers." + matcherName)
34 if err != nil {
caddyconfig/httpcaddyfile/options.go
+2 -2
@@ -60,10 +60,10 @@ func parseHandlerOrder(d *caddyfile.Dispenser) ([]string, error) {
60 if len(order) == 1 && order[0] == "appearance" {
61 return []string{"appearance"}, nil
62 }
63 - if len(order) > 0 && d.NextBlock() {
63 + if len(order) > 0 && d.NextBlock(0) {
64 return nil, d.Err("cannot open block if there are arguments")
65 }
66 - for d.NextBlock() {
66 + for d.NextBlock(0) {
67 order = append(order, d.Val())
68 if d.NextArg() {
69 return nil, d.ArgErr()
modules/caddyhttp/encode/caddyfile.go
+1 -1
@@ -67,7 +67,7 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
67 enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)
68 }
69
70 - for d.NextBlock() {
70 + for d.NextBlock(0) {
71 name := d.Val()
72 mod, err := caddy.GetModule("http.encoders." + name)
73 if err != nil {
modules/caddyhttp/fileserver/caddyfile.go
+1 -1
@@ -43,7 +43,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
43 return nil, h.ArgErr()
44 }
45
46 - for h.NextBlock() {
46 + for h.NextBlock(0) {
47 switch h.Val() {
48 case "hide":
49 fsrv.Hide = h.RemainingArgs()
modules/caddyhttp/fileserver/matcher.go
+1 -1
@@ -69,7 +69,7 @@ func (MatchFile) CaddyModule() caddy.ModuleInfo {
69 //
70 func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
71 for d.Next() {
72 - for d.NextBlock() {
72 + for d.NextBlock(0) {
73 switch d.Val() {
74 case "root":
75 if !d.NextArg() {
modules/caddyhttp/headers/caddyfile.go
+1 -1
@@ -49,7 +49,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
49 }
50
51 // if not, they should be in a block
52 - for h.NextBlock() {
52 + for h.NextBlock(0) {
53 if hasArgs {
54 return nil, h.Err("cannot specify headers in both arguments and block")
55 }
modules/caddyhttp/matchers.go
+3
@@ -258,6 +258,9 @@ func (MatchHeader) CaddyModule() caddy.ModuleInfo {
258
259 // UnmarshalCaddyfile implements caddyfile.Unmarshaler.
260 func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
261 + if *m == nil {
262 + *m = make(map[string][]string)
263 + }
264 for d.Next() {
265 var field, val string
266 if !d.Args(&field, &val) {
modules/caddyhttp/reverseproxy/caddyfile.go
+2 -3
@@ -81,7 +81,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
81 })
82 }
83
84 - for d.NextBlock() {
84 + for d.NextBlock(0) {
85 switch d.Val() {
86 case "to":
87 args := d.RemainingArgs()
@@ -343,7 +343,6 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
343 if !ok {
344 return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod.Name)
345 }
346 - d.Next() // consume the module name token
346 err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
347 if err != nil {
348 return err
@@ -377,7 +376,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
376 // }
377 //
378 func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
380 - for d.NextBlock() {
379 + for d.NextBlock(0) {
380 switch d.Val() {
381 case "read_buffer":
382 if !d.NextArg() {
modules/caddyhttp/reverseproxy/fastcgi/caddyfile.go
+27 -25
@@ -39,32 +39,34 @@ func init() {
39 // }
40 //
41 func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
42 - for d.NextBlock() {
43 - switch d.Val() {
44 - case "root":
45 - if !d.NextArg() {
46 - return d.ArgErr()
42 + for d.Next() {
43 + for d.NextBlock(0) {
44 + switch d.Val() {
45 + case "root":
46 + if !d.NextArg() {
47 + return d.ArgErr()
48 + }
49 + t.Root = d.Val()
50 +
51 + case "split":
52 + if !d.NextArg() {
53 + return d.ArgErr()
54 + }
55 + t.SplitPath = d.Val()
56 +
57 + case "env":
58 + args := d.RemainingArgs()
59 + if len(args) != 2 {
60 + return d.ArgErr()
61 + }
62 + if t.EnvVars == nil {
63 + t.EnvVars = make(map[string]string)
64 + }
65 + t.EnvVars[args[0]] = args[1]
66 +
67 + default:
68 + return d.Errf("unrecognized subdirective %s", d.Val())
69 }
48 - t.Root = d.Val()
49 -
50 - case "split":
51 - if !d.NextArg() {
52 - return d.ArgErr()
53 - }
54 - t.SplitPath = d.Val()
55 -
56 - case "env":
57 - args := d.RemainingArgs()
58 - if len(args) != 2 {
59 - return d.ArgErr()
60 - }
61 - if t.EnvVars == nil {
62 - t.EnvVars = make(map[string]string)
63 - }
64 - t.EnvVars[args[0]] = args[1]
65 -
66 - default:
67 - return d.Errf("unrecognized subdirective %s", d.Val())
70 }
71 }
72 return nil
modules/caddyhttp/staticresp.go
+1 -1
@@ -57,7 +57,7 @@ func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
57 if d.Args(&statusCodeStr) {
58 s.StatusCode = WeakString(statusCodeStr)
59 }
60 - for d.NextBlock() {
60 + for d.NextBlock(0) {
61 switch d.Val() {
62 case "body":
63 if s.Body != "" {
modules/caddyhttp/templates/caddyfile.go
+1 -1
@@ -34,7 +34,7 @@ func init() {
34 func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
35 t := new(Templates)
36 for h.Next() {
37 - for h.NextBlock() {
37 + for h.NextBlock(0) {
38 switch h.Val() {
39 case "mime":
40 t.MIMETypes = h.RemainingArgs()