mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-29 12:16:16 +08:00
Support for case insensitive paths using CASE_SENSITIVE_PATH environment variable.
This commit is contained in:
parent
63e4352db7
commit
9f9fbf2e1b
|
@ -45,17 +45,18 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
|||
// but we also want to be flexible for the script we proxy to.
|
||||
|
||||
fpath := r.URL.Path
|
||||
|
||||
if idx, ok := middleware.IndexFile(h.FileSys, fpath, rule.IndexFiles); ok {
|
||||
fpath = idx
|
||||
// Index file present.
|
||||
// If request path cannot be split, return error.
|
||||
if !h.canSplit(fpath, rule) {
|
||||
if !rule.canSplit(fpath) {
|
||||
return http.StatusInternalServerError, ErrIndexMissingSplit
|
||||
}
|
||||
} else {
|
||||
// No index file present.
|
||||
// If request path cannot be split, ignore request.
|
||||
if !h.canSplit(fpath, rule) {
|
||||
if !rule.canSplit(fpath) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -165,10 +166,6 @@ func (h Handler) exists(path string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (h Handler) canSplit(path string, rule Rule) bool {
|
||||
return strings.Contains(path, rule.SplitPath)
|
||||
}
|
||||
|
||||
// buildEnv returns a set of CGI environment variables for the request.
|
||||
func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]string, error) {
|
||||
var env map[string]string
|
||||
|
@ -186,8 +183,8 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]
|
|||
}
|
||||
|
||||
// Split path in preparation for env variables.
|
||||
// Previous h.canSplit checks ensure this can never be -1.
|
||||
splitPos := strings.Index(fpath, rule.SplitPath)
|
||||
// Previous rule.canSplit checks ensure this can never be -1.
|
||||
splitPos := rule.splitPos(fpath)
|
||||
|
||||
// Request has the extension; path was split successfully
|
||||
docURI := fpath[:splitPos+len(rule.SplitPath)]
|
||||
|
@ -292,6 +289,20 @@ type Rule struct {
|
|||
EnvVars [][2]string
|
||||
}
|
||||
|
||||
// canSplit checks if path can split into two based on rule.SplitPath.
|
||||
func (r Rule) canSplit(path string) bool {
|
||||
return r.splitPos(path) >= 0
|
||||
}
|
||||
|
||||
// splitPos returns the index where path should be split
|
||||
// based on rule.SplitPath.
|
||||
func (r Rule) splitPos(path string) int {
|
||||
if middleware.CaseSensitivePath {
|
||||
return strings.Index(path, r.SplitPath)
|
||||
}
|
||||
return strings.Index(strings.ToLower(path), strings.ToLower(r.SplitPath))
|
||||
}
|
||||
|
||||
var (
|
||||
headerNameReplacer = strings.NewReplacer(" ", "_", "-", "_")
|
||||
// ErrIndexMissingSplit describes an index configuration error.
|
||||
|
|
|
@ -1,6 +1,32 @@
|
|||
package middleware
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const caseSensitivePathEnv = "CASE_SENSITIVE_PATH"
|
||||
|
||||
func init() {
|
||||
initCaseSettings()
|
||||
}
|
||||
|
||||
// CaseSensitivePath determines if paths should be case sensitive.
|
||||
// This is configurable via CASE_SENSITIVE_PATH environment variable.
|
||||
// It defaults to false.
|
||||
var CaseSensitivePath = true
|
||||
|
||||
// initCaseSettings loads case sensitivity config from environment variable.
|
||||
//
|
||||
// This could have been in init, but init cannot be called from tests.
|
||||
func initCaseSettings() {
|
||||
switch os.Getenv(caseSensitivePathEnv) {
|
||||
case "0", "false":
|
||||
CaseSensitivePath = false
|
||||
default:
|
||||
CaseSensitivePath = true
|
||||
}
|
||||
}
|
||||
|
||||
// Path represents a URI path, maybe with pattern characters.
|
||||
type Path string
|
||||
|
@ -11,5 +37,8 @@ type Path string
|
|||
// comparison; this method assures that paths can be
|
||||
// easily and consistently matched.
|
||||
func (p Path) Matches(other string) bool {
|
||||
return strings.HasPrefix(string(p), other)
|
||||
if CaseSensitivePath {
|
||||
return strings.HasPrefix(string(p), other)
|
||||
}
|
||||
return strings.HasPrefix(strings.ToLower(string(p)), strings.ToLower(other))
|
||||
}
|
||||
|
|
58
middleware/path_test.go
Normal file
58
middleware/path_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPathCaseSensitivity(t *testing.T) {
|
||||
tests := []struct {
|
||||
basePath string
|
||||
path string
|
||||
caseSensitive bool
|
||||
expected bool
|
||||
}{
|
||||
{"/", "/file", true, true},
|
||||
{"/a", "/file", true, false},
|
||||
{"/f", "/file", true, true},
|
||||
{"/f", "/File", true, false},
|
||||
{"/f", "/File", false, true},
|
||||
{"/file", "/file", true, true},
|
||||
{"/file", "/file", false, true},
|
||||
{"/files", "/file", false, false},
|
||||
{"/files", "/file", true, false},
|
||||
{"/folder", "/folder/file.txt", true, true},
|
||||
{"/folders", "/folder/file.txt", true, false},
|
||||
{"/folder", "/Folder/file.txt", false, true},
|
||||
{"/folders", "/Folder/file.txt", false, false},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
CaseSensitivePath = test.caseSensitive
|
||||
valid := Path(test.path).Matches(test.basePath)
|
||||
if test.expected != valid {
|
||||
t.Errorf("Test %d: Expected %v, found %v", i, test.expected, valid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathCaseSensitiveEnv(t *testing.T) {
|
||||
tests := []struct {
|
||||
envValue string
|
||||
expected bool
|
||||
}{
|
||||
{"1", true},
|
||||
{"0", false},
|
||||
{"false", false},
|
||||
{"true", true},
|
||||
{"", true},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
os.Setenv(caseSensitivePathEnv, test.envValue)
|
||||
initCaseSettings()
|
||||
if test.expected != CaseSensitivePath {
|
||||
t.Errorf("Test %d: Expected %v, found %v", i, test.expected, CaseSensitivePath)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user