diff options
author | Matthew Holt <[email protected]> | 2016-06-04 22:50:23 -0600 |
---|---|---|
committer | Matthew Holt <[email protected]> | 2016-06-04 22:50:23 -0600 |
commit | 2f92443de7504c092a231a7ad7d28edb2d8e45bc (patch) | |
tree | a23e75e45216d6f63683a0f3f2447bec166905f7 | |
parent | 49fdc6a20a09ec35f67603fe0d7db67ae9286301 (diff) | |
download | caddy-2f92443de7504c092a231a7ad7d28edb2d8e45bc.tar.gz caddy-2f92443de7504c092a231a7ad7d28edb2d8e45bc.zip |
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.
-rw-r--r-- | caddy.go | 21 | ||||
-rw-r--r-- | caddy_test.go | 58 | ||||
-rw-r--r-- | caddyfile/dispenser.go | 24 | ||||
-rw-r--r-- | caddyfile/lexer.go | 16 | ||||
-rw-r--r-- | caddyfile/lexer_test.go | 116 | ||||
-rw-r--r-- | caddyfile/parse.go | 26 | ||||
-rw-r--r-- | caddyfile/parse_test.go | 23 | ||||
-rw-r--r-- | caddyhttp/httpserver/plugin.go | 34 | ||||
-rw-r--r-- | caddyhttp/httpserver/plugin_test.go | 22 | ||||
-rw-r--r-- | caddytls/client.go | 6 |
10 files changed, 206 insertions, 140 deletions
@@ -421,7 +421,7 @@ func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]r } if !Quiet { for _, srvln := range inst.servers { - if !IsLocalhost(srvln.listener.Addr().String()) { + if !IsLoopback(srvln.listener.Addr().String()) { checkFdlimit() break } @@ -571,6 +571,9 @@ func getServerType(serverType string) (ServerType, error) { if ok { return stype, nil } + if len(serverTypes) == 0 { + return ServerType{}, fmt.Errorf("no server types plugged in") + } if serverType == "" { if len(serverTypes) == 1 { 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") } - if len(serverTypes) == 0 { - return ServerType{}, fmt.Errorf("no server types plugged in") - } return ServerType{}, fmt.Errorf("unknown server type '%s'", serverType) } @@ -618,16 +618,16 @@ func Stop() error { 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 // be a host or a host:port combination. -func IsLocalhost(addr string) bool { +func IsLoopback(addr string) bool { host, _, err := net.SplitHostPort(addr) if err != nil { host = addr // happens if the addr is just a hostname } return host == "localhost" || - host == "::1" || + strings.Trim(host, "[]") == "::1" || strings.HasPrefix(host, "127.") } @@ -715,13 +715,6 @@ func DefaultInput(serverType string) Input { 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. // It does nothing if PidFile is not set. func writePidFile() error { diff --git a/caddy_test.go b/caddy_test.go new file mode 100644 index 000000000..51e93c8ac --- /dev/null +++ b/caddy_test.go @@ -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) + } + } +} diff --git a/caddyfile/dispenser.go b/caddyfile/dispenser.go index 7fa169d45..7beae9f3c 100644 --- a/caddyfile/dispenser.go +++ b/caddyfile/dispenser.go @@ -12,7 +12,7 @@ import ( // some really convenient methods. type Dispenser struct { filename string - tokens []token + tokens []Token cursor int nesting int } @@ -27,7 +27,7 @@ func NewDispenser(filename string, input io.Reader) Dispenser { } // 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{ filename: filename, tokens: tokens, @@ -59,8 +59,8 @@ func (d *Dispenser) NextArg() bool { return false } if d.cursor < len(d.tokens)-1 && - 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].File == d.tokens[d.cursor+1].File && + d.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) == d.tokens[d.cursor+1].Line { d.cursor++ return true } @@ -80,8 +80,8 @@ func (d *Dispenser) NextLine() bool { return false } if d.cursor < len(d.tokens)-1 && - (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].File != d.tokens[d.cursor+1].File || + d.tokens[d.cursor].Line+d.numLineBreaks(d.cursor) < d.tokens[d.cursor+1].Line) { d.cursor++ return true } @@ -131,7 +131,7 @@ func (d *Dispenser) Val() string { if d.cursor < 0 || d.cursor >= len(d.tokens) { 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 @@ -140,7 +140,7 @@ func (d *Dispenser) Line() int { if d.cursor < 0 || d.cursor >= len(d.tokens) { 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, @@ -149,7 +149,7 @@ 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 != "" { + if tokenFilename := d.tokens[d.cursor].File; tokenFilename != "" { return tokenFilename } return d.filename @@ -233,7 +233,7 @@ func (d *Dispenser) numLineBreaks(tknIdx int) int { if tknIdx < 0 || tknIdx >= len(d.tokens) { 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 @@ -246,6 +246,6 @@ func (d *Dispenser) isNewLine() bool { 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 + 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 } diff --git a/caddyfile/lexer.go b/caddyfile/lexer.go index 537c263aa..a3004af14 100644 --- a/caddyfile/lexer.go +++ b/caddyfile/lexer.go @@ -13,15 +13,15 @@ type ( // in quotes if it contains whitespace. lexer struct { reader *bufio.Reader - token token + token Token line int } - // token represents a single parsable unit. - token struct { - file string - line int - text string + // Token represents a single parsable unit. + Token struct { + File string + Line int + Text string } ) @@ -47,7 +47,7 @@ func (l *lexer) next() bool { var comment, quoted, escaped bool makeToken := func() bool { - l.token.text = string(val) + l.token.Text = string(val) return true } @@ -110,7 +110,7 @@ func (l *lexer) next() bool { } if len(val) == 0 { - l.token = token{line: l.line} + l.token = Token{Line: l.line} if ch == '"' { quoted = true continue diff --git a/caddyfile/lexer_test.go b/caddyfile/lexer_test.go index 0e30974d6..4f8295c42 100644 --- a/caddyfile/lexer_test.go +++ b/caddyfile/lexer_test.go @@ -7,44 +7,44 @@ import ( type lexerTestCase struct { input string - expected []token + expected []Token } func TestLexer(t *testing.T) { testCases := []lexerTestCase{ { input: `host:123`, - expected: []token{ - {line: 1, text: "host:123"}, + expected: []Token{ + {Line: 1, Text: "host:123"}, }, }, { input: `host:123 directive`, - expected: []token{ - {line: 1, text: "host:123"}, - {line: 3, text: "directive"}, + expected: []Token{ + {Line: 1, Text: "host:123"}, + {Line: 3, Text: "directive"}, }, }, { input: `host:123 { directive }`, - expected: []token{ - {line: 1, text: "host:123"}, - {line: 1, text: "{"}, - {line: 2, text: "directive"}, - {line: 3, text: "}"}, + expected: []Token{ + {Line: 1, Text: "host:123"}, + {Line: 1, Text: "{"}, + {Line: 2, Text: "directive"}, + {Line: 3, Text: "}"}, }, }, { input: `host:123 { directive }`, - expected: []token{ - {line: 1, text: "host:123"}, - {line: 1, text: "{"}, - {line: 1, text: "directive"}, - {line: 1, text: "}"}, + expected: []Token{ + {Line: 1, Text: "host:123"}, + {Line: 1, Text: "{"}, + {Line: 1, Text: "directive"}, + {Line: 1, Text: "}"}, }, }, { @@ -54,42 +54,42 @@ func TestLexer(t *testing.T) { # comment foobar # another comment }`, - expected: []token{ - {line: 1, text: "host:123"}, - {line: 1, text: "{"}, - {line: 3, text: "directive"}, - {line: 5, text: "foobar"}, - {line: 6, text: "}"}, + expected: []Token{ + {Line: 1, Text: "host:123"}, + {Line: 1, Text: "{"}, + {Line: 3, Text: "directive"}, + {Line: 5, Text: "foobar"}, + {Line: 6, Text: "}"}, }, }, { input: `a "quoted value" b foobar`, - expected: []token{ - {line: 1, text: "a"}, - {line: 1, text: "quoted value"}, - {line: 1, text: "b"}, - {line: 2, text: "foobar"}, + expected: []Token{ + {Line: 1, Text: "a"}, + {Line: 1, Text: "quoted value"}, + {Line: 1, Text: "b"}, + {Line: 2, Text: "foobar"}, }, }, { input: `A "quoted \"value\" inside" B`, - expected: []token{ - {line: 1, text: "A"}, - {line: 1, text: `quoted "value" inside`}, - {line: 1, text: "B"}, + expected: []Token{ + {Line: 1, Text: "A"}, + {Line: 1, Text: `quoted "value" inside`}, + {Line: 1, Text: "B"}, }, }, { input: `"don't\escape"`, - expected: []token{ - {line: 1, text: `don't\escape`}, + expected: []Token{ + {Line: 1, Text: `don't\escape`}, }, }, { input: `"don't\\escape"`, - expected: []token{ - {line: 1, text: `don't\\escape`}, + expected: []Token{ + {Line: 1, Text: `don't\\escape`}, }, }, { @@ -97,35 +97,35 @@ func TestLexer(t *testing.T) { break inside" { foobar }`, - expected: []token{ - {line: 1, text: "A"}, - {line: 1, text: "quoted value with line\n\t\t\t\t\tbreak inside"}, - {line: 2, text: "{"}, - {line: 3, text: "foobar"}, - {line: 4, text: "}"}, + expected: []Token{ + {Line: 1, Text: "A"}, + {Line: 1, Text: "quoted value with line\n\t\t\t\t\tbreak inside"}, + {Line: 2, Text: "{"}, + {Line: 3, Text: "foobar"}, + {Line: 4, Text: "}"}, }, }, { input: `"C:\php\php-cgi.exe"`, - expected: []token{ - {line: 1, text: `C:\php\php-cgi.exe`}, + expected: []Token{ + {Line: 1, Text: `C:\php\php-cgi.exe`}, }, }, { input: `empty "" string`, - expected: []token{ - {line: 1, text: `empty`}, - {line: 1, text: ``}, - {line: 1, text: `string`}, + expected: []Token{ + {Line: 1, Text: `empty`}, + {Line: 1, Text: ``}, + {Line: 1, Text: `string`}, }, }, { input: "skip those\r\nCR characters", - expected: []token{ - {line: 1, text: "skip"}, - {line: 1, text: "those"}, - {line: 2, text: "CR"}, - {line: 2, text: "characters"}, + expected: []Token{ + {Line: 1, Text: "skip"}, + {Line: 1, Text: "those"}, + {Line: 2, Text: "CR"}, + {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.load(strings.NewReader(input)) for l.next() { @@ -145,20 +145,20 @@ func tokenize(input string) (tokens []token) { 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) { 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++ { - 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", - n, i, expected[i].text, expected[i].line, actual[i].line) + n, i, expected[i].Text, expected[i].Line, actual[i].Line) 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'", - n, i, expected[i].text, actual[i].text) + n, i, expected[i].Text, actual[i].Text) break } } diff --git a/caddyfile/parse.go b/caddyfile/parse.go index cce4cfbf0..b3b851d5c 100644 --- a/caddyfile/parse.go +++ b/caddyfile/parse.go @@ -22,7 +22,7 @@ func ServerBlocks(filename string, input io.Reader, validDirectives []string) ([ // allTokens lexes the entire input, but does not parse it. // It returns all the tokens from the input, unstructured // and in order. -func allTokens(input io.Reader) (tokens []token) { +func allTokens(input io.Reader) (tokens []Token) { l := new(lexer) l.load(input) for l.next() { @@ -55,7 +55,7 @@ func (p *parser) parseAll() ([]ServerBlock, 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() if err != nil { @@ -224,7 +224,7 @@ func (p *parser) doImport() error { tokensAfter := p.tokens[p.cursor+1:] // collect all the imported tokens - var importedTokens []token + var importedTokens []Token for _, importFile := range matches { newTokens, err := p.doSingleImport(importFile) if err != nil { @@ -243,7 +243,7 @@ func (p *parser) doImport() error { // doSingleImport lexes the individual file at importFile and returns // 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) if err != nil { 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 filename := filepath.Base(importFile) for i := 0; i < len(importedTokens); i++ { - importedTokens[i].file = filename + importedTokens[i].File = filename } return importedTokens, nil @@ -289,7 +289,7 @@ func (p *parser) directive() error { } else if p.Val() == "}" && nesting == 0 { 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]) } @@ -359,11 +359,9 @@ func replaceEnvReferences(s, refStart, refEnd string) string { return s } -type ( - // ServerBlock associates any number of keys (usually addresses - // of some sort) with tokens (grouped by directive name). - ServerBlock struct { - Keys []string - Tokens map[string][]token - } -) +// ServerBlock associates any number of keys (usually addresses +// of some sort) with tokens (grouped by directive name). +type ServerBlock struct { + Keys []string + Tokens map[string][]Token +} diff --git a/caddyfile/parse_test.go b/caddyfile/parse_test.go index 6b7ad47bb..27d62615a 100644 --- a/caddyfile/parse_test.go +++ b/caddyfile/parse_test.go @@ -16,15 +16,13 @@ func TestAllTokens(t *testing.T) { } for i, val := range expected { - if tokens[i].text != val { - t.Errorf("Token %d should be '%s' but was '%s'", i, val, tokens[i].text) + if tokens[i].Text != val { + t.Errorf("Token %d should be '%s' but was '%s'", i, val, tokens[i].Text) } } } func TestParseOneAndImport(t *testing.T) { - setupParseTests() - testParseOne := func(input string) (ServerBlock, error) { p := testParser(input) 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) { - setupParseTests() - for i, test := range []struct { input string shouldErr bool @@ -325,8 +321,6 @@ func TestParseAll(t *testing.T) { } func TestEnvironmentReplacement(t *testing.T) { - setupParseTests() - os.Setenv("PORT", "8080") os.Setenv("ADDRESS", "servername.com") os.Setenv("FOOBAR", "foobar") @@ -365,21 +359,21 @@ func TestEnvironmentReplacement(t *testing.T) { if actual, expected := blocks[0].Keys[0], ":8080"; 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) } // combined windows env vars in argument p = testParser(":{%PORT%}\ndir1 {%ADDRESS%}/{%FOOBAR%}") 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) } // malformed env var (windows) p = testParser(":1234\ndir1 {%ADDRESS}") 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) } @@ -393,16 +387,11 @@ func TestEnvironmentReplacement(t *testing.T) { // in quoted field p = testParser(":1234\ndir1 \"Test {$FOOBAR} test\"") 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) } } -func setupParseTests() { - // Set up some bogus directives for testing - //directives = []string{"dir1", "dir2", "dir3"} -} - func testParser(input string) parser { buf := strings.NewReader(input) p := parser{Dispenser: NewDispenser("Test", buf)} diff --git a/caddyhttp/httpserver/plugin.go b/caddyhttp/httpserver/plugin.go index 5e6cb0e78..3319a6b8d 100644 --- a/caddyhttp/httpserver/plugin.go +++ b/caddyhttp/httpserver/plugin.go @@ -70,12 +70,6 @@ type httpContext struct { // executing directives and otherwise prepares the directives to // be parsed and executed. 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 _, sb := range serverBlocks { 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 } @@ -215,12 +221,15 @@ type Address struct { // String returns a human-friendly print of the address. func (a Address) String() string { + if a.Host == "" && a.Port == "" { + return "" + } scheme := a.Scheme if scheme == "" { - if a.Port == "80" { - scheme = "http" - } else if a.Port == "443" { + if a.Port == "443" { scheme = "https" + } else { + scheme = "http" } } s := scheme @@ -228,12 +237,13 @@ func (a Address) String() string { s += "://" } s += a.Host - if (scheme == "https" && a.Port != "443") || - (scheme == "http" && a.Port != "80") { + if a.Port != "" && + ((scheme == "https" && a.Port != "443") || + (scheme == "http" && a.Port != "80")) { s += ":" + a.Port } if a.Path != "" { - s += "/" + a.Path + s += a.Path } return s } diff --git a/caddyhttp/httpserver/plugin_test.go b/caddyhttp/httpserver/plugin_test.go index 353e0948c..9959ce9bb 100644 --- a/caddyhttp/httpserver/plugin_test.go +++ b/caddyhttp/httpserver/plugin_test.go @@ -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) + } + } +} diff --git a/caddytls/client.go b/caddytls/client.go index c0e093322..8324b8382 100644 --- a/caddytls/client.go +++ b/caddytls/client.go @@ -58,11 +58,7 @@ var newACMEClient = func(config *Config, allowPrompts bool) (*ACMEClient, error) caURL = "https://" + caURL } u, err := url.Parse(caURL) - if u.Scheme != "https" && - u.Host != "localhost" && - u.Host != "[::1]" && - !strings.HasPrefix(u.Host, "127.") && - !strings.HasPrefix(u.Host, "10.") { + if u.Scheme != "https" && !caddy.IsLoopback(u.Host) && !strings.HasPrefix(u.Host, "10.") { return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL) } |