mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-03-25 22:00:56 +02:00
Ensure upstreams are sorted by longest first
This commit is contained in:
parent
8a06779d41
commit
075cb9c3a0
@ -6,6 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
@ -26,7 +27,7 @@ func NewProxy(upstreams options.Upstreams, sigData *options.SignatureData, write
|
|||||||
serveMux: mux.NewRouter(),
|
serveMux: mux.NewRouter(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, upstream := range upstreams {
|
for _, upstream := range sortByPathLongest(upstreams) {
|
||||||
if upstream.Static {
|
if upstream.Static {
|
||||||
if err := m.registerStaticResponseHandler(upstream, writer); err != nil {
|
if err := m.registerStaticResponseHandler(upstream, writer); err != nil {
|
||||||
return nil, fmt.Errorf("could not register static upstream %q: %v", upstream.ID, err)
|
return nil, fmt.Errorf("could not register static upstream %q: %v", upstream.ID, err)
|
||||||
@ -145,3 +146,33 @@ func registerTrailingSlashHandler(serveMux *mux.Router) {
|
|||||||
http.Redirect(rw, req, req.URL.String()+"/", http.StatusMovedPermanently)
|
http.Redirect(rw, req, req.URL.String()+"/", http.StatusMovedPermanently)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sortByPathLongest ensures that the upstreams are sorted by longest path.
|
||||||
|
// If rewrites are involved, a rewrite takes precedence over a non-rewrite.
|
||||||
|
// When two upstreams define rewrites, whichever has the longest path will take
|
||||||
|
// precedence (note this is the input to the rewrite logic).
|
||||||
|
// This does not account for when a rewrite would actually make the path shorter.
|
||||||
|
// This should maintain the sorting behaviour of the standard go serve mux.
|
||||||
|
func sortByPathLongest(in options.Upstreams) options.Upstreams {
|
||||||
|
sort.Slice(in, func(i, j int) bool {
|
||||||
|
iRW := in[i].RewriteTarget
|
||||||
|
jRW := in[j].RewriteTarget
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case iRW != "" && jRW != "":
|
||||||
|
// If both have a rewrite target, whichever has the longest pattern
|
||||||
|
// should go first
|
||||||
|
return len(in[i].Path) > len(in[j].Path)
|
||||||
|
case iRW != "" && jRW == "":
|
||||||
|
// Only one has rewrite, it goes first
|
||||||
|
return true
|
||||||
|
case iRW == "" && jRW != "":
|
||||||
|
// Only one has rewrite, it goes first
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
// Default to longest Path wins
|
||||||
|
return len(in[i].Path) > len(in[j].Path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
var _ = Describe("Proxy Suite", func() {
|
var _ = Describe("Proxy Suite", func() {
|
||||||
var upstreamServer http.Handler
|
var upstreamServer http.Handler
|
||||||
|
|
||||||
|
Context("multiUpstreamProxy", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
sigData := &options.SignatureData{Hash: crypto.SHA256, Key: "secret"}
|
sigData := &options.SignatureData{Hash: crypto.SHA256, Key: "secret"}
|
||||||
|
|
||||||
@ -54,6 +55,12 @@ var _ = Describe("Proxy Suite", func() {
|
|||||||
Static: true,
|
Static: true,
|
||||||
StaticCode: &accepted,
|
StaticCode: &accepted,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "static-backend-long",
|
||||||
|
Path: "/static/long",
|
||||||
|
Static: true,
|
||||||
|
StaticCode: &accepted,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ID: "bad-http-backend",
|
ID: "bad-http-backend",
|
||||||
Path: "/bad-http/",
|
Path: "/bad-http/",
|
||||||
@ -71,6 +78,17 @@ var _ = Describe("Proxy Suite", func() {
|
|||||||
RewriteTarget: "/different/backend/path/$1",
|
RewriteTarget: "/different/backend/path/$1",
|
||||||
URI: serverAddr,
|
URI: serverAddr,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "double-match-plain",
|
||||||
|
Path: "/double-match/",
|
||||||
|
URI: serverAddr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "double-match-rewrite",
|
||||||
|
Path: "^/double-match/(.*)",
|
||||||
|
RewriteTarget: "/double-match/rewrite/$1",
|
||||||
|
URI: serverAddr,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@ -262,5 +280,107 @@ var _ = Describe("Proxy Suite", func() {
|
|||||||
},
|
},
|
||||||
upstream: "static-backend-no-trailing-slash",
|
upstream: "static-backend-no-trailing-slash",
|
||||||
}),
|
}),
|
||||||
|
Entry("should match longest path first", &proxyTableInput{
|
||||||
|
target: "http://example.localhost/static/long",
|
||||||
|
response: testHTTPResponse{
|
||||||
|
code: 202,
|
||||||
|
header: map[string][]string{},
|
||||||
|
raw: "Authenticated",
|
||||||
|
},
|
||||||
|
upstream: "static-backend-long",
|
||||||
|
}),
|
||||||
|
Entry("should match rewrite path first", &proxyTableInput{
|
||||||
|
target: "http://example.localhost/double-match/foo",
|
||||||
|
response: testHTTPResponse{
|
||||||
|
code: 200,
|
||||||
|
header: map[string][]string{
|
||||||
|
contentType: {applicationJSON},
|
||||||
|
},
|
||||||
|
request: testHTTPRequest{
|
||||||
|
Method: "GET",
|
||||||
|
URL: "http://example.localhost/double-match/rewrite/foo",
|
||||||
|
Header: map[string][]string{
|
||||||
|
"Gap-Auth": {""},
|
||||||
|
"Gap-Signature": {"sha256 eYyUNdsrTmnvFpavpP8AdHGUGzqJ39QEjqn0/3fQPHA="},
|
||||||
|
},
|
||||||
|
Body: []byte{},
|
||||||
|
Host: "example.localhost",
|
||||||
|
RequestURI: "http://example.localhost/double-match/rewrite/foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
upstream: "double-match-rewrite",
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("sortByPathLongest", func() {
|
||||||
|
type sortByPathLongestTableInput struct {
|
||||||
|
input options.Upstreams
|
||||||
|
expectedOutput options.Upstreams
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpPath = options.Upstream{
|
||||||
|
Path: "/http/",
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpSubPath = options.Upstream{
|
||||||
|
Path: "/http/subpath/",
|
||||||
|
}
|
||||||
|
|
||||||
|
var longerPath = options.Upstream{
|
||||||
|
Path: "/longer-than-http",
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortPathWithRewrite = options.Upstream{
|
||||||
|
Path: "^/h/(.*)",
|
||||||
|
RewriteTarget: "/$1",
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortSubPathWithRewrite = options.Upstream{
|
||||||
|
Path: "^/h/bar/(.*)",
|
||||||
|
RewriteTarget: "/$1",
|
||||||
|
}
|
||||||
|
|
||||||
|
DescribeTable("short sort into the correct order",
|
||||||
|
func(in sortByPathLongestTableInput) {
|
||||||
|
Expect(sortByPathLongest(in.input)).To(Equal(in.expectedOutput))
|
||||||
|
},
|
||||||
|
Entry("with a mix of paths registered", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{httpPath, httpSubPath, shortSubPathWithRewrite, longerPath, shortPathWithRewrite},
|
||||||
|
expectedOutput: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite, longerPath, httpSubPath, httpPath},
|
||||||
|
}),
|
||||||
|
Entry("when a subpath is registered (in order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{httpSubPath, httpPath},
|
||||||
|
expectedOutput: options.Upstreams{httpSubPath, httpPath},
|
||||||
|
}),
|
||||||
|
Entry("when a subpath is registered (out of order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{httpPath, httpSubPath},
|
||||||
|
expectedOutput: options.Upstreams{httpSubPath, httpPath},
|
||||||
|
}),
|
||||||
|
Entry("when longer paths are registered (in order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{longerPath, httpPath},
|
||||||
|
expectedOutput: options.Upstreams{longerPath, httpPath},
|
||||||
|
}),
|
||||||
|
Entry("when longer paths are registered (out of order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{httpPath, longerPath},
|
||||||
|
expectedOutput: options.Upstreams{longerPath, httpPath},
|
||||||
|
}),
|
||||||
|
Entry("when a rewrite target is registered (in order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{shortPathWithRewrite, longerPath},
|
||||||
|
expectedOutput: options.Upstreams{shortPathWithRewrite, longerPath},
|
||||||
|
}),
|
||||||
|
Entry("when a rewrite target is registered (out of order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{longerPath, shortPathWithRewrite},
|
||||||
|
expectedOutput: options.Upstreams{shortPathWithRewrite, longerPath},
|
||||||
|
}),
|
||||||
|
Entry("with multiple rewrite targets registered (in order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite},
|
||||||
|
expectedOutput: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite},
|
||||||
|
}),
|
||||||
|
Entry("with multiple rewrite targets registered (out of order)", sortByPathLongestTableInput{
|
||||||
|
input: options.Upstreams{shortPathWithRewrite, shortSubPathWithRewrite},
|
||||||
|
expectedOutput: options.Upstreams{shortSubPathWithRewrite, shortPathWithRewrite},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user