mirror of
https://github.com/caddyserver/caddy.git
synced 2024-12-27 14:13:48 +03:00
caddyfile: Improve Dispenser.NextBlock() to support nesting
This commit is contained in:
parent
0cf592fa2e
commit
2459c292a4
14 changed files with 95 additions and 58 deletions
|
@ -123,18 +123,39 @@ func (d *Dispenser) NextLine() bool {
|
||||||
|
|
||||||
// NextBlock can be used as the condition of a for loop
|
// NextBlock can be used as the condition of a for loop
|
||||||
// to load the next token as long as it opens a block or
|
// to load the next token as long as it opens a block or
|
||||||
// is already in a block. It returns true if a token was
|
// is already in a block nested more than initialNestingLevel.
|
||||||
// loaded, or false when the block's closing curly brace
|
// In other words, a loop over NextBlock() will iterate
|
||||||
// was loaded and thus the block ended. Nested blocks are
|
// all tokens in the block assuming the next token is an
|
||||||
// not supported.
|
// open curly brace, until the matching closing brace.
|
||||||
func (d *Dispenser) NextBlock() bool {
|
// The open and closing brace tokens for the outer-most
|
||||||
if d.nesting > 0 {
|
// block will be consumed internally and omitted from
|
||||||
d.Next()
|
// the iteration.
|
||||||
|
//
|
||||||
|
// Proper use of this method looks like this:
|
||||||
|
//
|
||||||
|
// for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// However, in simple cases where it is known that the
|
||||||
|
// Dispenser is new and has not already traversed state
|
||||||
|
// by a loop over NextBlock(), this will do:
|
||||||
|
//
|
||||||
|
// for d.NextBlock(0) {
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// As with other token parsing logic, a loop over
|
||||||
|
// NextBlock() should be contained within a loop over
|
||||||
|
// Next(), as it is usually prudent to skip the initial
|
||||||
|
// token.
|
||||||
|
func (d *Dispenser) NextBlock(initialNestingLevel int) bool {
|
||||||
|
if d.nesting > initialNestingLevel {
|
||||||
|
if !d.Next() {
|
||||||
|
return false // should be EOF error
|
||||||
|
}
|
||||||
if d.Val() == "}" {
|
if d.Val() == "}" {
|
||||||
d.nesting--
|
d.nesting--
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
return true
|
return d.nesting > initialNestingLevel
|
||||||
}
|
}
|
||||||
if !d.nextOnSameLine() { // block must open on same line
|
if !d.nextOnSameLine() { // block must open on same line
|
||||||
return false
|
return false
|
||||||
|
@ -143,19 +164,18 @@ func (d *Dispenser) NextBlock() bool {
|
||||||
d.cursor-- // roll back if not opening brace
|
d.cursor-- // roll back if not opening brace
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
d.Next()
|
d.Next() // consume open curly brace
|
||||||
if d.Val() == "}" {
|
if d.Val() == "}" {
|
||||||
// open and then closed right away
|
return false // open and then closed right away
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
d.nesting++
|
d.nesting++
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nested returns true if the token is currently nested
|
// Nesting returns the current nesting level. Necessary
|
||||||
// inside a block (i.e. an open curly brace was consumed).
|
// if using NextBlock()
|
||||||
func (d *Dispenser) Nested() bool {
|
func (d *Dispenser) Nesting() int {
|
||||||
return d.nesting > 0
|
return d.nesting
|
||||||
}
|
}
|
||||||
|
|
||||||
// Val gets the text of the current token. If there is no token
|
// Val gets the text of the current token. If there is no token
|
||||||
|
@ -230,19 +250,32 @@ func (d *Dispenser) RemainingArgs() []string {
|
||||||
// NewFromNextTokens returns a new dispenser with a copy of
|
// NewFromNextTokens returns a new dispenser with a copy of
|
||||||
// the tokens from the current token until the end of the
|
// the tokens from the current token until the end of the
|
||||||
// "directive" whether that be to the end of the line or
|
// "directive" whether that be to the end of the line or
|
||||||
// the end of a block that starts at the end of the line.
|
// the end of a block that starts at the end of the line;
|
||||||
|
// in other words, until the end of the segment.
|
||||||
func (d *Dispenser) NewFromNextTokens() *Dispenser {
|
func (d *Dispenser) NewFromNextTokens() *Dispenser {
|
||||||
tkns := []Token{d.Token()}
|
tkns := []Token{d.Token()}
|
||||||
for d.NextArg() {
|
for d.NextArg() {
|
||||||
tkns = append(tkns, d.Token())
|
tkns = append(tkns, d.Token())
|
||||||
}
|
}
|
||||||
for d.NextBlock() {
|
var openedBlock bool
|
||||||
for d.Nested() {
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
|
if !openedBlock {
|
||||||
|
// because NextBlock() consumes the initial open
|
||||||
|
// curly brace, we rewind here to append it, since
|
||||||
|
// our case is special in that we want to include
|
||||||
|
// all the tokens including surrounding curly braces
|
||||||
|
// for a new dispenser to have
|
||||||
|
d.Prev()
|
||||||
tkns = append(tkns, d.Token())
|
tkns = append(tkns, d.Token())
|
||||||
d.NextBlock()
|
d.Next()
|
||||||
|
openedBlock = true
|
||||||
}
|
}
|
||||||
|
tkns = append(tkns, d.Token())
|
||||||
|
}
|
||||||
|
if openedBlock {
|
||||||
|
// include closing brace accordingly
|
||||||
|
tkns = append(tkns, d.Token())
|
||||||
}
|
}
|
||||||
tkns = append(tkns, d.Token())
|
|
||||||
return NewDispenser(tkns)
|
return NewDispenser(tkns)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ func TestDispenser_NextBlock(t *testing.T) {
|
||||||
d := newTestDispenser(input)
|
d := newTestDispenser(input)
|
||||||
|
|
||||||
assertNextBlock := func(shouldLoad bool, expectedCursor, expectedNesting int) {
|
assertNextBlock := func(shouldLoad bool, expectedCursor, expectedNesting int) {
|
||||||
if loaded := d.NextBlock(); loaded != shouldLoad {
|
if loaded := d.NextBlock(0); loaded != shouldLoad {
|
||||||
t.Errorf("NextBlock(): Should return %v but got %v", shouldLoad, loaded)
|
t.Errorf("NextBlock(): Should return %v but got %v", shouldLoad, loaded)
|
||||||
}
|
}
|
||||||
if d.cursor != expectedCursor {
|
if d.cursor != expectedCursor {
|
||||||
|
|
|
@ -107,7 +107,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasBlock bool
|
var hasBlock bool
|
||||||
for h.NextBlock() {
|
for h.NextBlock(0) {
|
||||||
hasBlock = true
|
hasBlock = true
|
||||||
|
|
||||||
switch h.Val() {
|
switch h.Val() {
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (st *ServerType) parseMatcherDefinitions(d *caddyfile.Dispenser) (map[strin
|
||||||
matchers := make(map[string]map[string]json.RawMessage)
|
matchers := make(map[string]map[string]json.RawMessage)
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
definitionName := d.Val()
|
definitionName := d.Val()
|
||||||
for d.NextBlock() {
|
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||||
matcherName := d.Val()
|
matcherName := d.Val()
|
||||||
mod, err := caddy.GetModule("http.matchers." + matcherName)
|
mod, err := caddy.GetModule("http.matchers." + matcherName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -60,10 +60,10 @@ func parseHandlerOrder(d *caddyfile.Dispenser) ([]string, error) {
|
||||||
if len(order) == 1 && order[0] == "appearance" {
|
if len(order) == 1 && order[0] == "appearance" {
|
||||||
return []string{"appearance"}, nil
|
return []string{"appearance"}, nil
|
||||||
}
|
}
|
||||||
if len(order) > 0 && d.NextBlock() {
|
if len(order) > 0 && d.NextBlock(0) {
|
||||||
return nil, d.Err("cannot open block if there are arguments")
|
return nil, d.Err("cannot open block if there are arguments")
|
||||||
}
|
}
|
||||||
for d.NextBlock() {
|
for d.NextBlock(0) {
|
||||||
order = append(order, d.Val())
|
order = append(order, d.Val())
|
||||||
if d.NextArg() {
|
if d.NextArg() {
|
||||||
return nil, d.ArgErr()
|
return nil, d.ArgErr()
|
||||||
|
|
|
@ -67,7 +67,7 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)
|
enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
for d.NextBlock() {
|
for d.NextBlock(0) {
|
||||||
name := d.Val()
|
name := d.Val()
|
||||||
mod, err := caddy.GetModule("http.encoders." + name)
|
mod, err := caddy.GetModule("http.encoders." + name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -43,7 +43,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
||||||
return nil, h.ArgErr()
|
return nil, h.ArgErr()
|
||||||
}
|
}
|
||||||
|
|
||||||
for h.NextBlock() {
|
for h.NextBlock(0) {
|
||||||
switch h.Val() {
|
switch h.Val() {
|
||||||
case "hide":
|
case "hide":
|
||||||
fsrv.Hide = h.RemainingArgs()
|
fsrv.Hide = h.RemainingArgs()
|
||||||
|
|
|
@ -69,7 +69,7 @@ func (MatchFile) CaddyModule() caddy.ModuleInfo {
|
||||||
//
|
//
|
||||||
func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
for d.NextBlock() {
|
for d.NextBlock(0) {
|
||||||
switch d.Val() {
|
switch d.Val() {
|
||||||
case "root":
|
case "root":
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
|
|
|
@ -49,7 +49,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if not, they should be in a block
|
// if not, they should be in a block
|
||||||
for h.NextBlock() {
|
for h.NextBlock(0) {
|
||||||
if hasArgs {
|
if hasArgs {
|
||||||
return nil, h.Err("cannot specify headers in both arguments and block")
|
return nil, h.Err("cannot specify headers in both arguments and block")
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,6 +258,9 @@ func (MatchHeader) CaddyModule() caddy.ModuleInfo {
|
||||||
|
|
||||||
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
// UnmarshalCaddyfile implements caddyfile.Unmarshaler.
|
||||||
func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
|
if *m == nil {
|
||||||
|
*m = make(map[string][]string)
|
||||||
|
}
|
||||||
for d.Next() {
|
for d.Next() {
|
||||||
var field, val string
|
var field, val string
|
||||||
if !d.Args(&field, &val) {
|
if !d.Args(&field, &val) {
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for d.NextBlock() {
|
for d.NextBlock(0) {
|
||||||
switch d.Val() {
|
switch d.Val() {
|
||||||
case "to":
|
case "to":
|
||||||
args := d.RemainingArgs()
|
args := d.RemainingArgs()
|
||||||
|
@ -343,7 +343,6 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod.Name)
|
return d.Errf("transport module '%s' is not a Caddyfile unmarshaler", mod.Name)
|
||||||
}
|
}
|
||||||
d.Next() // consume the module name token
|
|
||||||
err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
|
err = unm.UnmarshalCaddyfile(d.NewFromNextTokens())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -377,7 +376,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.NextBlock() {
|
for d.NextBlock(0) {
|
||||||
switch d.Val() {
|
switch d.Val() {
|
||||||
case "read_buffer":
|
case "read_buffer":
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
|
|
|
@ -39,32 +39,34 @@ func init() {
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
for d.NextBlock() {
|
for d.Next() {
|
||||||
switch d.Val() {
|
for d.NextBlock(0) {
|
||||||
case "root":
|
switch d.Val() {
|
||||||
if !d.NextArg() {
|
case "root":
|
||||||
return d.ArgErr()
|
if !d.NextArg() {
|
||||||
}
|
return d.ArgErr()
|
||||||
t.Root = d.Val()
|
}
|
||||||
|
t.Root = d.Val()
|
||||||
|
|
||||||
case "split":
|
case "split":
|
||||||
if !d.NextArg() {
|
if !d.NextArg() {
|
||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
t.SplitPath = d.Val()
|
t.SplitPath = d.Val()
|
||||||
|
|
||||||
case "env":
|
case "env":
|
||||||
args := d.RemainingArgs()
|
args := d.RemainingArgs()
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
return d.ArgErr()
|
return d.ArgErr()
|
||||||
}
|
}
|
||||||
if t.EnvVars == nil {
|
if t.EnvVars == nil {
|
||||||
t.EnvVars = make(map[string]string)
|
t.EnvVars = make(map[string]string)
|
||||||
}
|
}
|
||||||
t.EnvVars[args[0]] = args[1]
|
t.EnvVars[args[0]] = args[1]
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return d.Errf("unrecognized subdirective %s", d.Val())
|
return d.Errf("unrecognized subdirective %s", d.Val())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -57,7 +57,7 @@ func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
|
||||||
if d.Args(&statusCodeStr) {
|
if d.Args(&statusCodeStr) {
|
||||||
s.StatusCode = WeakString(statusCodeStr)
|
s.StatusCode = WeakString(statusCodeStr)
|
||||||
}
|
}
|
||||||
for d.NextBlock() {
|
for d.NextBlock(0) {
|
||||||
switch d.Val() {
|
switch d.Val() {
|
||||||
case "body":
|
case "body":
|
||||||
if s.Body != "" {
|
if s.Body != "" {
|
||||||
|
|
|
@ -34,7 +34,7 @@ func init() {
|
||||||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
|
||||||
t := new(Templates)
|
t := new(Templates)
|
||||||
for h.Next() {
|
for h.Next() {
|
||||||
for h.NextBlock() {
|
for h.NextBlock(0) {
|
||||||
switch h.Val() {
|
switch h.Val() {
|
||||||
case "mime":
|
case "mime":
|
||||||
t.MIMETypes = h.RemainingArgs()
|
t.MIMETypes = h.RemainingArgs()
|
||||||
|
|
Loading…
Reference in a new issue