summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatthew Holt <[email protected]>2016-06-04 22:50:23 -0600
committerMatthew Holt <[email protected]>2016-06-04 22:50:23 -0600
commit2f92443de7504c092a231a7ad7d28edb2d8e45bc (patch)
treea23e75e45216d6f63683a0f3f2447bec166905f7
parent49fdc6a20a09ec35f67603fe0d7db67ae9286301 (diff)
downloadcaddy-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.go21
-rw-r--r--caddy_test.go58
-rw-r--r--caddyfile/dispenser.go24
-rw-r--r--caddyfile/lexer.go16
-rw-r--r--caddyfile/lexer_test.go116
-rw-r--r--caddyfile/parse.go26
-rw-r--r--caddyfile/parse_test.go23
-rw-r--r--caddyhttp/httpserver/plugin.go34
-rw-r--r--caddyhttp/httpserver/plugin_test.go22
-rw-r--r--caddytls/client.go6
10 files changed, 206 insertions, 140 deletions
diff --git a/caddy.go b/caddy.go
index 69897cb33..0cd342abc 100644
--- a/caddy.go
+++ b/caddy.go
@@ -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)
}