mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-07-29 06:49:58 +00:00
TUN-528: Move cloudflared into a separate repo
This commit is contained in:
263
vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite.go
generated
vendored
Normal file
263
vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite.go
generated
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package rewrite is middleware for rewriting requests internally to
|
||||
// a different path.
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
// Result is the result of a rewrite
|
||||
type Result int
|
||||
|
||||
const (
|
||||
// RewriteIgnored is returned when rewrite is not done on request.
|
||||
RewriteIgnored Result = iota
|
||||
// RewriteDone is returned when rewrite is done on request.
|
||||
RewriteDone
|
||||
)
|
||||
|
||||
// Rewrite is middleware to rewrite request locations internally before being handled.
|
||||
type Rewrite struct {
|
||||
Next httpserver.Handler
|
||||
FileSys http.FileSystem
|
||||
Rules []httpserver.HandlerConfig
|
||||
}
|
||||
|
||||
// ServeHTTP implements the httpserver.Handler interface.
|
||||
func (rw Rewrite) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if rule := httpserver.ConfigSelector(rw.Rules).Select(r); rule != nil {
|
||||
rule.(Rule).Rewrite(rw.FileSys, r)
|
||||
}
|
||||
|
||||
return rw.Next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// Rule describes an internal location rewrite rule.
|
||||
type Rule interface {
|
||||
httpserver.HandlerConfig
|
||||
// Rewrite rewrites the internal location of the current request.
|
||||
Rewrite(http.FileSystem, *http.Request) Result
|
||||
}
|
||||
|
||||
// SimpleRule is a simple rewrite rule.
|
||||
type SimpleRule struct {
|
||||
Regexp *regexp.Regexp
|
||||
To string
|
||||
Negate bool
|
||||
}
|
||||
|
||||
// NewSimpleRule creates a new Simple Rule
|
||||
func NewSimpleRule(from, to string, negate bool) (*SimpleRule, error) {
|
||||
r, err := regexp.Compile(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SimpleRule{
|
||||
Regexp: r,
|
||||
To: to,
|
||||
Negate: negate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BasePath satisfies httpserver.Config
|
||||
func (s SimpleRule) BasePath() string { return "/" }
|
||||
|
||||
// Match satisfies httpserver.Config
|
||||
func (s *SimpleRule) Match(r *http.Request) bool {
|
||||
matches := regexpMatches(s.Regexp, "/", r.URL.Path)
|
||||
if s.Negate {
|
||||
return len(matches) == 0
|
||||
}
|
||||
return len(matches) > 0
|
||||
}
|
||||
|
||||
// Rewrite rewrites the internal location of the current request.
|
||||
func (s *SimpleRule) Rewrite(fs http.FileSystem, r *http.Request) Result {
|
||||
|
||||
// attempt rewrite
|
||||
return To(fs, r, s.To, newReplacer(r))
|
||||
}
|
||||
|
||||
// ComplexRule is a rewrite rule based on a regular expression
|
||||
type ComplexRule struct {
|
||||
// Path base. Request to this path and subpaths will be rewritten
|
||||
Base string
|
||||
|
||||
// Path to rewrite to
|
||||
To string
|
||||
|
||||
// Extensions to filter by
|
||||
Exts []string
|
||||
|
||||
// Request matcher
|
||||
httpserver.RequestMatcher
|
||||
|
||||
Regexp *regexp.Regexp
|
||||
}
|
||||
|
||||
// NewComplexRule creates a new RegexpRule. It returns an error if regexp
|
||||
// pattern (pattern) or extensions (ext) are invalid.
|
||||
func NewComplexRule(base, pattern, to string, ext []string, matcher httpserver.RequestMatcher) (ComplexRule, error) {
|
||||
// validate regexp if present
|
||||
var r *regexp.Regexp
|
||||
if pattern != "" {
|
||||
var err error
|
||||
r, err = regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return ComplexRule{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// validate extensions if present
|
||||
for _, v := range ext {
|
||||
if len(v) < 2 || (len(v) < 3 && v[0] == '!') {
|
||||
// check if no extension is specified
|
||||
if v != "/" && v != "!/" {
|
||||
return ComplexRule{}, fmt.Errorf("invalid extension %v", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use both IfMatcher and PathMatcher
|
||||
matcher = httpserver.MergeRequestMatchers(
|
||||
// If condition matcher
|
||||
matcher,
|
||||
// Base path matcher
|
||||
httpserver.PathMatcher(base),
|
||||
)
|
||||
|
||||
return ComplexRule{
|
||||
Base: base,
|
||||
To: to,
|
||||
Exts: ext,
|
||||
RequestMatcher: matcher,
|
||||
Regexp: r,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BasePath satisfies httpserver.Config
|
||||
func (r ComplexRule) BasePath() string { return r.Base }
|
||||
|
||||
// Match satisfies httpserver.Config.
|
||||
//
|
||||
// Though ComplexRule embeds a RequestMatcher, additional
|
||||
// checks are needed which requires a custom implementation.
|
||||
func (r ComplexRule) Match(req *http.Request) bool {
|
||||
// validate RequestMatcher
|
||||
// includes if and path
|
||||
if !r.RequestMatcher.Match(req) {
|
||||
return false
|
||||
}
|
||||
|
||||
// validate extensions
|
||||
if !r.matchExt(req.URL.Path) {
|
||||
return false
|
||||
}
|
||||
|
||||
// if regex is nil, ignore
|
||||
if r.Regexp == nil {
|
||||
return true
|
||||
}
|
||||
// otherwise validate regex
|
||||
return regexpMatches(r.Regexp, r.Base, req.URL.Path) != nil
|
||||
}
|
||||
|
||||
// Rewrite rewrites the internal location of the current request.
|
||||
func (r ComplexRule) Rewrite(fs http.FileSystem, req *http.Request) (re Result) {
|
||||
replacer := newReplacer(req)
|
||||
|
||||
// validate regexp if present
|
||||
if r.Regexp != nil {
|
||||
matches := regexpMatches(r.Regexp, r.Base, req.URL.Path)
|
||||
switch len(matches) {
|
||||
case 0:
|
||||
// no match
|
||||
return
|
||||
default:
|
||||
// set regexp match variables {1}, {2} ...
|
||||
|
||||
// url escaped values of ? and #.
|
||||
q, f := url.QueryEscape("?"), url.QueryEscape("#")
|
||||
|
||||
for i := 1; i < len(matches); i++ {
|
||||
// Special case of unescaped # and ? by stdlib regexp.
|
||||
// Reverse the unescape.
|
||||
if strings.ContainsAny(matches[i], "?#") {
|
||||
matches[i] = strings.NewReplacer("?", q, "#", f).Replace(matches[i])
|
||||
}
|
||||
|
||||
replacer.Set(fmt.Sprint(i), matches[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// attempt rewrite
|
||||
return To(fs, req, r.To, replacer)
|
||||
}
|
||||
|
||||
// matchExt matches rPath against registered file extensions.
|
||||
// Returns true if a match is found and false otherwise.
|
||||
func (r ComplexRule) matchExt(rPath string) bool {
|
||||
f := filepath.Base(rPath)
|
||||
ext := path.Ext(f)
|
||||
if ext == "" {
|
||||
ext = "/"
|
||||
}
|
||||
|
||||
mustUse := false
|
||||
for _, v := range r.Exts {
|
||||
use := true
|
||||
if v[0] == '!' {
|
||||
use = false
|
||||
v = v[1:]
|
||||
}
|
||||
|
||||
if use {
|
||||
mustUse = true
|
||||
}
|
||||
|
||||
if ext == v {
|
||||
return use
|
||||
}
|
||||
}
|
||||
|
||||
return !mustUse
|
||||
}
|
||||
|
||||
func regexpMatches(regexp *regexp.Regexp, base, rPath string) []string {
|
||||
if regexp != nil {
|
||||
// include trailing slash in regexp if present
|
||||
start := len(base)
|
||||
if strings.HasSuffix(base, "/") {
|
||||
start--
|
||||
}
|
||||
return regexp.FindStringSubmatch(rPath[start:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newReplacer(r *http.Request) httpserver.Replacer {
|
||||
return httpserver.NewReplacer(r, nil, "")
|
||||
}
|
176
vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite_test.go
generated
vendored
Normal file
176
vendor/github.com/mholt/caddy/caddyhttp/rewrite/rewrite_test.go
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func TestRewrite(t *testing.T) {
|
||||
rw := Rewrite{
|
||||
Next: httpserver.HandlerFunc(urlPrinter),
|
||||
Rules: []httpserver.HandlerConfig{
|
||||
newSimpleRule(t, "^/from$", "/to"),
|
||||
newSimpleRule(t, "^/a$", "/b"),
|
||||
newSimpleRule(t, "^/b$", "/b{uri}"),
|
||||
},
|
||||
FileSys: http.Dir("."),
|
||||
}
|
||||
|
||||
regexps := [][]string{
|
||||
{"/reg/", ".*", "/to", ""},
|
||||
{"/r/", "[a-z]+", "/toaz", "!.html|"},
|
||||
{"/path/", "[a-z0-9]", "/to/{path}", ""},
|
||||
{"/url/", "a([a-z0-9]*)s([A-Z]{2})", "/to/{rewrite_path}", ""},
|
||||
{"/ab/", "ab", "/ab?{query}", ".txt|"},
|
||||
{"/ab/", "ab", "/ab?type=html&{query}", ".html|"},
|
||||
{"/abc/", "ab", "/abc/{file}", ".html|"},
|
||||
{"/abcd/", "ab", "/a/{dir}/{file}", ".html|"},
|
||||
{"/abcde/", "ab", "/a#{fragment}", ".html|"},
|
||||
{"/ab/", `.*\.jpg`, "/ajpg", ""},
|
||||
{"/reggrp", `/ad/([0-9]+)([a-z]*)`, "/a{1}/{2}", ""},
|
||||
{"/reg2grp", `(.*)`, "/{1}", ""},
|
||||
{"/reg3grp", `(.*)/(.*)/(.*)`, "/{1}{2}{3}", ""},
|
||||
{"/hashtest", "(.*)", "/{1}", ""},
|
||||
}
|
||||
|
||||
for _, regexpRule := range regexps {
|
||||
var ext []string
|
||||
if s := strings.Split(regexpRule[3], "|"); len(s) > 1 {
|
||||
ext = s[:len(s)-1]
|
||||
}
|
||||
rule, err := NewComplexRule(regexpRule[0], regexpRule[1], regexpRule[2], ext, httpserver.IfMatcher{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rw.Rules = append(rw.Rules, rule)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
from string
|
||||
expectedTo string
|
||||
}{
|
||||
{"/from", "/to"},
|
||||
{"/a", "/b"},
|
||||
{"/b", "/b/b"},
|
||||
{"/aa", "/aa"},
|
||||
{"/", "/"},
|
||||
{"/a?foo=bar", "/b?foo=bar"},
|
||||
{"/asdf?foo=bar", "/asdf?foo=bar"},
|
||||
{"/foo#bar", "/foo#bar"},
|
||||
{"/a#foo", "/b#foo"},
|
||||
{"/reg/foo", "/to"},
|
||||
{"/re", "/re"},
|
||||
{"/r/", "/r/"},
|
||||
{"/r/123", "/r/123"},
|
||||
{"/r/a123", "/toaz"},
|
||||
{"/r/abcz", "/toaz"},
|
||||
{"/r/z", "/toaz"},
|
||||
{"/r/z.html", "/r/z.html"},
|
||||
{"/r/z.js", "/toaz"},
|
||||
{"/path/a1b2c", "/to/path/a1b2c"},
|
||||
{"/path/d3e4f", "/to/path/d3e4f"},
|
||||
{"/url/asAB", "/to/url/asAB"},
|
||||
{"/url/aBsAB", "/url/aBsAB"},
|
||||
{"/url/a00sAB", "/to/url/a00sAB"},
|
||||
{"/url/a0z0sAB", "/to/url/a0z0sAB"},
|
||||
{"/ab/aa", "/ab/aa"},
|
||||
{"/ab/ab", "/ab/ab"},
|
||||
{"/ab/ab.txt", "/ab"},
|
||||
{"/ab/ab.txt?name=name", "/ab?name=name"},
|
||||
{"/ab/ab.html?name=name", "/ab?type=html&name=name"},
|
||||
{"/abc/ab.html", "/abc/ab.html"},
|
||||
{"/abcd/abcd.html", "/a/abcd/abcd.html"},
|
||||
{"/abcde/abcde.html", "/a"},
|
||||
{"/abcde/abcde.html#1234", "/a#1234"},
|
||||
{"/ab/ab.jpg", "/ajpg"},
|
||||
{"/reggrp/ad/12", "/a12/"},
|
||||
{"/reggrp/ad/124a", "/a124/a"},
|
||||
{"/reggrp/ad/124abc", "/a124/abc"},
|
||||
{"/reg2grp/ad/124abc", "/ad/124abc"},
|
||||
{"/reg3grp/ad/aa/66", "/adaa66"},
|
||||
{"/reg3grp/ad612/n1n/ab", "/ad612n1nab"},
|
||||
{"/hashtest/a%20%23%20test", "/a%20%23%20test"},
|
||||
{"/hashtest/a%20%3F%20test", "/a%20%3F%20test"},
|
||||
{"/hashtest/a%20%3F%23test", "/a%20%3F%23test"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
req, err := http.NewRequest("GET", test.from, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
|
||||
}
|
||||
ctx := context.WithValue(req.Context(), httpserver.OriginalURLCtxKey, *req.URL)
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
rw.ServeHTTP(rec, req)
|
||||
|
||||
if got, want := rec.Body.String(), test.expectedTo; got != want {
|
||||
t.Errorf("Test %d: Expected URL to be '%s' but was '%s'", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestWordpress is a test for wordpress usecase.
|
||||
func TestWordpress(t *testing.T) {
|
||||
rw := Rewrite{
|
||||
Next: httpserver.HandlerFunc(urlPrinter),
|
||||
Rules: []httpserver.HandlerConfig{
|
||||
// both rules are same, thanks to Go regexp (confusion).
|
||||
newSimpleRule(t, "^/wp-admin", "{path} {path}/ /index.php?{query}", true),
|
||||
newSimpleRule(t, "^\\/wp-admin", "{path} {path}/ /index.php?{query}", true),
|
||||
},
|
||||
FileSys: http.Dir("."),
|
||||
}
|
||||
tests := []struct {
|
||||
from string
|
||||
expectedTo string
|
||||
}{
|
||||
{"/wp-admin", "/wp-admin"},
|
||||
{"/wp-admin/login.php", "/wp-admin/login.php"},
|
||||
{"/not-wp-admin/login.php?not=admin", "/index.php?not=admin"},
|
||||
{"/loophole", "/index.php"},
|
||||
{"/user?name=john", "/index.php?name=john"},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
req, err := http.NewRequest("GET", test.from, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: Could not create HTTP request: %v", i, err)
|
||||
}
|
||||
ctx := context.WithValue(req.Context(), httpserver.OriginalURLCtxKey, *req.URL)
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
rw.ServeHTTP(rec, req)
|
||||
|
||||
if got, want := rec.Body.String(), test.expectedTo; got != want {
|
||||
t.Errorf("Test %d: Expected URL to be '%s' but was '%s'", i, want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func urlPrinter(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
fmt.Fprint(w, r.URL.String())
|
||||
return 0, nil
|
||||
}
|
129
vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup.go
generated
vendored
Normal file
129
vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("rewrite", caddy.Plugin{
|
||||
ServerType: "http",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
// setup configures a new Rewrite middleware instance.
|
||||
func setup(c *caddy.Controller) error {
|
||||
rewrites, err := rewriteParse(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg := httpserver.GetConfig(c)
|
||||
|
||||
cfg.AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
|
||||
return Rewrite{
|
||||
Next: next,
|
||||
FileSys: http.Dir(cfg.Root),
|
||||
Rules: rewrites,
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func rewriteParse(c *caddy.Controller) ([]httpserver.HandlerConfig, error) {
|
||||
var rules []httpserver.HandlerConfig
|
||||
|
||||
for c.Next() {
|
||||
var rule Rule
|
||||
var err error
|
||||
var base = "/"
|
||||
var pattern, to string
|
||||
var ext []string
|
||||
var negate bool
|
||||
|
||||
args := c.RemainingArgs()
|
||||
|
||||
var matcher httpserver.RequestMatcher
|
||||
|
||||
switch len(args) {
|
||||
case 1:
|
||||
base = args[0]
|
||||
fallthrough
|
||||
case 0:
|
||||
// Integrate request matcher for 'if' conditions.
|
||||
matcher, err = httpserver.SetupIfMatcher(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for c.NextBlock() {
|
||||
if httpserver.IfMatcherKeyword(c) {
|
||||
continue
|
||||
}
|
||||
switch c.Val() {
|
||||
case "r", "regexp":
|
||||
if !c.NextArg() {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
pattern = c.Val()
|
||||
case "to":
|
||||
args1 := c.RemainingArgs()
|
||||
if len(args1) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
to = strings.Join(args1, " ")
|
||||
case "ext":
|
||||
args1 := c.RemainingArgs()
|
||||
if len(args1) == 0 {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
ext = args1
|
||||
default:
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
}
|
||||
// ensure to is specified
|
||||
if to == "" {
|
||||
return nil, c.ArgErr()
|
||||
}
|
||||
if rule, err = NewComplexRule(base, pattern, to, ext, matcher); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rules = append(rules, rule)
|
||||
|
||||
// the only unhandled case is 2 and above
|
||||
default:
|
||||
if args[0] == "not" {
|
||||
negate = true
|
||||
args = args[1:]
|
||||
}
|
||||
rule, err = NewSimpleRule(args[0], strings.Join(args[1:], " "), negate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
225
vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup_test.go
generated
vendored
Normal file
225
vendor/github.com/mholt/caddy/caddyhttp/rewrite/setup_test.go
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
c := caddy.NewTestController("http", `rewrite /from /to`)
|
||||
err := setup(c)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no errors, but got: %v", err)
|
||||
}
|
||||
mids := httpserver.GetConfig(c).Middleware()
|
||||
if len(mids) == 0 {
|
||||
t.Fatal("Expected middleware, had 0 instead")
|
||||
}
|
||||
|
||||
handler := mids[0](httpserver.EmptyNext)
|
||||
myHandler, ok := handler.(Rewrite)
|
||||
if !ok {
|
||||
t.Fatalf("Expected handler to be type Rewrite, got: %#v", handler)
|
||||
}
|
||||
|
||||
if !httpserver.SameNext(myHandler.Next, httpserver.EmptyNext) {
|
||||
t.Error("'Next' field of handler was not set properly")
|
||||
}
|
||||
|
||||
if len(myHandler.Rules) != 1 {
|
||||
t.Errorf("Expected handler to have %d rule, has %d instead", 1, len(myHandler.Rules))
|
||||
}
|
||||
}
|
||||
|
||||
// newSimpleRule is convenience test function for SimpleRule.
|
||||
func newSimpleRule(t *testing.T, from, to string, negate ...bool) Rule {
|
||||
var n bool
|
||||
if len(negate) > 0 {
|
||||
n = negate[0]
|
||||
}
|
||||
rule, err := NewSimpleRule(from, to, n)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return rule
|
||||
}
|
||||
|
||||
func TestRewriteParse(t *testing.T) {
|
||||
simpleTests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
expected []Rule
|
||||
}{
|
||||
{`rewrite /from /to`, false, []Rule{
|
||||
newSimpleRule(t, "/from", "/to"),
|
||||
}},
|
||||
{`rewrite /from /to
|
||||
rewrite a b`, false, []Rule{
|
||||
newSimpleRule(t, "/from", "/to"),
|
||||
newSimpleRule(t, "a", "b"),
|
||||
}},
|
||||
{`rewrite a`, true, []Rule{}},
|
||||
{`rewrite`, true, []Rule{}},
|
||||
{`rewrite a b c`, false, []Rule{
|
||||
newSimpleRule(t, "a", "b c"),
|
||||
}},
|
||||
{`rewrite not a b c`, false, []Rule{
|
||||
newSimpleRule(t, "a", "b c", true),
|
||||
}},
|
||||
}
|
||||
|
||||
for i, test := range simpleTests {
|
||||
actual, err := rewriteParse(caddy.NewTestController("http", test.input))
|
||||
|
||||
if err == nil && test.shouldErr {
|
||||
t.Errorf("Test %d didn't error, but it should have", i)
|
||||
} else if err != nil && !test.shouldErr {
|
||||
t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
|
||||
} else if err != nil && test.shouldErr {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
t.Fatalf("Test %d expected %d rules, but got %d",
|
||||
i, len(test.expected), len(actual))
|
||||
}
|
||||
|
||||
for j, e := range test.expected {
|
||||
actualRule := actual[j].(*SimpleRule)
|
||||
expectedRule := e.(*SimpleRule)
|
||||
|
||||
if actualRule.Regexp.String() != expectedRule.Regexp.String() {
|
||||
t.Errorf("Test %d, rule %d: Expected From=%s, got %s",
|
||||
i, j, expectedRule.Regexp.String(), actualRule.Regexp.String())
|
||||
}
|
||||
|
||||
if actualRule.To != expectedRule.To {
|
||||
t.Errorf("Test %d, rule %d: Expected To=%s, got %s",
|
||||
i, j, expectedRule.Regexp.String(), actualRule.Regexp.String())
|
||||
}
|
||||
|
||||
if actualRule.Negate != expectedRule.Negate {
|
||||
t.Errorf("Test %d, rule %d: Expected Negate=%v, got %v",
|
||||
i, j, expectedRule.Negate, actualRule.Negate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regexpTests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
expected []Rule
|
||||
}{
|
||||
{`rewrite {
|
||||
r .*
|
||||
to /to /index.php?
|
||||
}`, false, []Rule{
|
||||
ComplexRule{Base: "/", To: "/to /index.php?", Regexp: regexp.MustCompile(".*")},
|
||||
}},
|
||||
{`rewrite {
|
||||
regexp .*
|
||||
to /to
|
||||
ext / html txt
|
||||
}`, false, []Rule{
|
||||
ComplexRule{Base: "/", To: "/to", Exts: []string{"/", "html", "txt"}, Regexp: regexp.MustCompile(".*")},
|
||||
}},
|
||||
{`rewrite /path {
|
||||
r rr
|
||||
to /dest
|
||||
}
|
||||
rewrite / {
|
||||
regexp [a-z]+
|
||||
to /to /to2
|
||||
}
|
||||
`, false, []Rule{
|
||||
ComplexRule{Base: "/path", To: "/dest", Regexp: regexp.MustCompile("rr")},
|
||||
ComplexRule{Base: "/", To: "/to /to2", Regexp: regexp.MustCompile("[a-z]+")},
|
||||
}},
|
||||
{`rewrite {
|
||||
r .*
|
||||
}`, true, []Rule{
|
||||
ComplexRule{},
|
||||
}},
|
||||
{`rewrite {
|
||||
|
||||
}`, true, []Rule{
|
||||
ComplexRule{},
|
||||
}},
|
||||
{`rewrite /`, true, []Rule{
|
||||
ComplexRule{},
|
||||
}},
|
||||
{`rewrite {
|
||||
if {path} match /
|
||||
to /to
|
||||
}`, false, []Rule{
|
||||
ComplexRule{Base: "/", To: "/to"},
|
||||
}},
|
||||
}
|
||||
|
||||
for i, test := range regexpTests {
|
||||
actual, err := rewriteParse(caddy.NewTestController("http", test.input))
|
||||
|
||||
if err == nil && test.shouldErr {
|
||||
t.Errorf("Test %d didn't error, but it should have", i)
|
||||
} else if err != nil && !test.shouldErr {
|
||||
t.Errorf("Test %d errored, but it shouldn't have; got '%v'", i, err)
|
||||
} else if err != nil && test.shouldErr {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(actual) != len(test.expected) {
|
||||
t.Fatalf("Test %d expected %d rules, but got %d",
|
||||
i, len(test.expected), len(actual))
|
||||
}
|
||||
|
||||
for j, e := range test.expected {
|
||||
actualRule := actual[j].(ComplexRule)
|
||||
expectedRule := e.(ComplexRule)
|
||||
|
||||
if actualRule.Base != expectedRule.Base {
|
||||
t.Errorf("Test %d, rule %d: Expected Base=%s, got %s",
|
||||
i, j, expectedRule.Base, actualRule.Base)
|
||||
}
|
||||
|
||||
if actualRule.To != expectedRule.To {
|
||||
t.Errorf("Test %d, rule %d: Expected To=%s, got %s",
|
||||
i, j, expectedRule.To, actualRule.To)
|
||||
}
|
||||
|
||||
if fmt.Sprint(actualRule.Exts) != fmt.Sprint(expectedRule.Exts) {
|
||||
t.Errorf("Test %d, rule %d: Expected Ext=%v, got %v",
|
||||
i, j, expectedRule.To, actualRule.To)
|
||||
}
|
||||
|
||||
if actualRule.Regexp != nil {
|
||||
if actualRule.Regexp.String() != expectedRule.Regexp.String() {
|
||||
t.Errorf("Test %d, rule %d: Expected Pattern=%s, got %s",
|
||||
i, j, actualRule.Regexp.String(), expectedRule.Regexp.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if rules_fmt := fmt.Sprintf("%v", actual); strings.HasPrefix(rules_fmt, "%!") {
|
||||
t.Errorf("Test %d: Failed to string encode: %#v", i, rules_fmt)
|
||||
}
|
||||
}
|
||||
}
|
0
vendor/github.com/mholt/caddy/caddyhttp/rewrite/testdata/testdir/empty
generated
vendored
Normal file
0
vendor/github.com/mholt/caddy/caddyhttp/rewrite/testdata/testdir/empty
generated
vendored
Normal file
1
vendor/github.com/mholt/caddy/caddyhttp/rewrite/testdata/testfile
generated
vendored
Normal file
1
vendor/github.com/mholt/caddy/caddyhttp/rewrite/testdata/testfile
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
empty
|
103
vendor/github.com/mholt/caddy/caddyhttp/rewrite/to.go
generated
vendored
Normal file
103
vendor/github.com/mholt/caddy/caddyhttp/rewrite/to.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
// To attempts rewrite. It attempts to rewrite to first valid path
|
||||
// or the last path if none of the paths are valid.
|
||||
func To(fs http.FileSystem, r *http.Request, to string, replacer httpserver.Replacer) Result {
|
||||
tos := strings.Fields(to)
|
||||
|
||||
// try each rewrite paths
|
||||
t := ""
|
||||
query := ""
|
||||
for _, v := range tos {
|
||||
t = replacer.Replace(v)
|
||||
tparts := strings.SplitN(t, "?", 2)
|
||||
t = path.Clean(tparts[0])
|
||||
|
||||
if len(tparts) > 1 {
|
||||
query = tparts[1]
|
||||
}
|
||||
|
||||
// add trailing slash for directories, if present
|
||||
if strings.HasSuffix(tparts[0], "/") && !strings.HasSuffix(t, "/") {
|
||||
t += "/"
|
||||
}
|
||||
|
||||
// validate file
|
||||
if validFile(fs, t) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// validate resulting path
|
||||
u, err := url.Parse(t)
|
||||
if err != nil {
|
||||
// Let the user know we got here. Rewrite is expected but
|
||||
// the resulting url is invalid.
|
||||
log.Printf("[ERROR] rewrite: resulting path '%v' is invalid. error: %v", t, err)
|
||||
return RewriteIgnored
|
||||
}
|
||||
|
||||
// perform rewrite
|
||||
r.URL.Path = u.Path
|
||||
if query != "" {
|
||||
// overwrite query string if present
|
||||
r.URL.RawQuery = query
|
||||
}
|
||||
if u.Fragment != "" {
|
||||
// overwrite fragment if present
|
||||
r.URL.Fragment = u.Fragment
|
||||
}
|
||||
|
||||
return RewriteDone
|
||||
}
|
||||
|
||||
// validFile checks if file exists on the filesystem.
|
||||
// if file ends with `/`, it is validated as a directory.
|
||||
func validFile(fs http.FileSystem, file string) bool {
|
||||
if fs == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
f, err := fs.Open(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// directory
|
||||
if strings.HasSuffix(file, "/") {
|
||||
return stat.IsDir()
|
||||
}
|
||||
|
||||
// file
|
||||
return !stat.IsDir()
|
||||
}
|
66
vendor/github.com/mholt/caddy/caddyhttp/rewrite/to_test.go
generated
vendored
Normal file
66
vendor/github.com/mholt/caddy/caddyhttp/rewrite/to_test.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2015 Light Code Labs, LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
)
|
||||
|
||||
func TestTo(t *testing.T) {
|
||||
fs := http.Dir("testdata")
|
||||
tests := []struct {
|
||||
url string
|
||||
to string
|
||||
expected string
|
||||
}{
|
||||
{"/", "/somefiles", "/somefiles"},
|
||||
{"/somefiles", "/somefiles /index.php{uri}", "/index.php/somefiles"},
|
||||
{"/somefiles", "/testfile /index.php{uri}", "/testfile"},
|
||||
{"/somefiles", "/testfile/ /index.php{uri}", "/index.php/somefiles"},
|
||||
{"/somefiles", "/somefiles /index.php{uri}", "/index.php/somefiles"},
|
||||
{"/?a=b", "/somefiles /index.php?{query}", "/index.php?a=b"},
|
||||
{"/?a=b", "/testfile /index.php?{query}", "/testfile?a=b"},
|
||||
{"/?a=b", "/testdir /index.php?{query}", "/index.php?a=b"},
|
||||
{"/?a=b", "/testdir/ /index.php?{query}", "/testdir/?a=b"},
|
||||
{"/test?url=http://", " /p/{path}?{query}", "/p/test?url=http://"},
|
||||
{"/test?url=http://", " /p/{rewrite_path}?{query}", "/p/test?url=http://"},
|
||||
{"/test/?url=http://", " /{uri}", "/test/?url=http://"},
|
||||
}
|
||||
|
||||
uri := func(r *url.URL) string {
|
||||
uri := r.Path
|
||||
if r.RawQuery != "" {
|
||||
uri += "?" + r.RawQuery
|
||||
}
|
||||
return uri
|
||||
}
|
||||
for i, test := range tests {
|
||||
r, err := http.NewRequest("GET", test.url, nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), httpserver.OriginalURLCtxKey, *r.URL)
|
||||
r = r.WithContext(ctx)
|
||||
To(fs, r, test.to, newReplacer(r))
|
||||
if uri(r.URL) != test.expected {
|
||||
t.Errorf("Test %v: expected %v found %v", i, test.expected, uri(r.URL))
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user