2023-04-19 01:30:38 +01:00
|
|
|
package router
|
|
|
|
|
|
|
|
import (
|
2023-08-28 23:09:29 +01:00
|
|
|
"fmt"
|
2023-07-22 01:11:47 +01:00
|
|
|
"github.com/1f349/violet/proxy"
|
2023-08-17 14:38:00 +01:00
|
|
|
"github.com/1f349/violet/proxy/websocket"
|
2023-07-22 01:11:47 +01:00
|
|
|
"github.com/1f349/violet/target"
|
2024-04-20 16:17:32 +01:00
|
|
|
"github.com/mrmelon54/trie"
|
2023-08-28 23:09:29 +01:00
|
|
|
"github.com/stretchr/testify/assert"
|
2023-04-19 01:30:38 +01:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"net/url"
|
2023-07-12 16:55:09 +01:00
|
|
|
"path"
|
2023-04-19 01:30:38 +01:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2023-06-06 00:39:12 +01:00
|
|
|
type routeTestBase struct {
|
|
|
|
path string
|
|
|
|
dst target.Route
|
|
|
|
tests map[string]string
|
|
|
|
}
|
|
|
|
|
2023-04-19 01:30:38 +01:00
|
|
|
type redirectTestBase struct {
|
|
|
|
path string
|
|
|
|
dst target.Redirect
|
|
|
|
tests map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
type mss map[string]string
|
|
|
|
|
2023-06-06 00:39:12 +01:00
|
|
|
var (
|
|
|
|
routeTests = []routeTestBase{
|
|
|
|
{"/", target.Route{}, mss{
|
|
|
|
"/": "/",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Flags: target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Flags: target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Flags: target.FlagPre}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/",
|
|
|
|
"/hello": "/hello",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Flags: target.FlagPre, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "/world/hello",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Flags: target.FlagPre | target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/",
|
|
|
|
"/hello": "/",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Route{Flags: target.FlagPre | target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "/world",
|
|
|
|
}},
|
|
|
|
{"/hello", target.Route{}, mss{
|
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Flags: target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Flags: target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Flags: target.FlagPre}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "/hi",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Flags: target.FlagPre, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "/world/hi",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Flags: target.FlagPre | target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "/",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Route{Flags: target.FlagPre | target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "/world",
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
redirectTests = []redirectTestBase{
|
|
|
|
{"/", target.Redirect{}, mss{
|
|
|
|
"/": "/",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Flags: target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Flags: target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Flags: target.FlagPre}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/",
|
|
|
|
"/hello": "/hello",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Flags: target.FlagPre, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "/world/hello",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Flags: target.FlagPre | target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/",
|
|
|
|
"/hello": "/",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/", target.Redirect{Flags: target.FlagPre | target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "/world",
|
|
|
|
"/hello": "/world",
|
|
|
|
}},
|
|
|
|
{"/hello", target.Redirect{}, mss{
|
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Flags: target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Flags: target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Flags: target.FlagPre}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "/hi",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Flags: target.FlagPre, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "/world/hi",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Flags: target.FlagPre | target.FlagAbs}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/",
|
|
|
|
"/hello/hi": "/",
|
|
|
|
}},
|
2023-07-12 16:55:09 +01:00
|
|
|
{"/hello", target.Redirect{Flags: target.FlagPre | target.FlagAbs, Dst: "world"}, mss{
|
2023-06-06 00:39:12 +01:00
|
|
|
"/": "",
|
|
|
|
"/hello": "/world",
|
|
|
|
"/hello/hi": "/world",
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestRouter_AddRoute(t *testing.T) {
|
|
|
|
transSecure := &fakeTransport{}
|
|
|
|
transInsecure := &fakeTransport{}
|
|
|
|
|
|
|
|
for _, i := range routeTests {
|
2023-08-17 14:38:00 +01:00
|
|
|
r := New(proxy.NewHybridTransportWithCalls(transSecure, transInsecure, &websocket.Server{}))
|
2023-06-06 00:39:12 +01:00
|
|
|
dst := i.dst
|
2023-07-12 16:55:09 +01:00
|
|
|
dst.Dst = path.Join("127.0.0.1:8080", dst.Dst)
|
|
|
|
dst.Src = path.Join("example.com", i.path)
|
2023-06-06 00:39:12 +01:00
|
|
|
t.Logf("Running tests for %#v\n", dst)
|
2023-07-12 16:55:09 +01:00
|
|
|
r.AddRoute(dst)
|
2023-06-06 00:39:12 +01:00
|
|
|
for k, v := range i.tests {
|
|
|
|
u1 := &url.URL{Scheme: "https", Host: "example.com", Path: k}
|
|
|
|
req, _ := http.NewRequest(http.MethodGet, u1.String(), nil)
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
r.ServeHTTP(rec, req)
|
|
|
|
if v == "" {
|
|
|
|
if transSecure.req != nil {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
2023-07-16 23:14:20 +01:00
|
|
|
t.Log(r.route["example.com"].String())
|
2023-06-06 00:39:12 +01:00
|
|
|
t.Fatalf("%s => %s\n", k, v)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if transSecure.req == nil {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
|
|
|
t.Log(r.route["example.com"].String())
|
|
|
|
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", k, v, k, "")
|
|
|
|
}
|
|
|
|
if v != transSecure.req.URL.Path {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
|
|
|
t.Log(r.route["example.com"].String())
|
|
|
|
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", k, v, k, transSecure.req.URL.Path)
|
|
|
|
}
|
|
|
|
transSecure.req = nil
|
|
|
|
}
|
2023-04-19 01:30:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestRouter_AddRedirect(t *testing.T) {
|
|
|
|
for _, i := range redirectTests {
|
2023-04-21 03:21:46 +01:00
|
|
|
r := New(nil)
|
2023-04-19 01:30:38 +01:00
|
|
|
dst := i.dst
|
2023-07-12 16:55:09 +01:00
|
|
|
dst.Dst = path.Join("example.com", dst.Dst)
|
2023-04-19 01:30:38 +01:00
|
|
|
dst.Code = http.StatusFound
|
2023-07-12 16:55:09 +01:00
|
|
|
dst.Src = path.Join("www.example.com", i.path)
|
2023-04-19 01:30:38 +01:00
|
|
|
t.Logf("Running tests for %#v\n", dst)
|
2023-07-12 16:55:09 +01:00
|
|
|
r.AddRedirect(dst)
|
2023-04-19 01:30:38 +01:00
|
|
|
for k, v := range i.tests {
|
|
|
|
u1 := &url.URL{Scheme: "https", Host: "example.com", Path: v}
|
|
|
|
if v == "" {
|
|
|
|
u1 = nil
|
|
|
|
}
|
|
|
|
u2 := &url.URL{Scheme: "https", Host: "www.example.com", Path: k}
|
|
|
|
assertHttpRedirect(t, r, http.StatusFound, outputUrl(u1), http.MethodGet, outputUrl(u2))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-06 00:39:12 +01:00
|
|
|
func assertHttpRedirect(t *testing.T, r *Router, code int, target, method, start string) {
|
|
|
|
res := httptest.NewRecorder()
|
|
|
|
req := httptest.NewRequest(method, start, nil)
|
|
|
|
r.ServeHTTP(res, req)
|
|
|
|
l := res.Header().Get("Location")
|
|
|
|
if target == "" {
|
|
|
|
if code == res.Code || "" != l {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
|
|
|
t.Log(r.redirect["www.example.com"].String())
|
|
|
|
t.Fatalf("%s => %s\n", start, target)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if code != res.Code || target != l {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
|
|
|
t.Log(r.redirect["www.example.com"].String())
|
|
|
|
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", start, target, start, l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-19 01:30:38 +01:00
|
|
|
func outputUrl(u *url.URL) string {
|
|
|
|
if u == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return u.String()
|
|
|
|
}
|
2023-06-19 16:27:36 +01:00
|
|
|
|
|
|
|
func TestRouter_AddWildcardRoute(t *testing.T) {
|
|
|
|
transSecure := &fakeTransport{}
|
|
|
|
transInsecure := &fakeTransport{}
|
|
|
|
|
|
|
|
for _, i := range routeTests {
|
2023-08-17 14:38:00 +01:00
|
|
|
r := New(proxy.NewHybridTransportWithCalls(transSecure, transInsecure, &websocket.Server{}))
|
2023-06-19 16:27:36 +01:00
|
|
|
dst := i.dst
|
2023-07-12 16:55:09 +01:00
|
|
|
dst.Dst = path.Join("127.0.0.1:8080", dst.Dst)
|
2023-07-16 23:14:20 +01:00
|
|
|
dst.Src = path.Join("*.example.com", i.path)
|
2023-06-19 16:27:36 +01:00
|
|
|
t.Logf("Running tests for %#v\n", dst)
|
2023-07-12 16:55:09 +01:00
|
|
|
r.AddRoute(dst)
|
2023-06-19 16:27:36 +01:00
|
|
|
for k, v := range i.tests {
|
2023-07-16 23:14:20 +01:00
|
|
|
u1 := &url.URL{Scheme: "https", Host: "test.example.com", Path: k}
|
2023-06-19 16:27:36 +01:00
|
|
|
req, _ := http.NewRequest(http.MethodGet, u1.String(), nil)
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
r.ServeHTTP(rec, req)
|
|
|
|
if v == "" {
|
|
|
|
if transSecure.req != nil {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
2023-07-16 23:14:20 +01:00
|
|
|
t.Log(r.route["*.example.com"].String())
|
2023-06-19 16:27:36 +01:00
|
|
|
t.Fatalf("%s => %s\n", k, v)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if transSecure.req == nil {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
2023-07-16 23:14:20 +01:00
|
|
|
t.Log(r.route["*.example.com"].String())
|
2023-06-19 16:27:36 +01:00
|
|
|
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", k, v, k, "")
|
|
|
|
}
|
|
|
|
if v != transSecure.req.URL.Path {
|
|
|
|
t.Logf("Test URL: %#v\n", req.URL)
|
2023-07-16 23:14:20 +01:00
|
|
|
t.Log(r.route["*.example.com"].String())
|
2023-06-19 16:27:36 +01:00
|
|
|
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", k, v, k, transSecure.req.URL.Path)
|
|
|
|
}
|
|
|
|
transSecure.req = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-28 23:09:29 +01:00
|
|
|
|
|
|
|
type fakeRoundTripper struct{}
|
|
|
|
|
|
|
|
func (f *fakeRoundTripper) RoundTrip(_ *http.Request) (*http.Response, error) {
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
rec.WriteHeader(http.StatusNotFound)
|
|
|
|
return rec.Result(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetServeData_Route(t *testing.T) {
|
|
|
|
hyb := proxy.NewHybridTransportWithCalls(&fakeRoundTripper{}, &fakeRoundTripper{}, nil)
|
|
|
|
req, err := http.NewRequest(http.MethodGet, "https://example.com/hello/world/this/is/a/test", nil)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
h := trie.BuildFromMap(map[string]target.Route{
|
|
|
|
"/hello/world": {Flags: target.FlagPre, Proxy: hyb},
|
|
|
|
})
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
pairs := h.GetAllKeyValues([]byte(req.URL.Path))
|
|
|
|
fmt.Printf("%#v\n", pairs)
|
|
|
|
assert.True(t, getServeData(rec, req, h))
|
|
|
|
}
|