2024-03-09 11:21:24 +02:00
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// SPDX-FileCopyrightText: © 2015 LabStack LLC and Echo contributors
|
|
|
|
|
2021-03-08 03:01:02 +02:00
|
|
|
package middleware
|
|
|
|
|
|
|
|
import (
|
2024-03-09 10:50:47 +02:00
|
|
|
"bufio"
|
|
|
|
"errors"
|
2021-03-08 03:01:02 +02:00
|
|
|
"github.com/stretchr/testify/assert"
|
2024-03-09 10:50:47 +02:00
|
|
|
"net"
|
2021-03-08 03:01:02 +02:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"regexp"
|
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2021-03-09 14:22:11 +02:00
|
|
|
func TestRewriteURL(t *testing.T) {
|
2021-03-08 03:01:02 +02:00
|
|
|
var testCases = []struct {
|
|
|
|
whenURL string
|
|
|
|
expectPath string
|
|
|
|
expectRawPath string
|
2021-03-09 14:22:11 +02:00
|
|
|
expectQuery string
|
|
|
|
expectErr string
|
2021-03-08 03:01:02 +02:00
|
|
|
}{
|
|
|
|
{
|
|
|
|
whenURL: "http://localhost:8080/old",
|
|
|
|
expectPath: "/new",
|
|
|
|
expectRawPath: "",
|
|
|
|
},
|
|
|
|
{ // encoded `ol%64` (decoded `old`) should not be rewritten to `/new`
|
|
|
|
whenURL: "/ol%64", // `%64` is decoded `d`
|
|
|
|
expectPath: "/old",
|
|
|
|
expectRawPath: "/ol%64",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
whenURL: "http://localhost:8080/users/+_+/orders/___++++?test=1",
|
|
|
|
expectPath: "/user/+_+/order/___++++",
|
|
|
|
expectRawPath: "",
|
2021-03-09 14:22:11 +02:00
|
|
|
expectQuery: "test=1",
|
2021-03-08 03:01:02 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
whenURL: "http://localhost:8080/users/%20a/orders/%20aa",
|
|
|
|
expectPath: "/user/ a/order/ aa",
|
|
|
|
expectRawPath: "",
|
|
|
|
},
|
|
|
|
{
|
2021-03-09 14:22:11 +02:00
|
|
|
whenURL: "http://localhost:8080/%47%6f%2f?test=1",
|
2021-03-08 03:01:02 +02:00
|
|
|
expectPath: "/Go/",
|
|
|
|
expectRawPath: "/%47%6f%2f",
|
2021-03-09 14:22:11 +02:00
|
|
|
expectQuery: "test=1",
|
2021-03-08 03:01:02 +02:00
|
|
|
},
|
|
|
|
{
|
|
|
|
whenURL: "/users/jill/orders/T%2FcO4lW%2Ft%2FVp%2F",
|
|
|
|
expectPath: "/user/jill/order/T/cO4lW/t/Vp/",
|
|
|
|
expectRawPath: "/user/jill/order/T%2FcO4lW%2Ft%2FVp%2F",
|
|
|
|
},
|
|
|
|
{ // do nothing, replace nothing
|
|
|
|
whenURL: "http://localhost:8080/user/jill/order/T%2FcO4lW%2Ft%2FVp%2F",
|
|
|
|
expectPath: "/user/jill/order/T/cO4lW/t/Vp/",
|
|
|
|
expectRawPath: "/user/jill/order/T%2FcO4lW%2Ft%2FVp%2F",
|
|
|
|
},
|
2021-03-09 14:22:11 +02:00
|
|
|
{
|
|
|
|
whenURL: "http://localhost:8080/static",
|
|
|
|
expectPath: "/static/path",
|
|
|
|
expectRawPath: "",
|
|
|
|
expectQuery: "role=AUTHOR&limit=1000",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
whenURL: "/static",
|
|
|
|
expectPath: "/static/path",
|
|
|
|
expectRawPath: "",
|
|
|
|
expectQuery: "role=AUTHOR&limit=1000",
|
|
|
|
},
|
2021-03-08 03:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
rules := map[*regexp.Regexp]string{
|
|
|
|
regexp.MustCompile("^/old$"): "/new",
|
|
|
|
regexp.MustCompile("^/users/(.*?)/orders/(.*?)$"): "/user/$1/order/$2",
|
2021-03-09 14:22:11 +02:00
|
|
|
regexp.MustCompile("^/static$"): "/static/path?role=AUTHOR&limit=1000",
|
2021-03-08 03:01:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.whenURL, func(t *testing.T) {
|
|
|
|
req := httptest.NewRequest(http.MethodGet, tc.whenURL, nil)
|
|
|
|
|
2021-03-09 14:22:11 +02:00
|
|
|
err := rewriteURL(rules, req)
|
2021-03-08 03:01:02 +02:00
|
|
|
|
2021-03-09 14:22:11 +02:00
|
|
|
if tc.expectErr != "" {
|
|
|
|
assert.EqualError(t, err, tc.expectErr)
|
|
|
|
} else {
|
|
|
|
assert.NoError(t, err)
|
|
|
|
}
|
2021-03-08 03:01:02 +02:00
|
|
|
assert.Equal(t, tc.expectPath, req.URL.Path) // Path field is stored in decoded form: /%47%6f%2f becomes /Go/.
|
|
|
|
assert.Equal(t, tc.expectRawPath, req.URL.RawPath) // RawPath, an optional field which only gets set if the default encoding is different from Path.
|
2021-03-09 14:22:11 +02:00
|
|
|
assert.Equal(t, tc.expectQuery, req.URL.RawQuery)
|
2021-03-08 03:01:02 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2024-03-09 10:50:47 +02:00
|
|
|
|
|
|
|
type testResponseWriterNoFlushHijack struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterNoFlushHijack) WriteHeader(statusCode int) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterNoFlushHijack) Write([]byte) (int, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterNoFlushHijack) Header() http.Header {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type testResponseWriterUnwrapper struct {
|
|
|
|
unwrapCalled int
|
|
|
|
rw http.ResponseWriter
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterUnwrapper) WriteHeader(statusCode int) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterUnwrapper) Write([]byte) (int, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterUnwrapper) Header() http.Header {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterUnwrapper) Unwrap() http.ResponseWriter {
|
|
|
|
w.unwrapCalled++
|
|
|
|
return w.rw
|
|
|
|
}
|
|
|
|
|
|
|
|
type testResponseWriterUnwrapperHijack struct {
|
|
|
|
testResponseWriterUnwrapper
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *testResponseWriterUnwrapperHijack) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
|
|
return nil, nil, errors.New("can hijack")
|
|
|
|
}
|