mirror of
https://github.com/caddyserver/caddy.git
synced 2025-03-12 08:38:50 +01:00
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.
This commit is contained in:
parent
49fdc6a20a
commit
2f92443de7
10 changed files with 206 additions and 140 deletions
21
caddy.go
21
caddy.go
|
@ -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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
58
caddy_test.go
Normal file
58
caddy_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
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 (
|
||||||
// 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 (
|
||||||
// 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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (
|
||||||
|
|
||||||
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) {
|
||||||
# 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) {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
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) ([
|
||||||
// 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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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) {
|
||||||
// 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 {
|
||||||
} 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 {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
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) {
|
||||||
// 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 {
|
||||||
// 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue