mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	Fixed import command, added tests
This commit is contained in:
		
							parent
							
								
									c82d7c2dd2
								
							
						
					
					
						commit
						d3c229375c
					
				@ -37,7 +37,7 @@ func NewDispenserTokens(filename string, tokens []token) Dispenser {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Next loads the next token. Returns true if a token
 | 
					// Next loads the next token. Returns true if a token
 | 
				
			||||||
// was loaded; false otherwise. If false, all tokens
 | 
					// was loaded; false otherwise. If false, all tokens
 | 
				
			||||||
// have already been consumed.
 | 
					// have been consumed.
 | 
				
			||||||
func (d *Dispenser) Next() bool {
 | 
					func (d *Dispenser) Next() bool {
 | 
				
			||||||
	if d.cursor < len(d.tokens)-1 {
 | 
						if d.cursor < len(d.tokens)-1 {
 | 
				
			||||||
		d.cursor++
 | 
							d.cursor++
 | 
				
			||||||
@ -49,7 +49,7 @@ func (d *Dispenser) Next() bool {
 | 
				
			|||||||
// NextArg loads the next token if it is on the same
 | 
					// NextArg loads the next token if it is on the same
 | 
				
			||||||
// line. Returns true if a token was loaded; false
 | 
					// line. Returns true if a token was loaded; false
 | 
				
			||||||
// otherwise. If false, all tokens on the line have
 | 
					// otherwise. If false, all tokens on the line have
 | 
				
			||||||
// been consumed.
 | 
					// been consumed. It handles imported tokens correctly.
 | 
				
			||||||
func (d *Dispenser) NextArg() bool {
 | 
					func (d *Dispenser) NextArg() bool {
 | 
				
			||||||
	if d.cursor < 0 {
 | 
						if d.cursor < 0 {
 | 
				
			||||||
		d.cursor++
 | 
							d.cursor++
 | 
				
			||||||
@ -59,7 +59,8 @@ func (d *Dispenser) NextArg() bool {
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if d.cursor < len(d.tokens)-1 &&
 | 
						if d.cursor < len(d.tokens)-1 &&
 | 
				
			||||||
		(d.tokens[d.cursor].line+d.numLineBreaks(d.cursor) == d.tokens[d.cursor+1].line) {
 | 
							d.tokens[d.cursor].file == d.tokens[d.cursor+1].file &&
 | 
				
			||||||
 | 
							d.tokens[d.cursor].line+d.numLineBreaks(d.cursor) == d.tokens[d.cursor+1].line {
 | 
				
			||||||
		d.cursor++
 | 
							d.cursor++
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -69,7 +70,7 @@ func (d *Dispenser) NextArg() bool {
 | 
				
			|||||||
// NextLine loads the next token only if it is not on the same
 | 
					// NextLine loads the next token only if it is not on the same
 | 
				
			||||||
// line as the current token, and returns true if a token was
 | 
					// line as the current token, and returns true if a token was
 | 
				
			||||||
// loaded; false otherwise. If false, there is not another token
 | 
					// loaded; false otherwise. If false, there is not another token
 | 
				
			||||||
// or it is on the same line.
 | 
					// or it is on the same line. It handles imported tokens correctly.
 | 
				
			||||||
func (d *Dispenser) NextLine() bool {
 | 
					func (d *Dispenser) NextLine() bool {
 | 
				
			||||||
	if d.cursor < 0 {
 | 
						if d.cursor < 0 {
 | 
				
			||||||
		d.cursor++
 | 
							d.cursor++
 | 
				
			||||||
@ -79,7 +80,8 @@ func (d *Dispenser) NextLine() bool {
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if d.cursor < len(d.tokens)-1 &&
 | 
						if d.cursor < len(d.tokens)-1 &&
 | 
				
			||||||
		d.tokens[d.cursor].line+d.numLineBreaks(d.cursor) < d.tokens[d.cursor+1].line {
 | 
							(d.tokens[d.cursor].file != d.tokens[d.cursor+1].file ||
 | 
				
			||||||
 | 
								d.tokens[d.cursor].line+d.numLineBreaks(d.cursor) < d.tokens[d.cursor+1].line) {
 | 
				
			||||||
		d.cursor++
 | 
							d.cursor++
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -135,6 +137,18 @@ func (d *Dispenser) Line() int {
 | 
				
			|||||||
	return d.tokens[d.cursor].line
 | 
						return d.tokens[d.cursor].line
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// File gets the filename of the current token. If there is no token loaded,
 | 
				
			||||||
 | 
					// it returns the filename originally given when parsing started.
 | 
				
			||||||
 | 
					func (d *Dispenser) File() string {
 | 
				
			||||||
 | 
						if d.cursor < 0 || d.cursor >= len(d.tokens) {
 | 
				
			||||||
 | 
							return d.filename
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if tokenFilename := d.tokens[d.cursor].file; tokenFilename != "" {
 | 
				
			||||||
 | 
							return tokenFilename
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.filename
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Args is a convenience function that loads the next arguments
 | 
					// Args is a convenience function that loads the next arguments
 | 
				
			||||||
// (tokens on the same line) into an arbitrary number of strings
 | 
					// (tokens on the same line) into an arbitrary number of strings
 | 
				
			||||||
// pointed to in targets. If there are fewer tokens available
 | 
					// pointed to in targets. If there are fewer tokens available
 | 
				
			||||||
@ -185,7 +199,7 @@ func (d *Dispenser) ArgErr() error {
 | 
				
			|||||||
// SyntaxErr creates a generic syntax error which explains what was
 | 
					// SyntaxErr creates a generic syntax error which explains what was
 | 
				
			||||||
// found and what was expected.
 | 
					// found and what was expected.
 | 
				
			||||||
func (d *Dispenser) SyntaxErr(expected string) error {
 | 
					func (d *Dispenser) SyntaxErr(expected string) error {
 | 
				
			||||||
	msg := fmt.Sprintf("%s:%d - Syntax error: Unexpected token '%s', expecting '%s'", d.filename, d.Line(), d.Val(), expected)
 | 
						msg := fmt.Sprintf("%s:%d - Syntax error: Unexpected token '%s', expecting '%s'", d.File(), d.Line(), d.Val(), expected)
 | 
				
			||||||
	return errors.New(msg)
 | 
						return errors.New(msg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -197,7 +211,7 @@ func (d *Dispenser) EofErr() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Err generates a custom parse error with a message of msg.
 | 
					// Err generates a custom parse error with a message of msg.
 | 
				
			||||||
func (d *Dispenser) Err(msg string) error {
 | 
					func (d *Dispenser) Err(msg string) error {
 | 
				
			||||||
	msg = fmt.Sprintf("%s:%d - Parse error: %s", d.filename, d.Line(), msg)
 | 
						msg = fmt.Sprintf("%s:%d - Parse error: %s", d.File(), d.Line(), msg)
 | 
				
			||||||
	return errors.New(msg)
 | 
						return errors.New(msg)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -215,3 +229,17 @@ func (d *Dispenser) numLineBreaks(tknIdx int) int {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return strings.Count(d.tokens[tknIdx].text, "\n")
 | 
						return strings.Count(d.tokens[tknIdx].text, "\n")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// isNewLine determines whether the current token is on a different
 | 
				
			||||||
 | 
					// line (higher line number) than the previous token. It handles imported
 | 
				
			||||||
 | 
					// tokens correctly. If there isn't a previous token, it returns true.
 | 
				
			||||||
 | 
					func (d *Dispenser) isNewLine() bool {
 | 
				
			||||||
 | 
						if d.cursor < 1 {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if d.cursor > len(d.tokens)-1 {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return d.tokens[d.cursor-1].file != d.tokens[d.cursor].file ||
 | 
				
			||||||
 | 
							d.tokens[d.cursor-1].line+d.numLineBreaks(d.cursor-1) < d.tokens[d.cursor].line
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								config/parse/import_test1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								config/parse/import_test1.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					dir2 arg1 arg2
 | 
				
			||||||
 | 
					dir3
 | 
				
			||||||
							
								
								
									
										4
									
								
								config/parse/import_test2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								config/parse/import_test2.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					host1 {
 | 
				
			||||||
 | 
						dir1
 | 
				
			||||||
 | 
						dir2 arg1
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -19,6 +19,7 @@ type (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// token represents a single parsable unit.
 | 
						// token represents a single parsable unit.
 | 
				
			||||||
	token struct {
 | 
						token struct {
 | 
				
			||||||
 | 
							file string
 | 
				
			||||||
		line int
 | 
							line int
 | 
				
			||||||
		text string
 | 
							text string
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,7 @@ package parse
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -73,7 +74,16 @@ func (p *parser) addresses() error {
 | 
				
			|||||||
	var expectingAnother bool
 | 
						var expectingAnother bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		tkn, startLine := p.Val(), p.Line()
 | 
							tkn := p.Val()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// special case: import directive replaces tokens during parse-time
 | 
				
			||||||
 | 
							if tkn == "import" && p.isNewLine() {
 | 
				
			||||||
 | 
								err := p.doImport()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Open brace definitely indicates end of addresses
 | 
							// Open brace definitely indicates end of addresses
 | 
				
			||||||
		if tkn == "{" {
 | 
							if tkn == "{" {
 | 
				
			||||||
@ -104,13 +114,13 @@ func (p *parser) addresses() error {
 | 
				
			|||||||
		if expectingAnother && !hasNext {
 | 
							if expectingAnother && !hasNext {
 | 
				
			||||||
			return p.EofErr()
 | 
								return p.EofErr()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if !expectingAnother && p.Line() > startLine {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !hasNext {
 | 
							if !hasNext {
 | 
				
			||||||
			p.eof = true
 | 
								p.eof = true
 | 
				
			||||||
			break // EOF
 | 
								break // EOF
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if !expectingAnother && p.isNewLine() {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
@ -156,6 +166,7 @@ func (p *parser) directives() error {
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								p.cursor-- // cursor is advanced when we continue, so roll back one more
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -188,12 +199,17 @@ func (p *parser) doImport() error {
 | 
				
			|||||||
	defer file.Close()
 | 
						defer file.Close()
 | 
				
			||||||
	importedTokens := allTokens(file)
 | 
						importedTokens := allTokens(file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Tack the filename onto these tokens so any errors show the imported file's name
 | 
				
			||||||
 | 
						for i := 0; i < len(importedTokens); i++ {
 | 
				
			||||||
 | 
							importedTokens[i].file = filepath.Base(importFile)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Splice out the import directive and its argument (2 tokens total)
 | 
						// Splice out the import directive and its argument (2 tokens total)
 | 
				
			||||||
	// and insert the imported tokens.
 | 
						// and insert the imported tokens in their place.
 | 
				
			||||||
	tokensBefore := p.tokens[:p.cursor-1]
 | 
						tokensBefore := p.tokens[:p.cursor-1]
 | 
				
			||||||
	tokensAfter := p.tokens[p.cursor+1:]
 | 
						tokensAfter := p.tokens[p.cursor+1:]
 | 
				
			||||||
	p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...)
 | 
						p.tokens = append(tokensBefore, append(importedTokens, tokensAfter...)...)
 | 
				
			||||||
	p.cursor -= 2
 | 
						p.cursor-- // cursor was advanced one position to read the filename; rewind it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -206,7 +222,6 @@ func (p *parser) doImport() error {
 | 
				
			|||||||
// by directive setup functions.
 | 
					// by directive setup functions.
 | 
				
			||||||
func (p *parser) directive() error {
 | 
					func (p *parser) directive() error {
 | 
				
			||||||
	dir := p.Val()
 | 
						dir := p.Val()
 | 
				
			||||||
	line := p.Line()
 | 
					 | 
				
			||||||
	nesting := 0
 | 
						nesting := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, ok := ValidDirectives[dir]; !ok {
 | 
						if _, ok := ValidDirectives[dir]; !ok {
 | 
				
			||||||
@ -219,7 +234,7 @@ func (p *parser) directive() error {
 | 
				
			|||||||
	for p.Next() {
 | 
						for p.Next() {
 | 
				
			||||||
		if p.Val() == "{" {
 | 
							if p.Val() == "{" {
 | 
				
			||||||
			nesting++
 | 
								nesting++
 | 
				
			||||||
		} else if p.Line()+p.numLineBreaks(p.cursor) > line && nesting == 0 {
 | 
							} else if p.isNewLine() && nesting == 0 {
 | 
				
			||||||
			p.cursor-- // read too far
 | 
								p.cursor-- // read too far
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		} else if p.Val() == "}" && nesting > 0 {
 | 
							} else if p.Val() == "}" && nesting > 0 {
 | 
				
			||||||
@ -239,7 +254,7 @@ func (p *parser) directive() error {
 | 
				
			|||||||
// openCurlyBrace expects the current token to be an
 | 
					// openCurlyBrace expects the current token to be an
 | 
				
			||||||
// opening curly brace. This acts like an assertion
 | 
					// opening curly brace. This acts like an assertion
 | 
				
			||||||
// because it returns an error if the token is not
 | 
					// because it returns an error if the token is not
 | 
				
			||||||
// a opening curly brace. It does not advance the token.
 | 
					// a opening curly brace. It does NOT advance the token.
 | 
				
			||||||
func (p *parser) openCurlyBrace() error {
 | 
					func (p *parser) openCurlyBrace() error {
 | 
				
			||||||
	if p.Val() != "{" {
 | 
						if p.Val() != "{" {
 | 
				
			||||||
		return p.SyntaxErr("{")
 | 
							return p.SyntaxErr("{")
 | 
				
			||||||
@ -250,7 +265,7 @@ func (p *parser) openCurlyBrace() error {
 | 
				
			|||||||
// closeCurlyBrace expects the current token to be
 | 
					// closeCurlyBrace expects the current token to be
 | 
				
			||||||
// a closing curly brace. This acts like an assertion
 | 
					// a closing curly brace. This acts like an assertion
 | 
				
			||||||
// because it returns an error if the token is not
 | 
					// because it returns an error if the token is not
 | 
				
			||||||
// a closing curly brace. It does not advance the token.
 | 
					// a closing curly brace. It does NOT advance the token.
 | 
				
			||||||
func (p *parser) closeCurlyBrace() error {
 | 
					func (p *parser) closeCurlyBrace() error {
 | 
				
			||||||
	if p.Val() != "}" {
 | 
						if p.Val() != "}" {
 | 
				
			||||||
		return p.SyntaxErr("}")
 | 
							return p.SyntaxErr("}")
 | 
				
			||||||
 | 
				
			|||||||
@ -57,7 +57,7 @@ func TestStandardAddress(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestParseOne(t *testing.T) {
 | 
					func TestParseOneAndImport(t *testing.T) {
 | 
				
			||||||
	setupParseTests()
 | 
						setupParseTests()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testParseOne := func(input string) (multiServerBlock, error) {
 | 
						testParseOne := func(input string) (multiServerBlock, error) {
 | 
				
			||||||
@ -218,6 +218,23 @@ func TestParseOne(t *testing.T) {
 | 
				
			|||||||
		}},
 | 
							}},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{``, false, []address{}, map[string]int{}},
 | 
							{``, false, []address{}, map[string]int{}},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{`localhost
 | 
				
			||||||
 | 
							  dir1 arg1
 | 
				
			||||||
 | 
							  import import_test1.txt`, false, []address{
 | 
				
			||||||
 | 
								{"localhost", ""},
 | 
				
			||||||
 | 
							}, map[string]int{
 | 
				
			||||||
 | 
								"dir1": 2,
 | 
				
			||||||
 | 
								"dir2": 3,
 | 
				
			||||||
 | 
								"dir3": 1,
 | 
				
			||||||
 | 
							}},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{`import import_test2.txt`, false, []address{
 | 
				
			||||||
 | 
								{"host1", ""},
 | 
				
			||||||
 | 
							}, map[string]int{
 | 
				
			||||||
 | 
								"dir1": 1,
 | 
				
			||||||
 | 
								"dir2": 2,
 | 
				
			||||||
 | 
							}},
 | 
				
			||||||
	} {
 | 
						} {
 | 
				
			||||||
		result, err := testParseOne(test.input)
 | 
							result, err := testParseOne(test.input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										6
									
								
								dist/CHANGES.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								dist/CHANGES.txt
									
									
									
									
										vendored
									
									
								
							@ -1,5 +1,11 @@
 | 
				
			|||||||
CHANGES
 | 
					CHANGES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<master>
 | 
				
			||||||
 | 
					- errors: Error log now includes timestamp with each entry
 | 
				
			||||||
 | 
					- gzip: Default filtering is by extension (fixes bug); removed MIME type filter
 | 
				
			||||||
 | 
					- import: Fixed; works inside and outside server blocks
 | 
				
			||||||
 | 
					- templates: Restricted or missing files result in proper 403 or 404 error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
0.7.2 (July 1, 2015)
 | 
					0.7.2 (July 1, 2015)
 | 
				
			||||||
- Custom builds through caddyserver.com - extend Caddy by writing addons
 | 
					- Custom builds through caddyserver.com - extend Caddy by writing addons
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user