mirror of
https://github.com/caddyserver/caddy.git
synced 2025-01-07 11:28:48 +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 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 {
|
||||||
key = strings.ToLower(key)
|
|
||||||
if _, dup := h.keysToSiteConfigs[key]; dup {
|
|
||||||
return serverBlocks, fmt.Errorf("duplicate site key: %s", key)
|
|
||||||
}
|
|
||||||
addr, err := standardizeAddress(key)
|
addr, err := standardizeAddress(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return serverBlocks, err
|
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
|
// Fill in address components from command line so that middleware
|
||||||
// have access to the correct information during setup
|
// have access to the correct information during setup
|
||||||
if addr.Host == "" && Host != DefaultHost {
|
if addr.Host == "" && Host != DefaultHost {
|
||||||
|
@ -145,7 +147,7 @@ func (h *httpContext) InspectServerBlocks(sourceFile string, serverBlocks []cadd
|
||||||
if addrCopy.Port == "" && Port == DefaultPort {
|
if addrCopy.Port == "" && Port == DefaultPort {
|
||||||
addrCopy.Port = Port
|
addrCopy.Port = Port
|
||||||
}
|
}
|
||||||
addrStr := strings.ToLower(addrCopy.String())
|
addrStr := addrCopy.String()
|
||||||
if otherSiteKey, dup := siteAddrs[addrStr]; dup {
|
if otherSiteKey, dup := siteAddrs[addrStr]; dup {
|
||||||
err := fmt.Errorf("duplicate site address: %s", addrStr)
|
err := fmt.Errorf("duplicate site address: %s", addrStr)
|
||||||
if (addrCopy.Host == Host && Host != DefaultHost) ||
|
if (addrCopy.Host == Host && Host != DefaultHost) ||
|
||||||
|
@ -249,12 +251,22 @@ func (h *httpContext) MakeServers() ([]caddy.Server, error) {
|
||||||
return servers, nil
|
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.
|
// GetConfig gets the SiteConfig that corresponds to c.
|
||||||
// If none exist (should only happen in tests), then a
|
// If none exist (should only happen in tests), then a
|
||||||
// new, empty one will be created.
|
// new, empty one will be created.
|
||||||
func GetConfig(c *caddy.Controller) *SiteConfig {
|
func GetConfig(c *caddy.Controller) *SiteConfig {
|
||||||
ctx := c.Context().(*httpContext)
|
ctx := c.Context().(*httpContext)
|
||||||
key := strings.ToLower(c.Key)
|
key := normalizedKey(c.Key)
|
||||||
if cfg, ok := ctx.keysToSiteConfigs[key]; ok {
|
if cfg, ok := ctx.keysToSiteConfigs[key]; ok {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
@ -358,6 +370,43 @@ func (a Address) VHost() string {
|
||||||
return a.Original
|
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
|
// standardizeAddress parses an address string into a structured format with separate
|
||||||
// scheme, host, port, and path portions, as well as the original input string.
|
// scheme, host, port, and path portions, as well as the original input string.
|
||||||
func standardizeAddress(str string) (Address, error) {
|
func standardizeAddress(str string) (Address, error) {
|
||||||
|
|
|
@ -18,6 +18,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/mholt/caddy"
|
"github.com/mholt/caddy"
|
||||||
"github.com/mholt/caddy/caddyfile"
|
"github.com/mholt/caddy/caddyfile"
|
||||||
)
|
)
|
||||||
|
@ -147,7 +151,20 @@ func TestInspectServerBlocksWithCustomDefaultPort(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Didn't expect an error, but got: %v", err)
|
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 {
|
if addr.Port != Port {
|
||||||
t.Errorf("Expected the port on the address to be set, but got: %#v", addr)
|
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) {
|
func TestGetConfig(t *testing.T) {
|
||||||
// case insensitivity for key
|
// case insensitivity for key
|
||||||
con := caddy.NewTestController("http", "")
|
con := caddy.NewTestController("http", "")
|
||||||
|
@ -201,6 +276,14 @@ func TestGetConfig(t *testing.T) {
|
||||||
if cfg == cfg3 {
|
if cfg == cfg3 {
|
||||||
t.Errorf("Expected different configs using when key is different; got %p and %p", 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) {
|
func TestDirectivesList(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue