Commit 2f92443d authored by Matthew Holt's avatar Matthew Holt

More tests, several fixes and improvements; export caddyfile.Token

We now sneakily chain in the errors directive if gzip is present but
not errors. This change fixes #616.
parent 49fdc6a2
...@@ -421,7 +421,7 @@ func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]r ...@@ -421,7 +421,7 @@ func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]r
} }
if !Quiet { if !Quiet {
for _, srvln := range inst.servers { for _, srvln := range inst.servers {
if !IsLocalhost(srvln.listener.Addr().String()) { if !IsLoopback(srvln.listener.Addr().String()) {
checkFdlimit() checkFdlimit()
break break
} }
...@@ -571,6 +571,9 @@ func getServerType(serverType string) (ServerType, error) { ...@@ -571,6 +571,9 @@ func getServerType(serverType string) (ServerType, error) {
if ok { if ok {
return stype, nil return stype, nil
} }
if len(serverTypes) == 0 {
return ServerType{}, fmt.Errorf("no server types plugged in")
}
if serverType == "" { if serverType == "" {
if len(serverTypes) == 1 { if len(serverTypes) == 1 {
for _, stype := range serverTypes { for _, stype := range serverTypes {
...@@ -579,9 +582,6 @@ func getServerType(serverType string) (ServerType, error) { ...@@ -579,9 +582,6 @@ func getServerType(serverType string) (ServerType, error) {
} }
return ServerType{}, fmt.Errorf("multiple server types available; must choose one") return ServerType{}, fmt.Errorf("multiple server types available; must choose one")
} }
if len(serverTypes) == 0 {
return ServerType{}, fmt.Errorf("no server types plugged in")
}
return ServerType{}, fmt.Errorf("unknown server type '%s'", serverType) return ServerType{}, fmt.Errorf("unknown server type '%s'", serverType)
} }
...@@ -618,16 +618,16 @@ func Stop() error { ...@@ -618,16 +618,16 @@ func Stop() error {
return nil return nil
} }
// IsLocalhost returns true if the hostname of addr looks // IsLoopback returns true if the hostname of addr looks
// explicitly like a common local hostname. addr must only // explicitly like a common local hostname. addr must only
// be a host or a host:port combination. // be a host or a host:port combination.
func IsLocalhost(addr string) bool { func IsLoopback(addr string) bool {
host, _, err := net.SplitHostPort(addr) host, _, err := net.SplitHostPort(addr)
if err != nil { if err != nil {
host = addr // happens if the addr is just a hostname host = addr // happens if the addr is just a hostname
} }
return host == "localhost" || return host == "localhost" ||
host == "::1" || strings.Trim(host, "[]") == "::1" ||
strings.HasPrefix(host, "127.") strings.HasPrefix(host, "127.")
} }
...@@ -715,13 +715,6 @@ func DefaultInput(serverType string) Input { ...@@ -715,13 +715,6 @@ func DefaultInput(serverType string) Input {
return serverTypes[serverType].DefaultInput() return serverTypes[serverType].DefaultInput()
} }
// IsLoopback returns true if host looks explicitly like a loopback address.
func IsLoopback(host string) bool {
return host == "localhost" ||
host == "::1" ||
strings.HasPrefix(host, "127.")
}
// writePidFile writes the process ID to the file at PidFile. // writePidFile writes the process ID to the file at PidFile.
// It does nothing if PidFile is not set. // It does nothing if PidFile is not set.
func writePidFile() error { func writePidFile() error {
......
package caddy
import "testing"
/*
// TODO
func TestCaddyStartStop(t *testing.T) {
caddyfile := "localhost:1984"
for i := 0; i < 2; i++ {
_, err := Start(CaddyfileInput{Contents: []byte(caddyfile)})
if err != nil {
t.Fatalf("Error starting, iteration %d: %v", i, err)
}
client := http.Client{
Timeout: time.Duration(2 * time.Second),
}
resp, err := client.Get("http://localhost:1984")
if err != nil {
t.Fatalf("Expected GET request to succeed (iteration %d), but it failed: %v", i, err)
}
resp.Body.Close()
err = Stop()
if err != nil {
t.Fatalf("Error stopping, iteration %d: %v", i, err)
}
}
}
*/
func TestIsLoopback(t *testing.T) {
for i, test := range []struct {
input string
expect bool
}{
{"example.com", false},
{"localhost", true},
{"localhost:1234", true},
{"localhost:", true},
{"127.0.0.1", true},
{"127.0.0.1:443", true},
{"127.0.1.5", true},
{"10.0.0.5", false},
{"12.7.0.1", false},
{"[::1]", true},
{"[::1]:1234", true},
{"::1", true},
{"::", false},
{"[::]", false},
{"local", false},
} {
if got, want := IsLoopback(test.input), test.expect; got != want {
t.Errorf("Test %d (%s): expected %v but was %v", i, test.input, want, got)
}
}
}
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
// some really convenient methods. // some really convenient methods.
type Dispenser struct { type Dispenser struct {
filename string filename string
tokens []token tokens []Token
cursor int cursor int
nesting int nesting int
} }
...@@ -27,7 +27,7 @@ func NewDispenser(filename string, input io.Reader) Dispenser { ...@@ -27,7 +27,7 @@ func NewDispenser(filename string, input io.Reader) Dispenser {
} }
// NewDispenserTokens returns a Dispenser filled with the given tokens. // NewDispenserTokens returns a Dispenser filled with the given tokens.
func NewDispenserTokens(filename string, tokens []token) Dispenser { func NewDispenserTokens(filename string, tokens []Token) Dispenser {
return Dispenser{ return Dispenser{
filename: filename, filename: filename,
tokens: tokens, tokens: tokens,
...@@ -59,8 +59,8 @@ func (d *Dispenser) NextArg() bool { ...@@ -59,8 +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].file == d.tokens[d.cursor+1].file && 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.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) == d.tokens[d.cursor+1].Line {
d.cursor++ d.cursor++
return true return true
} }
...@@ -80,8 +80,8 @@ func (d *Dispenser) NextLine() bool { ...@@ -80,8 +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].file != d.tokens[d.cursor+1].file || (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.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) < d.tokens[d.cursor+1].Line) {
d.cursor++ d.cursor++
return true return true
} }
...@@ -131,7 +131,7 @@ func (d *Dispenser) Val() string { ...@@ -131,7 +131,7 @@ func (d *Dispenser) Val() string {
if d.cursor < 0 || d.cursor >= len(d.tokens) { if d.cursor < 0 || d.cursor >= len(d.tokens) {
return "" return ""
} }
return d.tokens[d.cursor].text return d.tokens[d.cursor].Text
} }
// Line gets the line number of the current token. If there is no token // Line gets the line number of the current token. If there is no token
...@@ -140,7 +140,7 @@ func (d *Dispenser) Line() int { ...@@ -140,7 +140,7 @@ func (d *Dispenser) Line() int {
if d.cursor < 0 || d.cursor >= len(d.tokens) { if d.cursor < 0 || d.cursor >= len(d.tokens) {
return 0 return 0
} }
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, // File gets the filename of the current token. If there is no token loaded,
...@@ -149,7 +149,7 @@ func (d *Dispenser) File() string { ...@@ -149,7 +149,7 @@ func (d *Dispenser) File() string {
if d.cursor < 0 || d.cursor >= len(d.tokens) { if d.cursor < 0 || d.cursor >= len(d.tokens) {
return d.filename return d.filename
} }
if tokenFilename := d.tokens[d.cursor].file; tokenFilename != "" { if tokenFilename := d.tokens[d.cursor].File; tokenFilename != "" {
return tokenFilename return tokenFilename
} }
return d.filename return d.filename
...@@ -233,7 +233,7 @@ func (d *Dispenser) numLineBreaks(tknIdx int) int { ...@@ -233,7 +233,7 @@ func (d *Dispenser) numLineBreaks(tknIdx int) int {
if tknIdx < 0 || tknIdx >= len(d.tokens) { if tknIdx < 0 || tknIdx >= len(d.tokens) {
return 0 return 0
} }
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 // isNewLine determines whether the current token is on a different
...@@ -246,6 +246,6 @@ func (d *Dispenser) isNewLine() bool { ...@@ -246,6 +246,6 @@ func (d *Dispenser) isNewLine() bool {
if d.cursor > len(d.tokens)-1 { if d.cursor > len(d.tokens)-1 {
return false return false
} }
return d.tokens[d.cursor-1].file != d.tokens[d.cursor].file || 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 d.tokens[d.cursor-1].Line+d.numLineBreaks(d.cursor-1) < d.tokens[d.cursor].Line
} }
...@@ -13,15 +13,15 @@ type ( ...@@ -13,15 +13,15 @@ type (
// in quotes if it contains whitespace. // in quotes if it contains whitespace.
lexer struct { lexer struct {
reader *bufio.Reader reader *bufio.Reader
token token token Token
line int line int
} }
// token represents a single parsable unit. // Token represents a single parsable unit.
token struct { Token struct {
file string File string
line int Line int
text string Text string
} }
) )
...@@ -47,7 +47,7 @@ func (l *lexer) next() bool { ...@@ -47,7 +47,7 @@ func (l *lexer) next() bool {
var comment, quoted, escaped bool var comment, quoted, escaped bool
makeToken := func() bool { makeToken := func() bool {
l.token.text = string(val) l.token.Text = string(val)
return true return true
} }
...@@ -110,7 +110,7 @@ func (l *lexer) next() bool { ...@@ -110,7 +110,7 @@ func (l *lexer) next() bool {
} }
if len(val) == 0 { if len(val) == 0 {
l.token = token{line: l.line} l.token = Token{Line: l.line}
if ch == '"' { if ch == '"' {
quoted = true quoted = true
continue continue
......
...@@ -7,44 +7,44 @@ import ( ...@@ -7,44 +7,44 @@ import (
type lexerTestCase struct { type lexerTestCase struct {
input string input string
expected []token expected []Token
} }
func TestLexer(t *testing.T) { func TestLexer(t *testing.T) {
testCases := []lexerTestCase{ testCases := []lexerTestCase{
{ {
input: `host:123`, input: `host:123`,
expected: []token{ expected: []Token{
{line: 1, text: "host:123"}, {Line: 1, Text: "host:123"},
}, },
}, },
{ {
input: `host:123 input: `host:123
directive`, directive`,
expected: []token{ expected: []Token{
{line: 1, text: "host:123"}, {Line: 1, Text: "host:123"},
{line: 3, text: "directive"}, {Line: 3, Text: "directive"},
}, },
}, },
{ {
input: `host:123 { input: `host:123 {
directive directive
}`, }`,
expected: []token{ expected: []Token{
{line: 1, text: "host:123"}, {Line: 1, Text: "host:123"},
{line: 1, text: "{"}, {Line: 1, Text: "{"},
{line: 2, text: "directive"}, {Line: 2, Text: "directive"},
{line: 3, text: "}"}, {Line: 3, Text: "}"},
}, },
}, },
{ {
input: `host:123 { directive }`, input: `host:123 { directive }`,
expected: []token{ expected: []Token{
{line: 1, text: "host:123"}, {Line: 1, Text: "host:123"},
{line: 1, text: "{"}, {Line: 1, Text: "{"},
{line: 1, text: "directive"}, {Line: 1, Text: "directive"},
{line: 1, text: "}"}, {Line: 1, Text: "}"},
}, },
}, },
{ {
...@@ -54,42 +54,42 @@ func TestLexer(t *testing.T) { ...@@ -54,42 +54,42 @@ func TestLexer(t *testing.T) {
# comment # comment
foobar # another comment foobar # another comment
}`, }`,
expected: []token{ expected: []Token{
{line: 1, text: "host:123"}, {Line: 1, Text: "host:123"},
{line: 1, text: "{"}, {Line: 1, Text: "{"},
{line: 3, text: "directive"}, {Line: 3, Text: "directive"},
{line: 5, text: "foobar"}, {Line: 5, Text: "foobar"},
{line: 6, text: "}"}, {Line: 6, Text: "}"},
}, },
}, },
{ {
input: `a "quoted value" b input: `a "quoted value" b
foobar`, foobar`,
expected: []token{ expected: []Token{
{line: 1, text: "a"}, {Line: 1, Text: "a"},
{line: 1, text: "quoted value"}, {Line: 1, Text: "quoted value"},
{line: 1, text: "b"}, {Line: 1, Text: "b"},
{line: 2, text: "foobar"}, {Line: 2, Text: "foobar"},
}, },
}, },
{ {
input: `A "quoted \"value\" inside" B`, input: `A "quoted \"value\" inside" B`,
expected: []token{ expected: []Token{
{line: 1, text: "A"}, {Line: 1, Text: "A"},
{line: 1, text: `quoted "value" inside`}, {Line: 1, Text: `quoted "value" inside`},
{line: 1, text: "B"}, {Line: 1, Text: "B"},
}, },
}, },
{ {
input: `"don't\escape"`, input: `"don't\escape"`,
expected: []token{ expected: []Token{
{line: 1, text: `don't\escape`}, {Line: 1, Text: `don't\escape`},
}, },
}, },
{ {
input: `"don't\\escape"`, input: `"don't\\escape"`,
expected: []token{ expected: []Token{
{line: 1, text: `don't\\escape`}, {Line: 1, Text: `don't\\escape`},
}, },
}, },
{ {
...@@ -97,35 +97,35 @@ func TestLexer(t *testing.T) { ...@@ -97,35 +97,35 @@ func TestLexer(t *testing.T) {
break inside" { break inside" {
foobar foobar
}`, }`,
expected: []token{ expected: []Token{
{line: 1, text: "A"}, {Line: 1, Text: "A"},
{line: 1, text: "quoted value with line\n\t\t\t\t\tbreak inside"}, {Line: 1, Text: "quoted value with line\n\t\t\t\t\tbreak inside"},
{line: 2, text: "{"}, {Line: 2, Text: "{"},
{line: 3, text: "foobar"}, {Line: 3, Text: "foobar"},
{line: 4, text: "}"}, {Line: 4, Text: "}"},
}, },
}, },
{ {
input: `"C:\php\php-cgi.exe"`, input: `"C:\php\php-cgi.exe"`,
expected: []token{ expected: []Token{
{line: 1, text: `C:\php\php-cgi.exe`}, {Line: 1, Text: `C:\php\php-cgi.exe`},
}, },
}, },
{ {
input: `empty "" string`, input: `empty "" string`,
expected: []token{ expected: []Token{
{line: 1, text: `empty`}, {Line: 1, Text: `empty`},
{line: 1, text: ``}, {Line: 1, Text: ``},
{line: 1, text: `string`}, {Line: 1, Text: `string`},
}, },
}, },
{ {
input: "skip those\r\nCR characters", input: "skip those\r\nCR characters",
expected: []token{ expected: []Token{
{line: 1, text: "skip"}, {Line: 1, Text: "skip"},
{line: 1, text: "those"}, {Line: 1, Text: "those"},
{line: 2, text: "CR"}, {Line: 2, Text: "CR"},
{line: 2, text: "characters"}, {Line: 2, Text: "characters"},
}, },
}, },
} }
...@@ -136,7 +136,7 @@ func TestLexer(t *testing.T) { ...@@ -136,7 +136,7 @@ func TestLexer(t *testing.T) {
} }
} }
func tokenize(input string) (tokens []token) { func tokenize(input string) (tokens []Token) {
l := lexer{} l := lexer{}
l.load(strings.NewReader(input)) l.load(strings.NewReader(input))
for l.next() { for l.next() {
...@@ -145,20 +145,20 @@ func tokenize(input string) (tokens []token) { ...@@ -145,20 +145,20 @@ func tokenize(input string) (tokens []token) {
return return
} }
func lexerCompare(t *testing.T, n int, expected, actual []token) { func lexerCompare(t *testing.T, n int, expected, actual []Token) {
if len(expected) != len(actual) { if len(expected) != len(actual) {
t.Errorf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual)) t.Errorf("Test case %d: expected %d token(s) but got %d", n, len(expected), len(actual))
} }
for i := 0; i < len(actual) && i < len(expected); i++ { for i := 0; i < len(actual) && i < len(expected); i++ {
if actual[i].line != expected[i].line { if actual[i].Line != expected[i].Line {
t.Errorf("Test case %d token %d ('%s'): expected line %d but was line %d", t.Errorf("Test case %d token %d ('%s'): expected line %d but was line %d",
n, i, expected[i].text, expected[i].line, actual[i].line) n, i, expected[i].Text, expected[i].Line, actual[i].Line)
break break
} }
if actual[i].text != expected[i].text { if actual[i].Text != expected[i].Text {
t.Errorf("Test case %d token %d: expected text '%s' but was '%s'", t.Errorf("Test case %d token %d: expected text '%s' but was '%s'",
n, i, expected[i].text, actual[i].text) n, i, expected[i].Text, actual[i].Text)
break break
} }
} }
......
...@@ -22,7 +22,7 @@ func ServerBlocks(filename string, input io.Reader, validDirectives []string) ([ ...@@ -22,7 +22,7 @@ func ServerBlocks(filename string, input io.Reader, validDirectives []string) ([
// allTokens lexes the entire input, but does not parse it. // allTokens lexes the entire input, but does not parse it.
// It returns all the tokens from the input, unstructured // It returns all the tokens from the input, unstructured
// and in order. // and in order.
func allTokens(input io.Reader) (tokens []token) { func allTokens(input io.Reader) (tokens []Token) {
l := new(lexer) l := new(lexer)
l.load(input) l.load(input)
for l.next() { for l.next() {
...@@ -55,7 +55,7 @@ func (p *parser) parseAll() ([]ServerBlock, error) { ...@@ -55,7 +55,7 @@ func (p *parser) parseAll() ([]ServerBlock, error) {
} }
func (p *parser) parseOne() error { func (p *parser) parseOne() error {
p.block = ServerBlock{Tokens: make(map[string][]token)} p.block = ServerBlock{Tokens: make(map[string][]Token)}
err := p.begin() err := p.begin()
if err != nil { if err != nil {
...@@ -224,7 +224,7 @@ func (p *parser) doImport() error { ...@@ -224,7 +224,7 @@ func (p *parser) doImport() error {
tokensAfter := p.tokens[p.cursor+1:] tokensAfter := p.tokens[p.cursor+1:]
// collect all the imported tokens // collect all the imported tokens
var importedTokens []token var importedTokens []Token
for _, importFile := range matches { for _, importFile := range matches {
newTokens, err := p.doSingleImport(importFile) newTokens, err := p.doSingleImport(importFile)
if err != nil { if err != nil {
...@@ -243,7 +243,7 @@ func (p *parser) doImport() error { ...@@ -243,7 +243,7 @@ func (p *parser) doImport() error {
// doSingleImport lexes the individual file at importFile and returns // doSingleImport lexes the individual file at importFile and returns
// its tokens or an error, if any. // its tokens or an error, if any.
func (p *parser) doSingleImport(importFile string) ([]token, error) { func (p *parser) doSingleImport(importFile string) ([]Token, error) {
file, err := os.Open(importFile) file, err := os.Open(importFile)
if err != nil { if err != nil {
return nil, p.Errf("Could not import %s: %v", importFile, err) return nil, p.Errf("Could not import %s: %v", importFile, err)
...@@ -254,7 +254,7 @@ func (p *parser) doSingleImport(importFile string) ([]token, error) { ...@@ -254,7 +254,7 @@ func (p *parser) doSingleImport(importFile string) ([]token, error) {
// Tack the filename onto these tokens so errors show the imported file's name // Tack the filename onto these tokens so errors show the imported file's name
filename := filepath.Base(importFile) filename := filepath.Base(importFile)
for i := 0; i < len(importedTokens); i++ { for i := 0; i < len(importedTokens); i++ {
importedTokens[i].file = filename importedTokens[i].File = filename
} }
return importedTokens, nil return importedTokens, nil
...@@ -289,7 +289,7 @@ func (p *parser) directive() error { ...@@ -289,7 +289,7 @@ func (p *parser) directive() error {
} else if p.Val() == "}" && nesting == 0 { } else if p.Val() == "}" && nesting == 0 {
return p.Err("Unexpected '}' because no matching opening brace") return p.Err("Unexpected '}' because no matching opening brace")
} }
p.tokens[p.cursor].text = replaceEnvVars(p.tokens[p.cursor].text) p.tokens[p.cursor].Text = replaceEnvVars(p.tokens[p.cursor].Text)
p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor]) p.block.Tokens[dir] = append(p.block.Tokens[dir], p.tokens[p.cursor])
} }
...@@ -359,11 +359,9 @@ func replaceEnvReferences(s, refStart, refEnd string) string { ...@@ -359,11 +359,9 @@ func replaceEnvReferences(s, refStart, refEnd string) string {
return s return s
} }
type ( // ServerBlock associates any number of keys (usually addresses
// ServerBlock associates any number of keys (usually addresses // of some sort) with tokens (grouped by directive name).
// of some sort) with tokens (grouped by directive name). type ServerBlock struct {
ServerBlock struct {
Keys []string Keys []string
Tokens map[string][]token Tokens map[string][]Token
} }
)
...@@ -16,15 +16,13 @@ func TestAllTokens(t *testing.T) { ...@@ -16,15 +16,13 @@ func TestAllTokens(t *testing.T) {
} }
for i, val := range expected { for i, val := range expected {
if tokens[i].text != val { if tokens[i].Text != val {
t.Errorf("Token %d should be '%s' but was '%s'", i, val, tokens[i].text) t.Errorf("Token %d should be '%s' but was '%s'", i, val, tokens[i].Text)
} }
} }
} }
func TestParseOneAndImport(t *testing.T) { func TestParseOneAndImport(t *testing.T) {
setupParseTests()
testParseOne := func(input string) (ServerBlock, error) { testParseOne := func(input string) (ServerBlock, error) {
p := testParser(input) p := testParser(input)
p.Next() // parseOne doesn't call Next() to start, so we must p.Next() // parseOne doesn't call Next() to start, so we must
...@@ -249,8 +247,6 @@ func TestParseOneAndImport(t *testing.T) { ...@@ -249,8 +247,6 @@ func TestParseOneAndImport(t *testing.T) {
} }
func TestParseAll(t *testing.T) { func TestParseAll(t *testing.T) {
setupParseTests()
for i, test := range []struct { for i, test := range []struct {
input string input string
shouldErr bool shouldErr bool
...@@ -325,8 +321,6 @@ func TestParseAll(t *testing.T) { ...@@ -325,8 +321,6 @@ func TestParseAll(t *testing.T) {
} }
func TestEnvironmentReplacement(t *testing.T) { func TestEnvironmentReplacement(t *testing.T) {
setupParseTests()
os.Setenv("PORT", "8080") os.Setenv("PORT", "8080")
os.Setenv("ADDRESS", "servername.com") os.Setenv("ADDRESS", "servername.com")
os.Setenv("FOOBAR", "foobar") os.Setenv("FOOBAR", "foobar")
...@@ -365,21 +359,21 @@ func TestEnvironmentReplacement(t *testing.T) { ...@@ -365,21 +359,21 @@ func TestEnvironmentReplacement(t *testing.T) {
if actual, expected := blocks[0].Keys[0], ":8080"; expected != actual { if actual, expected := blocks[0].Keys[0], ":8080"; expected != actual {
t.Errorf("Expected key to be '%s' but was '%s'", expected, actual) t.Errorf("Expected key to be '%s' but was '%s'", expected, actual)
} }
if actual, expected := blocks[0].Tokens["dir1"][1].text, "foobar"; expected != actual { if actual, expected := blocks[0].Tokens["dir1"][1].Text, "foobar"; expected != actual {
t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual) t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
} }
// combined windows env vars in argument // combined windows env vars in argument
p = testParser(":{%PORT%}\ndir1 {%ADDRESS%}/{%FOOBAR%}") p = testParser(":{%PORT%}\ndir1 {%ADDRESS%}/{%FOOBAR%}")
blocks, _ = p.parseAll() blocks, _ = p.parseAll()
if actual, expected := blocks[0].Tokens["dir1"][1].text, "servername.com/foobar"; expected != actual { if actual, expected := blocks[0].Tokens["dir1"][1].Text, "servername.com/foobar"; expected != actual {
t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual) t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
} }
// malformed env var (windows) // malformed env var (windows)
p = testParser(":1234\ndir1 {%ADDRESS}") p = testParser(":1234\ndir1 {%ADDRESS}")
blocks, _ = p.parseAll() blocks, _ = p.parseAll()
if actual, expected := blocks[0].Tokens["dir1"][1].text, "{%ADDRESS}"; expected != actual { if actual, expected := blocks[0].Tokens["dir1"][1].Text, "{%ADDRESS}"; expected != actual {
t.Errorf("Expected host to be '%s' but was '%s'", expected, actual) t.Errorf("Expected host to be '%s' but was '%s'", expected, actual)
} }
...@@ -393,16 +387,11 @@ func TestEnvironmentReplacement(t *testing.T) { ...@@ -393,16 +387,11 @@ func TestEnvironmentReplacement(t *testing.T) {
// in quoted field // in quoted field
p = testParser(":1234\ndir1 \"Test {$FOOBAR} test\"") p = testParser(":1234\ndir1 \"Test {$FOOBAR} test\"")
blocks, _ = p.parseAll() blocks, _ = p.parseAll()
if actual, expected := blocks[0].Tokens["dir1"][1].text, "Test foobar test"; expected != actual { if actual, expected := blocks[0].Tokens["dir1"][1].Text, "Test foobar test"; expected != actual {
t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual) t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
} }
} }
func setupParseTests() {
// Set up some bogus directives for testing
//directives = []string{"dir1", "dir2", "dir3"}
}
func testParser(input string) parser { func testParser(input string) parser {
buf := strings.NewReader(input) buf := strings.NewReader(input)
p := parser{Dispenser: NewDispenser("Test", buf)} p := parser{Dispenser: NewDispenser("Test", buf)}
......
...@@ -70,12 +70,6 @@ type httpContext struct { ...@@ -70,12 +70,6 @@ type httpContext struct {
// executing directives and otherwise prepares the directives to // executing directives and otherwise prepares the directives to
// be parsed and executed. // be parsed and executed.
func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) { func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error) {
// TODO: Here we can inspect the server blocks
// and make changes to them, like adding a directive
// that must always be present (e.g. 'errors discard`?) -
// totally optional; server types need not register this
// function.
// For each address in each server block, make a new config // For each address in each server block, make a new config
for _, sb := range serverBlocks { for _, sb := range serverBlocks {
for _, key := range sb.Keys { for _, key := range sb.Keys {
...@@ -98,6 +92,18 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd ...@@ -98,6 +92,18 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
} }
} }
// For sites that have gzip (which gets chained in
// before the error handler) we should ensure that the
// errors directive also appears so error pages aren't
// written after the gzip writer is closed.
for _, sb := range serverBlocks {
_, hasGzip := sb.Tokens["gzip"]
_, hasErrors := sb.Tokens["errors"]
if hasGzip && !hasErrors {
sb.Tokens["errors"] = []caddyfile.Token{{Text: "errors"}}
}
}
return serverBlocks, nil return serverBlocks, nil
} }
...@@ -215,12 +221,15 @@ type Address struct { ...@@ -215,12 +221,15 @@ type Address struct {
// String returns a human-friendly print of the address. // String returns a human-friendly print of the address.
func (a Address) String() string { func (a Address) String() string {
if a.Host == "" && a.Port == "" {
return ""
}
scheme := a.Scheme scheme := a.Scheme
if scheme == "" { if scheme == "" {
if a.Port == "80" { if a.Port == "443" {
scheme = "http"
} else if a.Port == "443" {
scheme = "https" scheme = "https"
} else {
scheme = "http"
} }
} }
s := scheme s := scheme
...@@ -228,12 +237,13 @@ func (a Address) String() string { ...@@ -228,12 +237,13 @@ func (a Address) String() string {
s += "://" s += "://"
} }
s += a.Host s += a.Host
if (scheme == "https" && a.Port != "443") || if a.Port != "" &&
(scheme == "http" && a.Port != "80") { ((scheme == "https" && a.Port != "443") ||
(scheme == "http" && a.Port != "80")) {
s += ":" + a.Port s += ":" + a.Port
} }
if a.Path != "" { if a.Path != "" {
s += "/" + a.Path s += a.Path
} }
return s return s
} }
......
...@@ -90,3 +90,25 @@ func TestAddressVHost(t *testing.T) { ...@@ -90,3 +90,25 @@ func TestAddressVHost(t *testing.T) {
} }
} }
} }
func TestAddressString(t *testing.T) {
for i, test := range []struct {
addr Address
expected string
}{
{Address{Scheme: "http", Host: "host", Port: "1234", Path: "/path"}, "http://host:1234/path"},
{Address{Scheme: "", Host: "host", Port: "", Path: ""}, "http://host"},
{Address{Scheme: "", Host: "host", Port: "80", Path: ""}, "http://host"},
{Address{Scheme: "", Host: "host", Port: "443", Path: ""}, "https://host"},
{Address{Scheme: "https", Host: "host", Port: "443", Path: ""}, "https://host"},
{Address{Scheme: "https", Host: "host", Port: "", Path: ""}, "https://host"},
{Address{Scheme: "", Host: "host", Port: "80", Path: "/path"}, "http://host/path"},
{Address{Scheme: "http", Host: "", Port: "1234", Path: ""}, "http://:1234"},
{Address{Scheme: "", Host: "", Port: "", Path: ""}, ""},
} {
actual := test.addr.String()
if actual != test.expected {
t.Errorf("Test %d: expected '%s' but got '%s'", i, test.expected, actual)
}
}
}
...@@ -58,11 +58,7 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error) ...@@ -58,11 +58,7 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error)
caURL = "https://" + caURL caURL = "https://" + caURL
} }
u, err := url.Parse(caURL) u, err := url.Parse(caURL)
if u.Scheme != "https" && if u.Scheme != "https" && !caddy.IsLoopback(u.Host) && !strings.HasPrefix(u.Host, "10.") {
u.Host != "localhost" &&
u.Host != "[::1]" &&
!strings.HasPrefix(u.Host, "127.") &&
!strings.HasPrefix(u.Host, "10.") {
return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment