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()