mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-03-09 10:47:35 -04:00
allow proxy to route based on request method
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
@@ -45,10 +45,12 @@ type Policy struct {
|
||||
|
||||
// Route define forwarding routes
|
||||
type Route struct {
|
||||
Type RouteType `ocisConfig:"type"`
|
||||
Endpoint string `ocisConfig:"endpoint"`
|
||||
Backend string `ocisConfig:"backend"`
|
||||
ApacheVHost bool `ocisConfig:"apache-vhost"`
|
||||
Type RouteType `ocisConfig:"type"`
|
||||
// Method optionally limits the route to this HTTP method
|
||||
Method string `ocisConfig:"method"`
|
||||
Endpoint string `ocisConfig:"endpoint"`
|
||||
Backend string `ocisConfig:"backend"`
|
||||
ApacheVHost bool `ocisConfig:"apache-vhost"`
|
||||
}
|
||||
|
||||
// RouteType defines the type of a route
|
||||
|
||||
@@ -96,6 +96,15 @@ func DefaultPolicies() []config.Policy {
|
||||
Endpoint: "/remote.php/?preview=1",
|
||||
Backend: "http://localhost:9115",
|
||||
},
|
||||
{
|
||||
// TODO the actual REPORT goes to /dav/files/{username}, which is user specific ... how would this work in a spaces world?
|
||||
// TODO what paths are returned? the href contains the full path so it should be possible to return urls from other spaces?
|
||||
// TODO or we allow a REPORT on /dav/spaces to search all spaces and /dav/space/{spaceid} to search a specific space
|
||||
// send webdav REPORT requests to search service
|
||||
Method: "REPORT",
|
||||
Endpoint: "/dav/",
|
||||
Backend: "http://localhost:????", // TODO use registry?
|
||||
},
|
||||
{
|
||||
Endpoint: "/remote.php/",
|
||||
Backend: "http://localhost:9140",
|
||||
|
||||
@@ -27,7 +27,8 @@ import (
|
||||
// MultiHostReverseProxy extends "httputil" to support multiple hosts with different policies
|
||||
type MultiHostReverseProxy struct {
|
||||
httputil.ReverseProxy
|
||||
Directors map[string]map[config.RouteType]map[string]func(req *http.Request)
|
||||
// Directors holds policy route type method endpoint Director
|
||||
Directors map[string]map[config.RouteType]map[string]map[string]func(req *http.Request)
|
||||
PolicySelector policy.Selector
|
||||
logger log.Logger
|
||||
config *config.Config
|
||||
@@ -38,7 +39,7 @@ func NewMultiHostReverseProxy(opts ...Option) *MultiHostReverseProxy {
|
||||
options := newOptions(opts...)
|
||||
|
||||
rp := &MultiHostReverseProxy{
|
||||
Directors: make(map[string]map[config.RouteType]map[string]func(req *http.Request)),
|
||||
Directors: make(map[string]map[config.RouteType]map[string]map[string]func(req *http.Request)),
|
||||
logger: options.Logger,
|
||||
config: options.Config,
|
||||
}
|
||||
@@ -117,6 +118,7 @@ func (p *MultiHostReverseProxy) directorSelectionDirector(r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
method := ""
|
||||
// find matching director
|
||||
for _, rt := range config.RouteTypes {
|
||||
var handler func(string, url.URL) bool
|
||||
@@ -130,25 +132,36 @@ func (p *MultiHostReverseProxy) directorSelectionDirector(r *http.Request) {
|
||||
default:
|
||||
handler = p.prefixRouteMatcher
|
||||
}
|
||||
for endpoint := range p.Directors[pol][rt] {
|
||||
if p.Directors[pol][rt][r.Method] != nil {
|
||||
// use specific method
|
||||
method = r.Method
|
||||
}
|
||||
for endpoint := range p.Directors[pol][rt][method] {
|
||||
if handler(endpoint, *r.URL) {
|
||||
|
||||
p.logger.Debug().
|
||||
Str("policy", pol).
|
||||
Str("method", r.Method).
|
||||
Str("prefix", endpoint).
|
||||
Str("path", r.URL.Path).
|
||||
Str("routeType", string(rt)).
|
||||
Msg("director found")
|
||||
|
||||
p.Directors[pol][rt][endpoint](r)
|
||||
p.Directors[pol][rt][method][endpoint](r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// override default director with root. If any
|
||||
if p.Directors[pol][config.PrefixRoute]["/"] != nil {
|
||||
p.Directors[pol][config.PrefixRoute]["/"](r)
|
||||
switch {
|
||||
case p.Directors[pol][config.PrefixRoute][method]["/"] != nil:
|
||||
// try specific method
|
||||
p.Directors[pol][config.PrefixRoute][method]["/"](r)
|
||||
return
|
||||
case p.Directors[pol][config.PrefixRoute][""]["/"] != nil:
|
||||
// fallback to unspecific method
|
||||
p.Directors[pol][config.PrefixRoute][""]["/"](r)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -175,16 +188,19 @@ func singleJoiningSlash(a, b string) string {
|
||||
func (p *MultiHostReverseProxy) AddHost(policy string, target *url.URL, rt config.Route) {
|
||||
targetQuery := target.RawQuery
|
||||
if p.Directors[policy] == nil {
|
||||
p.Directors[policy] = make(map[config.RouteType]map[string]func(req *http.Request))
|
||||
p.Directors[policy] = make(map[config.RouteType]map[string]map[string]func(req *http.Request))
|
||||
}
|
||||
routeType := config.DefaultRouteType
|
||||
if rt.Type != "" {
|
||||
routeType = rt.Type
|
||||
}
|
||||
if p.Directors[policy][routeType] == nil {
|
||||
p.Directors[policy][routeType] = make(map[string]func(req *http.Request))
|
||||
p.Directors[policy][routeType] = make(map[string]map[string]func(req *http.Request))
|
||||
}
|
||||
p.Directors[policy][routeType][rt.Endpoint] = func(req *http.Request) {
|
||||
if p.Directors[policy][routeType][rt.Method] == nil {
|
||||
p.Directors[policy][routeType][rt.Method] = make(map[string]func(req *http.Request))
|
||||
}
|
||||
p.Directors[policy][routeType][rt.Method][rt.Endpoint] = func(req *http.Request) {
|
||||
req.URL.Scheme = target.Scheme
|
||||
req.URL.Host = target.Host
|
||||
// Apache deployments host addresses need to match on req.Host and req.URL.Host
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/owncloud/ocis/proxy/pkg/config"
|
||||
"github.com/owncloud/ocis/proxy/pkg/config/defaults"
|
||||
)
|
||||
|
||||
type matchertest struct {
|
||||
endpoint, target string
|
||||
matches bool
|
||||
method, endpoint, target string
|
||||
matches bool
|
||||
}
|
||||
|
||||
func TestPrefixRouteMatcher(t *testing.T) {
|
||||
@@ -99,3 +103,35 @@ func TestSingleJoiningSlash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirectorSelectionDirector(t *testing.T) {
|
||||
|
||||
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "ok")
|
||||
}))
|
||||
defer svr.Close()
|
||||
|
||||
p := NewMultiHostReverseProxy(Config(&config.Config{
|
||||
PolicySelector: &config.PolicySelector{
|
||||
Static: &config.StaticSelectorConf{
|
||||
Policy: "default",
|
||||
},
|
||||
},
|
||||
}))
|
||||
p.AddHost("default", &url.URL{Host: "ocdav"}, config.Route{Type: config.PrefixRoute, Method: "", Endpoint: "/dav", Backend: "ocdav"})
|
||||
p.AddHost("default", &url.URL{Host: "ocis-webdav"}, config.Route{Type: config.PrefixRoute, Method: "REPORT", Endpoint: "/dav", Backend: "ocis-webdav"})
|
||||
|
||||
table := []matchertest{
|
||||
{method: "PROPFIND", endpoint: "/dav/files/demo/", target: "ocdav"},
|
||||
{method: "REPORT", endpoint: "/dav/files/demo/", target: "ocis-webdav"},
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
r := httptest.NewRequest(http.MethodGet, "/dav/files/demo/", nil)
|
||||
p.directorSelectionDirector(r)
|
||||
if r.Host != test.target {
|
||||
t.Errorf("TestDirectorSelectionDirector got host %s expected %s", r.Host, test.target)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user