Support for case insensitive paths using CASE_SENSITIVE_PATH environment variable.

This commit is contained in:
Abiola Ibrahim 2016-03-13 22:11:54 +01:00
parent 63e4352db7
commit 9f9fbf2e1b
3 changed files with 108 additions and 10 deletions

View File

@ -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.

View File

@ -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 {
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
View 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)
}
}
}