0
Fork 0
mirror of https://github.com/caddyserver/caddy.git synced 2024-12-23 22:27:38 -05:00
caddy/config/parsing.go

152 lines
3.6 KiB
Go

package config
// This file contains the recursive-descent parsing
// functions.
// begin is the top of the recursive-descent parsing.
// It parses at most one server configuration (an address
// and its directives).
func (p *parser) begin() error {
err := p.address()
if err != nil {
return err
}
err = p.addressBlock()
if err != nil {
return err
}
return nil
}
// address expects that the current token is a host:port
// combination.
func (p *parser) address() error {
if p.tkn() == "}" || p.tkn() == "{" {
return p.err("Syntax", "'"+p.tkn()+"' is not a listening address or EOF")
}
p.cfg.Host, p.cfg.Port = parseAddress(p.tkn())
return nil
}
// addressBlock leads into parsing directives, including
// possible opening/closing curly braces around the block.
// It handles directives enclosed by curly braces and
// directives not enclosed by curly braces.
func (p *parser) addressBlock() error {
if !p.next() {
// file consisted of only an address
return nil
}
err := p.openCurlyBrace()
if err != nil {
// meh, single-server configs don't need curly braces
p.unused = true // we read the token but aren't consuming it
return p.directives()
}
err = p.directives()
if err != nil {
return err
}
err = p.closeCurlyBrace()
if err != nil {
return err
}
return nil
}
// openCurlyBrace expects the current token to be an
// opening curly brace. This acts like an assertion
// because it returns an error if the token is not
// a opening curly brace.
func (p *parser) openCurlyBrace() error {
if p.tkn() != "{" {
return p.syntaxErr("{")
}
return nil
}
// closeCurlyBrace expects the current token to be
// a closing curly brace. This acts like an assertion
// because it returns an error if the token is not
// a closing curly brace.
func (p *parser) closeCurlyBrace() error {
if p.tkn() != "}" {
return p.syntaxErr("}")
}
return nil
}
// directives parses through all the directives
// and it expects the current token to be the first
// directive. It goes until EOF or closing curly
// brace.
func (p *parser) directives() error {
for p.next() {
if p.tkn() == "}" {
// end of address scope
break
}
if fn, ok := validDirectives[p.tkn()]; ok {
err := fn(p)
if err != nil {
return err
}
} else if middlewareRegistered(p.tkn()) {
err := p.collectTokens()
if err != nil {
return err
}
} else {
return p.err("Syntax", "Unexpected token '"+p.tkn()+"', expecting a valid directive")
}
}
return nil
}
// collectTokens consumes tokens until the directive's scope
// closes (either end of line or end of curly brace block).
func (p *parser) collectTokens() error {
directive := p.tkn()
line := p.line()
nesting := 0
breakOk := false
disp := newDispenser(p)
// Re-use a duplicate directive's dispenser from before
// (the parsing logic in the middleware generator must
// account for multiple occurrences of its directive, even
// if that means returning an error or overwriting settings)
if existing, ok := p.other[directive]; ok {
disp = existing
}
// The directive is appended as a relevant token
disp.tokens = append(disp.tokens, p.lexer.token)
for p.next() {
if p.tkn() == "{" {
nesting++
} else if p.line() > line && nesting == 0 {
p.unused = true
breakOk = true
break
} else if p.tkn() == "}" && nesting > 0 {
nesting--
} else if p.tkn() == "}" && nesting == 0 {
return p.err("Syntax", "Unexpected '}' because no matching open curly brace '{'")
}
disp.tokens = append(disp.tokens, p.lexer.token)
}
if !breakOk || nesting > 0 {
return p.eofErr()
}
p.other[directive] = disp
return nil
}