diff --git a/caddy/config.go b/caddy/config.go index e29826751..3ff63b481 100644 --- a/caddy/config.go +++ b/caddy/config.go @@ -21,25 +21,22 @@ const ( DefaultConfigFile = "Caddyfile" ) -// loadConfigs reads input (named filename) and parses it, returning the -// server configurations in the order they appeared in the input. As part -// of this, it activates Let's Encrypt for the configs that are produced. -// Thus, the returned configs are already optimally configured optimally -// for HTTPS. -func loadConfigs(filename string, input io.Reader) ([]server.Config, error) { +// loadConfigsUpToIncludingTLS loads the configs from input with name filename and returns them, +// the parsed server blocks, the index of the last directive it processed, and an error (if any). +func loadConfigsUpToIncludingTLS(filename string, input io.Reader) ([]server.Config, []parse.ServerBlock, int, error) { var configs []server.Config // Each server block represents similar hosts/addresses, since they // were grouped together in the Caddyfile. serverBlocks, err := parse.ServerBlocks(filename, input, true) if err != nil { - return nil, err + return nil, nil, 0, err } if len(serverBlocks) == 0 { newInput := DefaultInput() serverBlocks, err = parse.ServerBlocks(newInput.Path(), bytes.NewReader(newInput.Body()), true) if err != nil { - return nil, err + return nil, nil, 0, err } } @@ -56,6 +53,7 @@ func loadConfigs(filename string, input io.Reader) ([]server.Config, error) { config := server.Config{ Host: addr.Host, Port: addr.Port, + Scheme: addr.Scheme, Root: Root, Middleware: make(map[string][]middleware.Middleware), ConfigFile: filename, @@ -88,7 +86,7 @@ func loadConfigs(filename string, input io.Reader) ([]server.Config, error) { // execute setup function and append middleware handler, if any midware, err := dir.setup(controller) if err != nil { - return nil, err + return nil, nil, lastDirectiveIndex, err } if midware != nil { // TODO: For now, we only support the default path scope / @@ -109,22 +107,31 @@ func loadConfigs(filename string, input io.Reader) ([]server.Config, error) { } } + return configs, serverBlocks, lastDirectiveIndex, nil +} + +// loadConfigs reads input (named filename) and parses it, returning the +// server configurations in the order they appeared in the input. As part +// of this, it activates Let's Encrypt for the configs that are produced. +// Thus, the returned configs are already optimally configured for HTTPS. +func loadConfigs(filename string, input io.Reader) ([]server.Config, error) { + configs, serverBlocks, lastDirectiveIndex, err := loadConfigsUpToIncludingTLS(filename, input) + if err != nil { + return nil, err + } + // Now we have all the configs, but they have only been set up to the // point of tls. We need to activate Let's Encrypt before setting up // the rest of the middlewares so they have correct information regarding - // TLS configuration, if necessary. (this call is append-only, so our - // iterations below shouldn't be affected) + // TLS configuration, if necessary. (this only appends, so our iterations + // over server blocks below shouldn't be affected) if !IsRestart() && !Quiet { fmt.Print("Activating privacy features...") } configs, err = letsencrypt.Activate(configs) if err != nil { - if !Quiet { - fmt.Println() - } return nil, err - } - if !IsRestart() && !Quiet { + } else if !IsRestart() && !Quiet { fmt.Println(" done.") } @@ -277,44 +284,17 @@ func arrangeBindings(allConfigs []server.Config) (bindingGroup, error) { // but execution may continue. The second error, if not nil, is a real // problem and the server should not be started. // -// This function handles edge cases gracefully. If a port name like -// "http" or "https" is unknown to the system, this function will -// change them to 80 or 443 respectively. If a hostname fails to -// resolve, that host can still be served but will be listening on -// the wildcard host instead. This function takes care of this for you. +// This function does not handle edge cases like port "http" or "https" if +// they are not known to the system. It does, however, serve on the wildcard +// host if resolving the address of the specific hostname fails. func resolveAddr(conf server.Config) (resolvAddr *net.TCPAddr, warnErr, fatalErr error) { - bindHost := conf.BindHost - - // TODO: Do we even need the port? Maybe we just need to look up the host. - resolvAddr, warnErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, conf.Port)) + resolvAddr, warnErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(conf.BindHost, conf.Port)) if warnErr != nil { - // Most likely the host lookup failed or the port is unknown - tryPort := conf.Port - - switch errVal := warnErr.(type) { - case *net.AddrError: - if errVal.Err == "unknown port" { - // some odd Linux machines don't support these port names; see issue #136 - switch conf.Port { - case "http": - tryPort = "80" - case "https": - tryPort = "443" - } - } - resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, tryPort)) - if fatalErr != nil { - return - } - default: - // the hostname probably couldn't be resolved, just bind to wildcard then - resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort("0.0.0.0", tryPort)) - if fatalErr != nil { - return - } + // the hostname probably couldn't be resolved, just bind to wildcard then + resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort("", conf.Port)) + if fatalErr != nil { + return } - - return } return @@ -334,12 +314,12 @@ func validDirective(d string) bool { // DefaultInput returns the default Caddyfile input // to use when it is otherwise empty or missing. // It uses the default host and port (depends on -// host, e.g. localhost is 2015, otherwise https) and +// host, e.g. localhost is 2015, otherwise 443) and // root. func DefaultInput() CaddyfileInput { port := Port - if letsencrypt.HostQualifies(Host) { - port = "https" + if letsencrypt.HostQualifies(Host) && port == DefaultPort { + port = "443" } return CaddyfileInput{ Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, port, Root)), diff --git a/caddy/config_test.go b/caddy/config_test.go index 3e70a9311..f5f0db6c2 100644 --- a/caddy/config_test.go +++ b/caddy/config_test.go @@ -13,10 +13,10 @@ func TestDefaultInput(t *testing.T) { t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual) } - // next few tests simulate user providing -host flag + // next few tests simulate user providing -host and/or -port flags Host = "not-localhost.com" - if actual, expected := string(DefaultInput().Body()), "not-localhost.com:https\nroot ."; actual != expected { + if actual, expected := string(DefaultInput().Body()), "not-localhost.com:443\nroot ."; actual != expected { t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual) } @@ -29,6 +29,18 @@ func TestDefaultInput(t *testing.T) { if actual, expected := string(DefaultInput().Body()), "127.0.1.1:2015\nroot ."; actual != expected { t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual) } + + Host = "not-localhost.com" + Port = "1234" + if actual, expected := string(DefaultInput().Body()), "not-localhost.com:1234\nroot ."; actual != expected { + t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual) + } + + Host = DefaultHost + Port = "1234" + if actual, expected := string(DefaultInput().Body()), ":1234\nroot ."; actual != expected { + t.Errorf("Host=%s; Port=%s; Root=%s;\nEXPECTED: '%s'\n ACTUAL: '%s'", Host, Port, Root, expected, actual) + } } func TestResolveAddr(t *testing.T) { @@ -51,14 +63,14 @@ func TestResolveAddr(t *testing.T) { {server.Config{Host: "localhost", Port: "80"}, false, false, "", 80}, {server.Config{BindHost: "localhost", Port: "1234"}, false, false, "127.0.0.1", 1234}, {server.Config{BindHost: "127.0.0.1", Port: "1234"}, false, false, "127.0.0.1", 1234}, - {server.Config{BindHost: "should-not-resolve", Port: "1234"}, true, false, "0.0.0.0", 1234}, + {server.Config{BindHost: "should-not-resolve", Port: "1234"}, true, false, "", 1234}, {server.Config{BindHost: "localhost", Port: "http"}, false, false, "127.0.0.1", 80}, {server.Config{BindHost: "localhost", Port: "https"}, false, false, "127.0.0.1", 443}, {server.Config{BindHost: "", Port: "1234"}, false, false, "", 1234}, {server.Config{BindHost: "localhost", Port: "abcd"}, false, true, "", 0}, {server.Config{BindHost: "127.0.0.1", Host: "should-not-be-used", Port: "1234"}, false, false, "127.0.0.1", 1234}, {server.Config{BindHost: "localhost", Host: "should-not-be-used", Port: "1234"}, false, false, "127.0.0.1", 1234}, - {server.Config{BindHost: "should-not-resolve", Host: "localhost", Port: "1234"}, true, false, "0.0.0.0", 1234}, + {server.Config{BindHost: "should-not-resolve", Host: "localhost", Port: "1234"}, true, false, "", 1234}, } { actualAddr, warnErr, fatalErr := resolveAddr(test.config) diff --git a/caddy/parse/parse.go b/caddy/parse/parse.go index 1f9137d9b..faef36c28 100644 --- a/caddy/parse/parse.go +++ b/caddy/parse/parse.go @@ -8,7 +8,7 @@ import "io" // If checkDirectives is true, only valid directives will be allowed // otherwise we consider it a parse error. Server blocks are returned // in the order in which they appear. -func ServerBlocks(filename string, input io.Reader, checkDirectives bool) ([]serverBlock, error) { +func ServerBlocks(filename string, input io.Reader, checkDirectives bool) ([]ServerBlock, error) { p := parser{Dispenser: NewDispenser(filename, input)} p.checkDirectives = checkDirectives blocks, err := p.parseAll() diff --git a/caddy/parse/parsing.go b/caddy/parse/parsing.go index 03d9d800a..2c45510c9 100644 --- a/caddy/parse/parsing.go +++ b/caddy/parse/parsing.go @@ -1,6 +1,7 @@ package parse import ( + "fmt" "net" "os" "path/filepath" @@ -9,13 +10,13 @@ import ( type parser struct { Dispenser - block serverBlock // current server block being parsed + block ServerBlock // current server block being parsed eof bool // if we encounter a valid EOF in a hard place checkDirectives bool // if true, directives must be known } -func (p *parser) parseAll() ([]serverBlock, error) { - var blocks []serverBlock +func (p *parser) parseAll() ([]ServerBlock, error) { + var blocks []ServerBlock for p.Next() { err := p.parseOne() @@ -31,7 +32,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 { @@ -99,11 +100,11 @@ func (p *parser) addresses() error { } // Parse and save this address - host, port, err := standardAddress(tkn) + addr, err := standardAddress(tkn) if err != nil { return err } - p.block.Addresses = append(p.block.Addresses, address{host, port}) + p.block.Addresses = append(p.block.Addresses, addr) } // Advance token and possibly break out of loop or return error @@ -273,39 +274,57 @@ func (p *parser) closeCurlyBrace() error { return nil } -// standardAddress turns the accepted host and port patterns -// into a format accepted by net.Dial. -func standardAddress(str string) (host, port string, err error) { - var schemePort, splitPort string +// standardAddress parses an address string into a structured format with separate +// scheme, host, and port portions, as well as the original input string. +func standardAddress(str string) (address, error) { + var scheme string + var err error + // first check for scheme and strip it off + input := str if strings.HasPrefix(str, "https://") { - schemePort = "https" + scheme = "https" str = str[8:] } else if strings.HasPrefix(str, "http://") { - schemePort = "http" + scheme = "http" str = str[7:] } - host, splitPort, err = net.SplitHostPort(str) + // separate host and port + host, port, err := net.SplitHostPort(str) if err != nil { - host, splitPort, err = net.SplitHostPort(str + ":") // tack on empty port - } - if err != nil { - // ¯\_(ツ)_/¯ - host = str + host, port, err = net.SplitHostPort(str + ":") + // no error check here; return err at end of function } - if splitPort != "" { - port = splitPort - } else { - port = schemePort + // see if we can set port based off scheme + if port == "" { + if scheme == "http" { + port = "80" + } else if scheme == "https" { + port = "443" + } } - return + // repeated or conflicting scheme is confusing, so error + if scheme != "" && (port == "http" || port == "https") { + return address{}, fmt.Errorf("[%s] scheme specified twice in address", str) + } + + // standardize http and https ports to their respective port numbers + if port == "http" { + scheme = "http" + port = "80" + } else if port == "https" { + scheme = "https" + port = "443" + } + + return address{Original: input, Scheme: scheme, Host: host, Port: port}, err } // replaceEnvVars replaces environment variables that appear in the token -// and understands both the Unix $SYNTAX and Windows %SYNTAX%. +// and understands both the $UNIX and %WINDOWS% syntaxes. func replaceEnvVars(s string) string { s = replaceEnvReferences(s, "{%", "%}") s = replaceEnvReferences(s, "{$", "}") @@ -330,26 +349,26 @@ func replaceEnvReferences(s, refStart, refEnd string) string { } type ( - // serverBlock associates tokens with a list of addresses + // ServerBlock associates tokens with a list of addresses // and groups tokens by directive name. - serverBlock struct { + ServerBlock struct { Addresses []address Tokens map[string][]token } address struct { - Host, Port string + Original, Scheme, Host, Port string } ) -// HostList converts the list of addresses (hosts) -// that are associated with this server block into -// a slice of strings. Each string is a host:port -// combination. -func (sb serverBlock) HostList() []string { +// HostList converts the list of addresses that are +// associated with this server block into a slice of +// strings, where each address is as it was originally +// read from the input. +func (sb ServerBlock) HostList() []string { sbHosts := make([]string, len(sb.Addresses)) for j, addr := range sb.Addresses { - sbHosts[j] = net.JoinHostPort(addr.Host, addr.Port) + sbHosts[j] = addr.Original } return sbHosts } diff --git a/caddy/parse/parsing_test.go b/caddy/parse/parsing_test.go index 97c86808a..45f2f72ab 100644 --- a/caddy/parse/parsing_test.go +++ b/caddy/parse/parsing_test.go @@ -8,51 +8,55 @@ import ( func TestStandardAddress(t *testing.T) { for i, test := range []struct { - input string - host, port string - shouldErr bool + input string + scheme, host, port string + shouldErr bool }{ - {`localhost`, "localhost", "", false}, - {`localhost:1234`, "localhost", "1234", false}, - {`localhost:`, "localhost", "", false}, - {`0.0.0.0`, "0.0.0.0", "", false}, - {`127.0.0.1:1234`, "127.0.0.1", "1234", false}, - {`:1234`, "", "1234", false}, - {`[::1]`, "::1", "", false}, - {`[::1]:1234`, "::1", "1234", false}, - {`:`, "", "", false}, - {`localhost:http`, "localhost", "http", false}, - {`localhost:https`, "localhost", "https", false}, - {`:http`, "", "http", false}, - {`:https`, "", "https", false}, - {`http://localhost`, "localhost", "http", false}, - {`https://localhost`, "localhost", "https", false}, - {`http://127.0.0.1`, "127.0.0.1", "http", false}, - {`https://127.0.0.1`, "127.0.0.1", "https", false}, - {`http://[::1]`, "::1", "http", false}, - {`http://localhost:1234`, "localhost", "1234", false}, - {`https://127.0.0.1:1234`, "127.0.0.1", "1234", false}, - {`http://[::1]:1234`, "::1", "1234", false}, - {``, "", "", false}, - {`::1`, "::1", "", true}, - {`localhost::`, "localhost::", "", true}, - {`#$%@`, "#$%@", "", true}, + {`localhost`, "", "localhost", "", false}, + {`localhost:1234`, "", "localhost", "1234", false}, + {`localhost:`, "", "localhost", "", false}, + {`0.0.0.0`, "", "0.0.0.0", "", false}, + {`127.0.0.1:1234`, "", "127.0.0.1", "1234", false}, + {`:1234`, "", "", "1234", false}, + {`[::1]`, "", "::1", "", false}, + {`[::1]:1234`, "", "::1", "1234", false}, + {`:`, "", "", "", false}, + {`localhost:http`, "http", "localhost", "80", false}, + {`localhost:https`, "https", "localhost", "443", false}, + {`:http`, "http", "", "80", false}, + {`:https`, "https", "", "443", false}, + {`http://localhost:https`, "", "", "", true}, // conflict + {`http://localhost:http`, "", "", "", true}, // repeated scheme + {`http://localhost`, "http", "localhost", "80", false}, + {`https://localhost`, "https", "localhost", "443", false}, + {`http://127.0.0.1`, "http", "127.0.0.1", "80", false}, + {`https://127.0.0.1`, "https", "127.0.0.1", "443", false}, + {`http://[::1]`, "http", "::1", "80", false}, + {`http://localhost:1234`, "http", "localhost", "1234", false}, + {`https://127.0.0.1:1234`, "https", "127.0.0.1", "1234", false}, + {`http://[::1]:1234`, "http", "::1", "1234", false}, + {``, "", "", "", false}, + {`::1`, "", "::1", "", true}, + {`localhost::`, "", "localhost::", "", true}, + {`#$%@`, "", "#$%@", "", true}, } { - host, port, err := standardAddress(test.input) + actual, err := standardAddress(test.input) if err != nil && !test.shouldErr { - t.Errorf("Test %d: Expected no error, but had error: %v", i, err) + t.Errorf("Test %d (%s): Expected no error, but had error: %v", i, test.input, err) } if err == nil && test.shouldErr { - t.Errorf("Test %d: Expected error, but had none", i) + t.Errorf("Test %d (%s): Expected error, but had none", i, test.input) } - if host != test.host { - t.Errorf("Test %d: Expected host '%s', got '%s'", i, test.host, host) + if actual.Scheme != test.scheme { + t.Errorf("Test %d (%s): Expected scheme '%s', got '%s'", i, test.input, test.scheme, actual.Scheme) } - - if port != test.port { - t.Errorf("Test %d: Expected port '%s', got '%s'", i, test.port, port) + if actual.Host != test.host { + t.Errorf("Test %d (%s): Expected host '%s', got '%s'", i, test.input, test.host, actual.Host) + } + if actual.Port != test.port { + t.Errorf("Test %d (%s): Expected port '%s', got '%s'", i, test.input, test.port, actual.Port) } } } @@ -60,7 +64,7 @@ func TestStandardAddress(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.Next() // parseOne doesn't call Next() to start, so we must err := p.parseOne() @@ -74,19 +78,19 @@ func TestParseOneAndImport(t *testing.T) { tokens map[string]int // map of directive name to number of tokens expected }{ {`localhost`, false, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{}}, {`localhost dir1`, false, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 1, }}, {`localhost:1234 dir1 foo bar`, false, []address{ - {"localhost", "1234"}, + {"localhost:1234", "", "localhost", "1234"}, }, map[string]int{ "dir1": 3, }}, @@ -94,7 +98,7 @@ func TestParseOneAndImport(t *testing.T) { {`localhost { dir1 }`, false, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 1, }}, @@ -103,7 +107,7 @@ func TestParseOneAndImport(t *testing.T) { dir1 foo bar dir2 }`, false, []address{ - {"localhost", "1234"}, + {"localhost:1234", "", "localhost", "1234"}, }, map[string]int{ "dir1": 3, "dir2": 1, @@ -111,8 +115,8 @@ func TestParseOneAndImport(t *testing.T) { {`http://localhost https://localhost dir1 foo bar`, false, []address{ - {"localhost", "http"}, - {"localhost", "https"}, + {"http://localhost", "http", "localhost", "80"}, + {"https://localhost", "https", "localhost", "443"}, }, map[string]int{ "dir1": 3, }}, @@ -120,8 +124,8 @@ func TestParseOneAndImport(t *testing.T) { {`http://localhost https://localhost { dir1 foo bar }`, false, []address{ - {"localhost", "http"}, - {"localhost", "https"}, + {"http://localhost", "http", "localhost", "80"}, + {"https://localhost", "https", "localhost", "443"}, }, map[string]int{ "dir1": 3, }}, @@ -129,22 +133,22 @@ func TestParseOneAndImport(t *testing.T) { {`http://localhost, https://localhost { dir1 foo bar }`, false, []address{ - {"localhost", "http"}, - {"localhost", "https"}, + {"http://localhost", "http", "localhost", "80"}, + {"https://localhost", "https", "localhost", "443"}, }, map[string]int{ "dir1": 3, }}, {`http://localhost, { }`, true, []address{ - {"localhost", "http"}, + {"http://localhost", "http", "localhost", "80"}, }, map[string]int{}}, {`host1:80, http://host2.com dir1 foo bar dir2 baz`, false, []address{ - {"host1", "80"}, - {"host2.com", "http"}, + {"host1:80", "", "host1", "80"}, + {"http://host2.com", "http", "host2.com", "80"}, }, map[string]int{ "dir1": 3, "dir2": 2, @@ -153,9 +157,9 @@ func TestParseOneAndImport(t *testing.T) { {`http://host1.com, http://host2.com, https://host3.com`, false, []address{ - {"host1.com", "http"}, - {"host2.com", "http"}, - {"host3.com", "https"}, + {"http://host1.com", "http", "host1.com", "80"}, + {"http://host2.com", "http", "host2.com", "80"}, + {"https://host3.com", "https", "host3.com", "443"}, }, map[string]int{}}, {`http://host1.com:1234, https://host2.com @@ -163,8 +167,8 @@ func TestParseOneAndImport(t *testing.T) { bar baz } dir2`, false, []address{ - {"host1.com", "1234"}, - {"host2.com", "https"}, + {"http://host1.com:1234", "http", "host1.com", "1234"}, + {"https://host2.com", "https", "host2.com", "443"}, }, map[string]int{ "dir1": 6, "dir2": 1, @@ -177,7 +181,7 @@ func TestParseOneAndImport(t *testing.T) { dir2 { foo bar }`, false, []address{ - {"127.0.0.1", ""}, + {"127.0.0.1", "", "127.0.0.1", ""}, }, map[string]int{ "dir1": 5, "dir2": 5, @@ -185,13 +189,13 @@ func TestParseOneAndImport(t *testing.T) { {`127.0.0.1 unknown_directive`, true, []address{ - {"127.0.0.1", ""}, + {"127.0.0.1", "", "127.0.0.1", ""}, }, map[string]int{}}, {`localhost dir1 { foo`, true, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 3, }}, @@ -199,7 +203,7 @@ func TestParseOneAndImport(t *testing.T) { {`localhost dir1 { }`, false, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 3, }}, @@ -207,7 +211,7 @@ func TestParseOneAndImport(t *testing.T) { {`localhost dir1 { } }`, true, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 3, }}, @@ -219,7 +223,7 @@ func TestParseOneAndImport(t *testing.T) { } } dir2 foo bar`, false, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 7, "dir2": 3, @@ -230,7 +234,7 @@ func TestParseOneAndImport(t *testing.T) { {`localhost dir1 arg1 import import_test1.txt`, false, []address{ - {"localhost", ""}, + {"localhost", "", "localhost", ""}, }, map[string]int{ "dir1": 2, "dir2": 3, @@ -238,7 +242,7 @@ func TestParseOneAndImport(t *testing.T) { }}, {`import import_test2.txt`, false, []address{ - {"host1", ""}, + {"host1", "", "host1", ""}, }, map[string]int{ "dir1": 1, "dir2": 2, @@ -301,23 +305,23 @@ func TestParseAll(t *testing.T) { addresses [][]address // addresses per server block, in order }{ {`localhost`, false, [][]address{ - {{"localhost", ""}}, + {{"localhost", "", "localhost", ""}}, }}, {`localhost:1234`, false, [][]address{ - []address{{"localhost", "1234"}}, + []address{{"localhost:1234", "", "localhost", "1234"}}, }}, {`localhost:1234 { } localhost:2015 { }`, false, [][]address{ - []address{{"localhost", "1234"}}, - []address{{"localhost", "2015"}}, + []address{{"localhost:1234", "", "localhost", "1234"}}, + []address{{"localhost:2015", "", "localhost", "2015"}}, }}, {`localhost:1234, http://host2`, false, [][]address{ - []address{{"localhost", "1234"}, {"host2", "http"}}, + []address{{"localhost:1234", "", "localhost", "1234"}, {"http://host2", "http", "host2", "80"}}, }}, {`localhost:1234, http://host2,`, true, [][]address{}}, @@ -326,8 +330,8 @@ func TestParseAll(t *testing.T) { } https://host3.com, https://host4.com { }`, false, [][]address{ - []address{{"host1.com", "http"}, {"host2.com", "http"}}, - []address{{"host3.com", "https"}, {"host4.com", "https"}}, + []address{{"http://host1.com", "http", "host1.com", "80"}, {"http://host2.com", "http", "host2.com", "80"}}, + []address{{"https://host3.com", "https", "host3.com", "443"}, {"https://host4.com", "https", "host4.com", "443"}}, }}, } { p := testParser(test.input) diff --git a/server/config.go b/server/config.go index f9ec05fc3..9f0d0ced0 100644 --- a/server/config.go +++ b/server/config.go @@ -17,6 +17,9 @@ type Config struct { // The port to listen on Port string + // The protocol (http/https) to serve with this config; only set if user explicitly specifies it + Scheme string + // The directory from which to serve files Root string @@ -62,10 +65,11 @@ func (c Config) Address() string { // TLSConfig describes how TLS should be configured and used. type TLSConfig struct { - Enabled bool - Certificate string - Key string - LetsEncryptEmail string + Enabled bool + Certificate string + Key string + LetsEncryptEmail string + //DisableHTTPRedir bool // TODO: not a good idea - should we really allow it? OCSPStaple []byte Ciphers []uint16 ProtocolMinVersion uint16 diff --git a/server/config_test.go b/server/config_test.go index d94f3581e..8787e467b 100644 --- a/server/config_test.go +++ b/server/config_test.go @@ -18,8 +18,8 @@ func TestConfigAddress(t *testing.T) { t.Errorf("Expected '%s' but got '%s'", expected, actual) } - cfg = Config{Host: "::1", Port: "https"} - if actual, expected := cfg.Address(), "[::1]:https"; expected != actual { + cfg = Config{Host: "::1", Port: "443"} + if actual, expected := cfg.Address(), "[::1]:443"; expected != actual { t.Errorf("Expected '%s' but got '%s'", expected, actual) } }