mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-10-31 02:27:19 -04: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
					
				| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user