mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-04 01:53:09 +03:00
httpserver: CaseSensitivePath applied to paths in site keys (#2034)
* different cases in path make different keys * Respect CaseSensitivePath variable when matching paths
This commit is contained in:
parent
f1eaae9b0d
commit
a8dfa9f0b7
2 changed files with 139 additions and 7 deletions
|
@ -122,15 +122,17 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
|
|||
// For each address in each server block, make a new config
|
||||
for _, sb := range serverBlocks {
|
||||
for _, key := range sb.Keys {
|
||||
key = strings.ToLower(key)
|
||||
if _, dup := h.keysToSiteConfigs[key]; dup {
|
||||
return serverBlocks, fmt.Errorf("duplicate site key: %s", key)
|
||||
}
|
||||
addr, err := standardizeAddress(key)
|
||||
if err != nil {
|
||||
return serverBlocks, err
|
||||
}
|
||||
|
||||
addr = addr.Normalize()
|
||||
key = addr.Key()
|
||||
if _, dup := h.keysToSiteConfigs[key]; dup {
|
||||
return serverBlocks, fmt.Errorf("duplicate site key: %s", key)
|
||||
}
|
||||
|
||||
// Fill in address components from command line so that middleware
|
||||
// have access to the correct information during setup
|
||||
if addr.Host == "" && Host != DefaultHost {
|
||||
|
@ -145,7 +147,7 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
|
|||
if addrCopy.Port == "" && Port == DefaultPort {
|
||||
addrCopy.Port = Port
|
||||
}
|
||||
addrStr := strings.ToLower(addrCopy.String())
|
||||
addrStr := addrCopy.String()
|
||||
if otherSiteKey, dup := siteAddrs[addrStr]; dup {
|
||||
err := fmt.Errorf("duplicate site address: %s", addrStr)
|
||||
if (addrCopy.Host == Host && Host != DefaultHost) ||
|
||||
|
@ -249,12 +251,22 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
|
|||
return servers, nil
|
||||
}
|
||||
|
||||
// normalizedKey returns "normalized" key representation:
|
||||
// scheme and host names are lowered, everything else stays the same
|
||||
func normalizedKey(key string) string {
|
||||
addr, err := standardizeAddress(key)
|
||||
if err != nil {
|
||||
return key
|
||||
}
|
||||
return addr.Normalize().Key()
|
||||
}
|
||||
|
||||
// GetConfig gets the SiteConfig that corresponds to c.
|
||||
// If none exist (should only happen in tests), then a
|
||||
// new, empty one will be created.
|
||||
func GetConfig(c *caddy.Controller) *SiteConfig {
|
||||
ctx := c.Context().(*httpContext)
|
||||
key := strings.ToLower(c.Key)
|
||||
key := normalizedKey(c.Key)
|
||||
if cfg, ok := ctx.keysToSiteConfigs[key]; ok {
|
||||
return cfg
|
||||
}
|
||||
|
@ -358,6 +370,43 @@ func (a Address) VHost() string {
|
|||
return a.Original
|
||||
}
|
||||
|
||||
// Normalize normalizes URL: turn scheme and host names into lower case
|
||||
func (a Address) Normalize() Address {
|
||||
path := a.Path
|
||||
if !CaseSensitivePath {
|
||||
path = strings.ToLower(path)
|
||||
}
|
||||
return Address{
|
||||
Original: a.Original,
|
||||
Scheme: strings.ToLower(a.Scheme),
|
||||
Host: strings.ToLower(a.Host),
|
||||
Port: a.Port,
|
||||
Path: path,
|
||||
}
|
||||
}
|
||||
|
||||
// Key is similar to String, just replaces scheme and host values with modified values.
|
||||
// Unlike String it doesn't add anything default (scheme, port, etc)
|
||||
func (a Address) Key() string {
|
||||
res := ""
|
||||
if a.Scheme != "" {
|
||||
res += a.Scheme + "://"
|
||||
}
|
||||
if a.Host != "" {
|
||||
res += a.Host
|
||||
}
|
||||
if a.Port != "" {
|
||||
if strings.HasPrefix(a.Original[len(res):], ":"+a.Port) {
|
||||
// insert port only if the original has its own explicit port
|
||||
res += ":" + a.Port
|
||||
}
|
||||
}
|
||||
if a.Path != "" {
|
||||
res += a.Path
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// standardizeAddress parses an address string into a structured format with separate
|
||||
// scheme, host, port, and path portions, as well as the original input string.
|
||||
func standardizeAddress(str string) (Address, error) {
|
||||
|
|
|
@ -18,6 +18,10 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"sort"
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyfile"
|
||||
)
|
||||
|
@ -147,7 +151,20 @@ func TestInspectServerBlocksWithCustomDefaultPort(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Didn't expect an error, but got: %v", err)
|
||||
}
|
||||
addr := ctx.keysToSiteConfigs["localhost"].Addr
|
||||
localhostKey := "localhost"
|
||||
item, ok := ctx.keysToSiteConfigs[localhostKey]
|
||||
if !ok {
|
||||
availableKeys := make(sort.StringSlice, len(ctx.keysToSiteConfigs))
|
||||
i := 0
|
||||
for key := range ctx.keysToSiteConfigs {
|
||||
availableKeys[i] = fmt.Sprintf("'%s'", key)
|
||||
i++
|
||||
}
|
||||
availableKeys.Sort()
|
||||
t.Errorf("`%s` not found within registered keys, only these are available: %s", localhostKey, strings.Join(availableKeys, ", "))
|
||||
return
|
||||
}
|
||||
addr := item.Addr
|
||||
if addr.Port != Port {
|
||||
t.Errorf("Expected the port on the address to be set, but got: %#v", addr)
|
||||
}
|
||||
|
@ -184,6 +201,64 @@ func TestInspectServerBlocksCaseInsensitiveKey(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestKeyNormalization(t *testing.T) {
|
||||
originalCaseSensitivePath := CaseSensitivePath
|
||||
defer func() {
|
||||
CaseSensitivePath = originalCaseSensitivePath
|
||||
}()
|
||||
CaseSensitivePath = true
|
||||
|
||||
caseSensitiveData := []struct {
|
||||
orig string
|
||||
res string
|
||||
}{
|
||||
{
|
||||
orig: "HTTP://A/ABCDEF",
|
||||
res: "http://a/ABCDEF",
|
||||
},
|
||||
{
|
||||
orig: "A/ABCDEF",
|
||||
res: "a/ABCDEF",
|
||||
},
|
||||
{
|
||||
orig: "A:2015/Port",
|
||||
res: "a:2015/Port",
|
||||
},
|
||||
}
|
||||
for _, item := range caseSensitiveData {
|
||||
v := normalizedKey(item.orig)
|
||||
if v != item.res {
|
||||
t.Errorf("Normalization of `%s` with CaseSensitivePath option set to true must be equal to `%s`, got `%s` instead", item.orig, item.res, v)
|
||||
}
|
||||
}
|
||||
|
||||
CaseSensitivePath = false
|
||||
caseInsensitiveData := []struct {
|
||||
orig string
|
||||
res string
|
||||
}{
|
||||
{
|
||||
orig: "HTTP://A/ABCDEF",
|
||||
res: "http://a/abcdef",
|
||||
},
|
||||
{
|
||||
orig: "A/ABCDEF",
|
||||
res: "a/abcdef",
|
||||
},
|
||||
{
|
||||
orig: "A:2015/Port",
|
||||
res: "a:2015/port",
|
||||
},
|
||||
}
|
||||
for _, item := range caseInsensitiveData {
|
||||
v := normalizedKey(item.orig)
|
||||
if v != item.res {
|
||||
t.Errorf("Normalization of `%s` with CaseSensitivePath option set to false must be equal to `%s`, got `%s` instead", item.orig, item.res, v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
// case insensitivity for key
|
||||
con := caddy.NewTestController("http", "")
|
||||
|
@ -201,6 +276,14 @@ func TestGetConfig(t *testing.T) {
|
|||
if cfg == cfg3 {
|
||||
t.Errorf("Expected different configs using when key is different; got %p and %p", cfg, cfg3)
|
||||
}
|
||||
|
||||
con.Key = "foo/foobar"
|
||||
cfg4 := GetConfig(con)
|
||||
con.Key = "foo/Foobar"
|
||||
cfg5 := GetConfig(con)
|
||||
if cfg4 == cfg5 {
|
||||
t.Errorf("Expected different cases in path to differentiate keys in general")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectivesList(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue