mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-06 22:40:31 -05:00
Fixed import command, added tests
This commit is contained in:
parent
c82d7c2dd2
commit
d3c229375c
7 changed files with 91 additions and 18 deletions
|
@ -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…
Reference in a new issue