mirror of
https://github.com/caddyserver/caddy.git
synced 2025-05-24 02:02:26 -04:00
* Fixed issue with {path} actually {uri} * Test added for path rewrite * add in uri_escaped * added rewrite_uri and test * fix broken test. Just checks for existance of rewrite header * gitignore * Use context to store uri value * ignore .vscode * tidy up, removal of comments and invalidated tests * Remove commented out code. * added comment as requested by lint * fixed spelling mistake * clarified code with variable name * added context for uri and test * added TODO comment to move consts
This commit is contained in:
parent
6aa0e30af3
commit
cfe52084aa
4
.gitignore
vendored
4
.gitignore
vendored
@ -14,4 +14,6 @@ access.log
|
|||||||
/*.conf
|
/*.conf
|
||||||
Caddyfile
|
Caddyfile
|
||||||
|
|
||||||
og_static/
|
og_static/
|
||||||
|
|
||||||
|
.vscode/
|
6
caddy.go
6
caddy.go
@ -871,9 +871,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CtxKey is a value for use with context.WithValue.
|
// CtxKey is a value for use with context.WithValue.
|
||||||
|
// TODO: Ideally CtxKey and consts will be moved to httpserver package.
|
||||||
|
// currently blocked by circular import with staticfiles.
|
||||||
type CtxKey string
|
type CtxKey string
|
||||||
|
|
||||||
// URLPathCtxKey is a context key. It can be used in HTTP handlers with
|
// URLPathCtxKey is a context key. It can be used in HTTP handlers with
|
||||||
// context.WithValue to access the original request URI that accompanied the
|
// context.WithValue to access the original request URI that accompanied the
|
||||||
// server request. The associated value will be of type string.
|
// server request. The associated value will be of type string.
|
||||||
const URLPathCtxKey CtxKey = "url_path"
|
const URLPathCtxKey CtxKey = "url_path"
|
||||||
|
|
||||||
|
// URIxRewriteCtxKey is a context key used to store original unrewritten
|
||||||
|
// URI in context.WithValue
|
||||||
|
const URIxRewriteCtxKey CtxKey = "caddy_rewrite_original_uri"
|
||||||
|
@ -33,11 +33,6 @@ type Handler struct {
|
|||||||
ServerPort string
|
ServerPort string
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a rewrite is performed, a header field of this name
|
|
||||||
// is added to the request
|
|
||||||
// It contains the original request URI before the rewrite.
|
|
||||||
const internalRewriteFieldName = "Caddy-Rewrite-Original-URI"
|
|
||||||
|
|
||||||
// ServeHTTP satisfies the httpserver.Handler interface.
|
// ServeHTTP satisfies the httpserver.Handler interface.
|
||||||
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
for _, rule := range h.Rules {
|
for _, rule := range h.Rules {
|
||||||
@ -219,12 +214,12 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]
|
|||||||
// Strip PATH_INFO from SCRIPT_NAME
|
// Strip PATH_INFO from SCRIPT_NAME
|
||||||
scriptName = strings.TrimSuffix(scriptName, pathInfo)
|
scriptName = strings.TrimSuffix(scriptName, pathInfo)
|
||||||
|
|
||||||
// Get the request URI. The request URI might be as it came in over the wire,
|
// Get the request URI from context. The request URI might be as it came in over the wire,
|
||||||
// or it might have been rewritten internally by the rewrite middleware (see issue #256).
|
// or it might have been rewritten internally by the rewrite middleware (see issue #256).
|
||||||
// If it was rewritten, there will be a header indicating the original URL,
|
// If it was rewritten, there will be a context value with the original URL,
|
||||||
// which is needed to get the correct RequestURI value for PHP apps.
|
// which is needed to get the correct RequestURI value for PHP apps.
|
||||||
reqURI := r.URL.RequestURI()
|
reqURI := r.URL.RequestURI()
|
||||||
if origURI := r.Header.Get(internalRewriteFieldName); origURI != "" {
|
if origURI, _ := r.Context().Value(caddy.URIxRewriteCtxKey).(string); origURI != "" {
|
||||||
reqURI = origURI
|
reqURI = origURI
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,11 +277,8 @@ func (h Handler) buildEnv(r *http.Request, rule Rule, fpath string) (map[string]
|
|||||||
env[envVar[0]] = replacer.Replace(envVar[1])
|
env[envVar[0]] = replacer.Replace(envVar[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all HTTP headers (except Caddy-Rewrite-Original-URI ) to env variables
|
// Add all HTTP headers to env variables
|
||||||
for field, val := range r.Header {
|
for field, val := range r.Header {
|
||||||
if strings.ToLower(field) == strings.ToLower(internalRewriteFieldName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
header := strings.ToUpper(field)
|
header := strings.ToUpper(field)
|
||||||
header = headerNameReplacer.Replace(header)
|
header = headerNameReplacer.Replace(header)
|
||||||
env["HTTP_"+header] = strings.Join(val, ", ")
|
env["HTTP_"+header] = strings.Join(val, ", ")
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -304,26 +303,6 @@ func TestBuildEnv(t *testing.T) {
|
|||||||
envExpected["CUSTOM_URI"] = "custom_uri/fgci_test.php?test=blabla"
|
envExpected["CUSTOM_URI"] = "custom_uri/fgci_test.php?test=blabla"
|
||||||
envExpected["CUSTOM_QUERY"] = "custom=true&test=blabla"
|
envExpected["CUSTOM_QUERY"] = "custom=true&test=blabla"
|
||||||
testBuildEnv(r, rule, fpath, envExpected)
|
testBuildEnv(r, rule, fpath, envExpected)
|
||||||
|
|
||||||
// 6. Test Caddy-Rewrite-Original-URI header is not removed
|
|
||||||
r = newReq()
|
|
||||||
rule.EnvVars = [][2]string{
|
|
||||||
{"HTTP_HOST", "{host}"},
|
|
||||||
{"CUSTOM_URI", "custom_uri{uri}"},
|
|
||||||
{"CUSTOM_QUERY", "custom=true&{query}"},
|
|
||||||
}
|
|
||||||
envExpected = newEnv()
|
|
||||||
envExpected["HTTP_HOST"] = "localhost:2015"
|
|
||||||
envExpected["CUSTOM_URI"] = "custom_uri/fgci_test.php?test=blabla"
|
|
||||||
envExpected["CUSTOM_QUERY"] = "custom=true&test=blabla"
|
|
||||||
httpFieldName := strings.ToUpper(internalRewriteFieldName)
|
|
||||||
envExpected["HTTP_"+httpFieldName] = ""
|
|
||||||
r.Header.Add(internalRewriteFieldName, "/apath/torewrite/index.php")
|
|
||||||
testBuildEnv(r, rule, fpath, envExpected)
|
|
||||||
if r.Header.Get(internalRewriteFieldName) == "" {
|
|
||||||
t.Errorf("Error: Header Expected %v", internalRewriteFieldName)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadTimeout(t *testing.T) {
|
func TestReadTimeout(t *testing.T) {
|
||||||
|
@ -240,15 +240,23 @@ func (r *replacer) getSubstitution(key string) string {
|
|||||||
case "{path}":
|
case "{path}":
|
||||||
// if a rewrite has happened, the original URI should be used as the path
|
// if a rewrite has happened, the original URI should be used as the path
|
||||||
// rather than the rewritten URI
|
// rather than the rewritten URI
|
||||||
path := r.request.Header.Get("Caddy-Rewrite-Original-URI")
|
var path string
|
||||||
if path == "" {
|
origpath, _ := r.request.Context().Value(caddy.URIxRewriteCtxKey).(string)
|
||||||
|
if origpath == "" {
|
||||||
path = r.request.URL.Path
|
path = r.request.URL.Path
|
||||||
|
} else {
|
||||||
|
parsedURL, _ := url.Parse(origpath)
|
||||||
|
path = parsedURL.Path
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
case "{path_escaped}":
|
case "{path_escaped}":
|
||||||
path := r.request.Header.Get("Caddy-Rewrite-Original-URI")
|
var path string
|
||||||
if path == "" {
|
origpath, _ := r.request.Context().Value(caddy.URIxRewriteCtxKey).(string)
|
||||||
|
if origpath == "" {
|
||||||
path = r.request.URL.Path
|
path = r.request.URL.Path
|
||||||
|
} else {
|
||||||
|
parsedURL, _ := url.Parse(origpath)
|
||||||
|
path = parsedURL.Path
|
||||||
}
|
}
|
||||||
return url.QueryEscape(path)
|
return url.QueryEscape(path)
|
||||||
case "{rewrite_path}":
|
case "{rewrite_path}":
|
||||||
@ -276,8 +284,20 @@ func (r *replacer) getSubstitution(key string) string {
|
|||||||
}
|
}
|
||||||
return port
|
return port
|
||||||
case "{uri}":
|
case "{uri}":
|
||||||
return r.request.URL.RequestURI()
|
uri, _ := r.request.Context().Value(caddy.URIxRewriteCtxKey).(string)
|
||||||
|
if uri == "" {
|
||||||
|
uri = r.request.URL.RequestURI()
|
||||||
|
}
|
||||||
|
return uri
|
||||||
case "{uri_escaped}":
|
case "{uri_escaped}":
|
||||||
|
uri, _ := r.request.Context().Value(caddy.URIxRewriteCtxKey).(string)
|
||||||
|
if uri == "" {
|
||||||
|
uri = r.request.URL.RequestURI()
|
||||||
|
}
|
||||||
|
return url.QueryEscape(uri)
|
||||||
|
case "{rewrite_uri}":
|
||||||
|
return r.request.URL.RequestURI()
|
||||||
|
case "{rewrite_uri_escaped}":
|
||||||
return url.QueryEscape(r.request.URL.RequestURI())
|
return url.QueryEscape(r.request.URL.RequestURI())
|
||||||
case "{when}":
|
case "{when}":
|
||||||
return now().Format(timeFormat)
|
return now().Format(timeFormat)
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package httpserver
|
package httpserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewReplacer(t *testing.T) {
|
func TestNewReplacer(t *testing.T) {
|
||||||
@ -149,6 +152,40 @@ func TestSet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test function to test that various placeholders hold correct values after a rewrite
|
||||||
|
// has been performed. The NewRequest actually contains the rewritten value.
|
||||||
|
func TestPathRewrite(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
recordRequest := NewResponseRecorder(w)
|
||||||
|
reader := strings.NewReader(`{"username": "dennis"}`)
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", "http://getcaddy.com/index.php?key=value", reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Request Formation Failed: %s\n", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.WithValue(request.Context(), caddy.URIxRewriteCtxKey, "a/custom/path.php?key=value")
|
||||||
|
request = request.WithContext(ctx)
|
||||||
|
|
||||||
|
repl := NewReplacer(request, recordRequest, "")
|
||||||
|
|
||||||
|
if repl.Replace("This path is '{path}'") != "This path is 'a/custom/path.php'" {
|
||||||
|
t.Error("Expected host {path} replacement failed (" + repl.Replace("This path is '{path}'") + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
if repl.Replace("This path is {rewrite_path}") != "This path is /index.php" {
|
||||||
|
t.Error("Expected host {rewrite_path} replacement failed (" + repl.Replace("This path is {rewrite_path}") + ")")
|
||||||
|
}
|
||||||
|
if repl.Replace("This path is '{uri}'") != "This path is 'a/custom/path.php?key=value'" {
|
||||||
|
t.Error("Expected host {uri} replacement failed (" + repl.Replace("This path is '{uri}'") + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
if repl.Replace("This path is {rewrite_uri}") != "This path is /index.php?key=value" {
|
||||||
|
t.Error("Expected host {rewrite_uri} replacement failed (" + repl.Replace("This path is {rewrite_uri}") + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestRound(t *testing.T) {
|
func TestRound(t *testing.T) {
|
||||||
var tests = map[time.Duration]time.Duration{
|
var tests = map[time.Duration]time.Duration{
|
||||||
// 599.935µs -> 560µs
|
// 599.935µs -> 560µs
|
||||||
|
@ -65,9 +65,6 @@ func (s SimpleRule) Match(r *http.Request) bool { return s.From == r.URL.Path }
|
|||||||
|
|
||||||
// Rewrite rewrites the internal location of the current request.
|
// Rewrite rewrites the internal location of the current request.
|
||||||
func (s SimpleRule) Rewrite(fs http.FileSystem, r *http.Request) Result {
|
func (s SimpleRule) Rewrite(fs http.FileSystem, r *http.Request) Result {
|
||||||
// take note of this rewrite for internal use by fastcgi
|
|
||||||
// all we need is the URI, not full URL
|
|
||||||
r.Header.Set(headerFieldName, r.URL.RequestURI())
|
|
||||||
|
|
||||||
// attempt rewrite
|
// attempt rewrite
|
||||||
return To(fs, r, s.To, newReplacer(r))
|
return To(fs, r, s.To, newReplacer(r))
|
||||||
@ -234,8 +231,3 @@ func (r *ComplexRule) regexpMatches(rPath string) []string {
|
|||||||
func newReplacer(r *http.Request) httpserver.Replacer {
|
func newReplacer(r *http.Request) httpserver.Replacer {
|
||||||
return httpserver.NewReplacer(r, nil, "")
|
return httpserver.NewReplacer(r, nil, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a rewrite is performed, this header is added to the request
|
|
||||||
// and is for internal use only, specifically the fastcgi middleware.
|
|
||||||
// It contains the original request URI before the rewrite.
|
|
||||||
const headerFieldName = "Caddy-Rewrite-Original-URI"
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package rewrite
|
package rewrite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy"
|
||||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,7 +51,7 @@ func To(fs http.FileSystem, r *http.Request, to string, replacer httpserver.Repl
|
|||||||
|
|
||||||
// take note of this rewrite for internal use by fastcgi
|
// take note of this rewrite for internal use by fastcgi
|
||||||
// all we need is the URI, not full URL
|
// all we need is the URI, not full URL
|
||||||
r.Header.Set(headerFieldName, r.URL.RequestURI())
|
*r = *r.WithContext(context.WithValue(r.Context(), caddy.URIxRewriteCtxKey, r.URL.RequestURI()))
|
||||||
|
|
||||||
// perform rewrite
|
// perform rewrite
|
||||||
r.URL.Path = u.Path
|
r.URL.Path = u.Path
|
||||||
|
Loading…
x
Reference in New Issue
Block a user