mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	caddyfile: Improve Dispenser.NextBlock() to support nesting
This commit is contained in:
		
							parent
							
								
									0cf592fa2e
								
							
						
					
					
						commit
						2459c292a4
					
				@ -123,18 +123,39 @@ func (d *Dispenser) NextLine() bool {
 | 
			
		||||
 | 
			
		||||
// NextBlock can be used as the condition of a for loop
 | 
			
		||||
// 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
 | 
			
		||||
// loaded, or false when the block's closing curly brace
 | 
			
		||||
// was loaded and thus the block ended. Nested blocks are
 | 
			
		||||
// not supported.
 | 
			
		||||
func (d *Dispenser) NextBlock() bool {
 | 
			
		||||
	if d.nesting > 0 {
 | 
			
		||||
		d.Next()
 | 
			
		||||
// is already in a block nested more than initialNestingLevel.
 | 
			
		||||
// In other words, a loop over NextBlock() will iterate
 | 
			
		||||
// all tokens in the block assuming the next token is an
 | 
			
		||||
// open curly brace, until the matching closing brace.
 | 
			
		||||
// The open and closing brace tokens for the outer-most
 | 
			
		||||
// block will be consumed internally and omitted from
 | 
			
		||||
// 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() == "}" {
 | 
			
		||||
			d.nesting--
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
		return d.nesting > initialNestingLevel
 | 
			
		||||
	}
 | 
			
		||||
	if !d.nextOnSameLine() { // block must open on same line
 | 
			
		||||
		return false
 | 
			
		||||
@ -143,19 +164,18 @@ func (d *Dispenser) NextBlock() bool {
 | 
			
		||||
		d.cursor-- // roll back if not opening brace
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	d.Next()
 | 
			
		||||
	d.Next() // consume open curly brace
 | 
			
		||||
	if d.Val() == "}" {
 | 
			
		||||
		// open and then closed right away
 | 
			
		||||
		return false
 | 
			
		||||
		return false // open and then closed right away
 | 
			
		||||
	}
 | 
			
		||||
	d.nesting++
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Nested returns true if the token is currently nested
 | 
			
		||||
// inside a block (i.e. an open curly brace was consumed).
 | 
			
		||||
func (d *Dispenser) Nested() bool {
 | 
			
		||||
	return d.nesting > 0
 | 
			
		||||
// Nesting returns the current nesting level. Necessary
 | 
			
		||||
// if using NextBlock()
 | 
			
		||||
func (d *Dispenser) Nesting() int {
 | 
			
		||||
	return d.nesting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// the tokens from the current token until the end of the
 | 
			
		||||
// "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 {
 | 
			
		||||
	tkns := []Token{d.Token()}
 | 
			
		||||
	for d.NextArg() {
 | 
			
		||||
		tkns = append(tkns, d.Token())
 | 
			
		||||
	}
 | 
			
		||||
	for d.NextBlock() {
 | 
			
		||||
		for d.Nested() {
 | 
			
		||||
	var openedBlock bool
 | 
			
		||||
	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())
 | 
			
		||||
			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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -148,7 +148,7 @@ func TestDispenser_NextBlock(t *testing.T) {
 | 
			
		||||
	d := newTestDispenser(input)
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
		}
 | 
			
		||||
		if d.cursor != expectedCursor {
 | 
			
		||||
 | 
			
		||||
@ -107,7 +107,7 @@ func parseTLS(h Helper) ([]ConfigValue, error) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var hasBlock bool
 | 
			
		||||
		for h.NextBlock() {
 | 
			
		||||
		for h.NextBlock(0) {
 | 
			
		||||
			hasBlock = true
 | 
			
		||||
 | 
			
		||||
			switch h.Val() {
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ func (st *ServerType) parseMatcherDefinitions(d *caddyfile.Dispenser) (map[strin
 | 
			
		||||
	matchers := make(map[string]map[string]json.RawMessage)
 | 
			
		||||
	for d.Next() {
 | 
			
		||||
		definitionName := d.Val()
 | 
			
		||||
		for d.NextBlock() {
 | 
			
		||||
		for nesting := d.Nesting(); d.NextBlock(nesting); {
 | 
			
		||||
			matcherName := d.Val()
 | 
			
		||||
			mod, err := caddy.GetModule("http.matchers." + matcherName)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -60,10 +60,10 @@ func parseHandlerOrder(d *caddyfile.Dispenser) ([]string, error) {
 | 
			
		||||
	if len(order) == 1 && order[0] == "appearance" {
 | 
			
		||||
		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")
 | 
			
		||||
	}
 | 
			
		||||
	for d.NextBlock() {
 | 
			
		||||
	for d.NextBlock(0) {
 | 
			
		||||
		order = append(order, d.Val())
 | 
			
		||||
		if d.NextArg() {
 | 
			
		||||
			return nil, d.ArgErr()
 | 
			
		||||
 | 
			
		||||
@ -67,7 +67,7 @@ func (enc *Encode) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
			enc.EncodingsRaw[arg] = caddyconfig.JSON(encoding, nil)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for d.NextBlock() {
 | 
			
		||||
		for d.NextBlock(0) {
 | 
			
		||||
			name := d.Val()
 | 
			
		||||
			mod, err := caddy.GetModule("http.encoders." + name)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
 | 
			
		||||
			return nil, h.ArgErr()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for h.NextBlock() {
 | 
			
		||||
		for h.NextBlock(0) {
 | 
			
		||||
			switch h.Val() {
 | 
			
		||||
			case "hide":
 | 
			
		||||
				fsrv.Hide = h.RemainingArgs()
 | 
			
		||||
 | 
			
		||||
@ -69,7 +69,7 @@ func (MatchFile) CaddyModule() caddy.ModuleInfo {
 | 
			
		||||
//
 | 
			
		||||
func (m *MatchFile) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
	for d.Next() {
 | 
			
		||||
		for d.NextBlock() {
 | 
			
		||||
		for d.NextBlock(0) {
 | 
			
		||||
			switch d.Val() {
 | 
			
		||||
			case "root":
 | 
			
		||||
				if !d.NextArg() {
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// if not, they should be in a block
 | 
			
		||||
		for h.NextBlock() {
 | 
			
		||||
		for h.NextBlock(0) {
 | 
			
		||||
			if hasArgs {
 | 
			
		||||
				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.
 | 
			
		||||
func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
	if *m == nil {
 | 
			
		||||
		*m = make(map[string][]string)
 | 
			
		||||
	}
 | 
			
		||||
	for d.Next() {
 | 
			
		||||
		var field, val string
 | 
			
		||||
		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() {
 | 
			
		||||
			case "to":
 | 
			
		||||
				args := d.RemainingArgs()
 | 
			
		||||
@ -343,7 +343,6 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
				if !ok {
 | 
			
		||||
					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())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
@ -377,7 +376,7 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
func (h *HTTPTransport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
	for d.NextBlock() {
 | 
			
		||||
	for d.NextBlock(0) {
 | 
			
		||||
		switch d.Val() {
 | 
			
		||||
		case "read_buffer":
 | 
			
		||||
			if !d.NextArg() {
 | 
			
		||||
 | 
			
		||||
@ -39,32 +39,34 @@ func init() {
 | 
			
		||||
//     }
 | 
			
		||||
//
 | 
			
		||||
func (t *Transport) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
	for d.NextBlock() {
 | 
			
		||||
		switch d.Val() {
 | 
			
		||||
		case "root":
 | 
			
		||||
			if !d.NextArg() {
 | 
			
		||||
				return d.ArgErr()
 | 
			
		||||
			}
 | 
			
		||||
			t.Root = d.Val()
 | 
			
		||||
	for d.Next() {
 | 
			
		||||
		for d.NextBlock(0) {
 | 
			
		||||
			switch d.Val() {
 | 
			
		||||
			case "root":
 | 
			
		||||
				if !d.NextArg() {
 | 
			
		||||
					return d.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				t.Root = d.Val()
 | 
			
		||||
 | 
			
		||||
		case "split":
 | 
			
		||||
			if !d.NextArg() {
 | 
			
		||||
				return d.ArgErr()
 | 
			
		||||
			}
 | 
			
		||||
			t.SplitPath = d.Val()
 | 
			
		||||
			case "split":
 | 
			
		||||
				if !d.NextArg() {
 | 
			
		||||
					return d.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				t.SplitPath = d.Val()
 | 
			
		||||
 | 
			
		||||
		case "env":
 | 
			
		||||
			args := d.RemainingArgs()
 | 
			
		||||
			if len(args) != 2 {
 | 
			
		||||
				return d.ArgErr()
 | 
			
		||||
			}
 | 
			
		||||
			if t.EnvVars == nil {
 | 
			
		||||
				t.EnvVars = make(map[string]string)
 | 
			
		||||
			}
 | 
			
		||||
			t.EnvVars[args[0]] = args[1]
 | 
			
		||||
			case "env":
 | 
			
		||||
				args := d.RemainingArgs()
 | 
			
		||||
				if len(args) != 2 {
 | 
			
		||||
					return d.ArgErr()
 | 
			
		||||
				}
 | 
			
		||||
				if t.EnvVars == nil {
 | 
			
		||||
					t.EnvVars = make(map[string]string)
 | 
			
		||||
				}
 | 
			
		||||
				t.EnvVars[args[0]] = args[1]
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return d.Errf("unrecognized subdirective %s", d.Val())
 | 
			
		||||
			default:
 | 
			
		||||
				return d.Errf("unrecognized subdirective %s", d.Val())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ func (s *StaticResponse) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
 | 
			
		||||
		if d.Args(&statusCodeStr) {
 | 
			
		||||
			s.StatusCode = WeakString(statusCodeStr)
 | 
			
		||||
		}
 | 
			
		||||
		for d.NextBlock() {
 | 
			
		||||
		for d.NextBlock(0) {
 | 
			
		||||
			switch d.Val() {
 | 
			
		||||
			case "body":
 | 
			
		||||
				if s.Body != "" {
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ func init() {
 | 
			
		||||
func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) {
 | 
			
		||||
	t := new(Templates)
 | 
			
		||||
	for h.Next() {
 | 
			
		||||
		for h.NextBlock() {
 | 
			
		||||
		for h.NextBlock(0) {
 | 
			
		||||
			switch h.Val() {
 | 
			
		||||
			case "mime":
 | 
			
		||||
				t.MIMETypes = h.RemainingArgs()
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user