mirror of
https://github.com/openfaas/faas.git
synced 2025-06-09 08:46:48 +00:00
Make 'URLPathTransformer' interface and implementation to do the function prefix trimming instead of baking it in. Also add a configuration option, 'pass_url_path_to_functions' to control whether the full path is passed to the functions or not.
Signed-off-by: Thomas E Lackey <telackey@bozemanpass.com>
This commit is contained in:
parent
7870b87c38
commit
decf9addb3
@ -29,12 +29,17 @@ type BaseURLResolver interface {
|
|||||||
Resolve(r *http.Request) string
|
Resolve(r *http.Request) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestURLPathTransformer Transform the incoming URL path for upstream requests
|
||||||
|
type URLPathTransformer interface {
|
||||||
|
Transform(r *http.Request) string
|
||||||
|
}
|
||||||
|
|
||||||
// MakeForwardingProxyHandler create a handler which forwards HTTP requests
|
// MakeForwardingProxyHandler create a handler which forwards HTTP requests
|
||||||
func MakeForwardingProxyHandler(proxy *types.HTTPClientReverseProxy, notifiers []HTTPNotifier, baseURLResolver BaseURLResolver) http.HandlerFunc {
|
func MakeForwardingProxyHandler(proxy *types.HTTPClientReverseProxy, notifiers []HTTPNotifier, baseURLResolver BaseURLResolver, urlPathTransformer URLPathTransformer) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
baseURL := baseURLResolver.Resolve(r)
|
baseURL := baseURLResolver.Resolve(r)
|
||||||
|
|
||||||
requestURL := r.URL.Path
|
requestURL := urlPathTransformer.Transform(r)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
@ -51,23 +56,7 @@ func MakeForwardingProxyHandler(proxy *types.HTTPClientReverseProxy, notifiers [
|
|||||||
}
|
}
|
||||||
|
|
||||||
func buildUpstreamRequest(r *http.Request, baseURL string, requestURL string) *http.Request {
|
func buildUpstreamRequest(r *http.Request, baseURL string, requestURL string) *http.Request {
|
||||||
url := baseURL
|
url := baseURL + requestURL
|
||||||
|
|
||||||
if requestURL != "" {
|
|
||||||
// When forwarding to a function, since the `/function/xyz` portion
|
|
||||||
// of a path like `/function/xyz/rest/of/path` is only used or needed
|
|
||||||
// by the Gateway, we want to trim it down to `/rest/of/path` for the
|
|
||||||
// upstream request. In the following regex, in the case of a match
|
|
||||||
// the requestURL will be at `0`, the function name at `1` and the
|
|
||||||
// rest of the path (the part we are interested in) at `2`.
|
|
||||||
matcher := functionMatcher.Copy()
|
|
||||||
parts := matcher.FindStringSubmatch(requestURL)
|
|
||||||
if 3 == len(parts) {
|
|
||||||
url += parts[2]
|
|
||||||
} else {
|
|
||||||
url += requestURL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(r.URL.RawQuery) > 0 {
|
if len(r.URL.RawQuery) > 0 {
|
||||||
url = fmt.Sprintf("%s?%s", url, r.URL.RawQuery)
|
url = fmt.Sprintf("%s?%s", url, r.URL.RawQuery)
|
||||||
@ -211,3 +200,46 @@ func (f FunctionAsHostBaseURLResolver) Resolve(r *http.Request) string {
|
|||||||
|
|
||||||
return fmt.Sprintf("http://%s%s:%d", svcName, suffix, watchdogPort)
|
return fmt.Sprintf("http://%s%s:%d", svcName, suffix, watchdogPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransparentURLPathTransformer passes the requested URL path through untouched.
|
||||||
|
type TransparentURLPathTransformer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform returns the URL path unchanged.
|
||||||
|
func (f TransparentURLPathTransformer) Transform(r *http.Request) string {
|
||||||
|
return r.URL.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathTruncatingURLPathTransformer always truncated the path to "/".
|
||||||
|
type PathTruncatingURLPathTransformer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform always return a path of "/".
|
||||||
|
func (f PathTruncatingURLPathTransformer) Transform(r *http.Request) string {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
// FunctionPrefixTrimmingURLPathTransformer removes the "/function/servicename/" prefix from the URL path.
|
||||||
|
type FunctionPrefixTrimmingURLPathTransformer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform removes the "/function/servicename/" prefix from the URL path.
|
||||||
|
func (f FunctionPrefixTrimmingURLPathTransformer) Transform(r *http.Request) string {
|
||||||
|
ret := r.URL.Path
|
||||||
|
|
||||||
|
if ret != "" {
|
||||||
|
// When forwarding to a function, since the `/function/xyz` portion
|
||||||
|
// of a path like `/function/xyz/rest/of/path` is only used or needed
|
||||||
|
// by the Gateway, we want to trim it down to `/rest/of/path` for the
|
||||||
|
// upstream request. In the following regex, in the case of a match
|
||||||
|
// the r.URL.Path will be at `0`, the function name at `1` and the
|
||||||
|
// rest of the path (the part we are interested in) at `2`.
|
||||||
|
matcher := functionMatcher.Copy()
|
||||||
|
parts := matcher.FindStringSubmatch(ret)
|
||||||
|
if 3 == len(parts) {
|
||||||
|
ret = parts[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
@ -164,7 +164,10 @@ func Test_buildUpstreamRequest_Body_Method_Query_Path(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream := buildUpstreamRequest(request, "http://xyz:8080", request.URL.Path)
|
transformer := FunctionPrefixTrimmingURLPathTransformer{}
|
||||||
|
transformedPath := transformer.Transform(request)
|
||||||
|
|
||||||
|
upstream := buildUpstreamRequest(request, "http://xyz:8080", transformedPath)
|
||||||
|
|
||||||
if request.Method != upstream.Method {
|
if request.Method != upstream.Method {
|
||||||
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// MakeQueuedProxy accepts work onto a queue
|
// MakeQueuedProxy accepts work onto a queue
|
||||||
func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueRequests queue.CanQueueRequests) http.HandlerFunc {
|
func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueRequests queue.CanQueueRequests, pathTransformer URLPathTransformer) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
|
|
||||||
@ -49,6 +49,7 @@ func MakeQueuedProxy(metrics metrics.MetricOptions, wildcard bool, canQueueReque
|
|||||||
Body: body,
|
Body: body,
|
||||||
Method: r.Method,
|
Method: r.Method,
|
||||||
QueryString: r.URL.RawQuery,
|
QueryString: r.URL.RawQuery,
|
||||||
|
Path: pathTransformer.Transform(r),
|
||||||
Header: r.Header,
|
Header: r.Header,
|
||||||
Host: r.Host,
|
Host: r.Host,
|
||||||
CallbackURL: callbackURL,
|
CallbackURL: callbackURL,
|
||||||
|
@ -12,6 +12,7 @@ type Request struct {
|
|||||||
Host string
|
Host string
|
||||||
Body []byte
|
Body []byte
|
||||||
Method string
|
Method string
|
||||||
|
Path string
|
||||||
QueryString string
|
QueryString string
|
||||||
Function string
|
Function string
|
||||||
CallbackURL *url.URL `json:"CallbackUrl"`
|
CallbackURL *url.URL `json:"CallbackUrl"`
|
||||||
|
@ -74,14 +74,23 @@ func main() {
|
|||||||
functionURLResolver = urlResolver
|
functionURLResolver = urlResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
faasHandlers.Proxy = handlers.MakeForwardingProxyHandler(reverseProxy, functionNotifiers, functionURLResolver)
|
urlTransformer := handlers.PathTruncatingURLPathTransformer{}
|
||||||
|
var functionURLTransformer handlers.URLPathTransformer
|
||||||
|
|
||||||
faasHandlers.RoutelessProxy = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
if config.PassURLPathsToFunctions {
|
||||||
faasHandlers.ListFunctions = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
functionURLTransformer = handlers.FunctionPrefixTrimmingURLPathTransformer{}
|
||||||
faasHandlers.DeployFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
} else {
|
||||||
faasHandlers.DeleteFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
functionURLTransformer = urlTransformer
|
||||||
faasHandlers.UpdateFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
}
|
||||||
queryFunction := handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
|
||||||
|
faasHandlers.Proxy = handlers.MakeForwardingProxyHandler(reverseProxy, functionNotifiers, functionURLResolver, functionURLTransformer)
|
||||||
|
|
||||||
|
faasHandlers.RoutelessProxy = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
faasHandlers.ListFunctions = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
faasHandlers.DeployFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
faasHandlers.DeleteFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
faasHandlers.UpdateFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
queryFunction := handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
|
||||||
alertHandler := plugin.NewExternalServiceQuery(*config.FunctionsProviderURL)
|
alertHandler := plugin.NewExternalServiceQuery(*config.FunctionsProviderURL)
|
||||||
faasHandlers.Alert = handlers.MakeAlertHandler(alertHandler)
|
faasHandlers.Alert = handlers.MakeAlertHandler(alertHandler)
|
||||||
@ -93,7 +102,7 @@ func main() {
|
|||||||
log.Fatalln(queueErr)
|
log.Fatalln(queueErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
faasHandlers.QueuedProxy = handlers.MakeCallIDMiddleware(handlers.MakeQueuedProxy(metricsOptions, true, natsQueue))
|
faasHandlers.QueuedProxy = handlers.MakeCallIDMiddleware(handlers.MakeQueuedProxy(metricsOptions, true, natsQueue, functionURLTransformer))
|
||||||
faasHandlers.AsyncReport = handlers.MakeAsyncReport(metricsOptions)
|
faasHandlers.AsyncReport = handlers.MakeAsyncReport(metricsOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +110,7 @@ func main() {
|
|||||||
faasHandlers.ListFunctions = metrics.AddMetricsHandler(faasHandlers.ListFunctions, prometheusQuery)
|
faasHandlers.ListFunctions = metrics.AddMetricsHandler(faasHandlers.ListFunctions, prometheusQuery)
|
||||||
faasHandlers.Proxy = handlers.MakeCallIDMiddleware(faasHandlers.Proxy)
|
faasHandlers.Proxy = handlers.MakeCallIDMiddleware(faasHandlers.Proxy)
|
||||||
|
|
||||||
faasHandlers.ScaleFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)
|
faasHandlers.ScaleFunction = handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)
|
||||||
|
|
||||||
if credentials != nil {
|
if credentials != nil {
|
||||||
faasHandlers.UpdateFunction =
|
faasHandlers.UpdateFunction =
|
||||||
@ -135,10 +144,10 @@ func main() {
|
|||||||
// r.StrictSlash(false) // This didn't work, so register routes twice.
|
// r.StrictSlash(false) // This didn't work, so register routes twice.
|
||||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", functionProxy)
|
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}", functionProxy)
|
||||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", functionProxy)
|
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/", functionProxy)
|
||||||
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/{params:.*}", faasHandlers.Proxy)
|
r.HandleFunc("/function/{name:[-a-zA-Z_0-9]+}/{params:.*}", functionProxy)
|
||||||
|
|
||||||
r.HandleFunc("/system/info", handlers.MakeInfoHandler(handlers.MakeForwardingProxyHandler(
|
r.HandleFunc("/system/info", handlers.MakeInfoHandler(handlers.MakeForwardingProxyHandler(
|
||||||
reverseProxy, forwardingNotifiers, urlResolver))).Methods(http.MethodGet)
|
reverseProxy, forwardingNotifiers, urlResolver, urlTransformer))).Methods(http.MethodGet)
|
||||||
|
|
||||||
r.HandleFunc("/system/alert", faasHandlers.Alert)
|
r.HandleFunc("/system/alert", faasHandlers.Alert)
|
||||||
|
|
||||||
@ -172,7 +181,7 @@ func main() {
|
|||||||
|
|
||||||
metricsHandler := metrics.PrometheusHandler()
|
metricsHandler := metrics.PrometheusHandler()
|
||||||
r.Handle("/metrics", metricsHandler)
|
r.Handle("/metrics", metricsHandler)
|
||||||
r.HandleFunc("/healthz", handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver)).Methods(http.MethodGet)
|
r.HandleFunc("/healthz", handlers.MakeForwardingProxyHandler(reverseProxy, forwardingNotifiers, urlResolver, urlTransformer)).Methods(http.MethodGet)
|
||||||
|
|
||||||
r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods(http.MethodGet)
|
r.Handle("/", http.RedirectHandler("/ui/", http.StatusMovedPermanently)).Methods(http.MethodGet)
|
||||||
|
|
||||||
|
@ -105,6 +105,8 @@ func (ReadConfig) Read(hasEnv HasEnv) GatewayConfig {
|
|||||||
cfg.DirectFunctions = parseBoolValue(hasEnv.Getenv("direct_functions"))
|
cfg.DirectFunctions = parseBoolValue(hasEnv.Getenv("direct_functions"))
|
||||||
cfg.DirectFunctionsSuffix = hasEnv.Getenv("direct_functions_suffix")
|
cfg.DirectFunctionsSuffix = hasEnv.Getenv("direct_functions_suffix")
|
||||||
|
|
||||||
|
cfg.PassURLPathsToFunctions = parseBoolValue(hasEnv.Getenv("pass_url_path_to_functions"))
|
||||||
|
|
||||||
cfg.UseBasicAuth = parseBoolValue(hasEnv.Getenv("basic_auth"))
|
cfg.UseBasicAuth = parseBoolValue(hasEnv.Getenv("basic_auth"))
|
||||||
|
|
||||||
secretPath := hasEnv.Getenv("secret_mount_path")
|
secretPath := hasEnv.Getenv("secret_mount_path")
|
||||||
@ -150,6 +152,10 @@ type GatewayConfig struct {
|
|||||||
// If set this will be used to resolve functions directly
|
// If set this will be used to resolve functions directly
|
||||||
DirectFunctionsSuffix string
|
DirectFunctionsSuffix string
|
||||||
|
|
||||||
|
// If set to true, the requested path will be passed along to the function, minus the "/function/xyz"
|
||||||
|
// prefix, else the path will be truncated to "/" regardless of what the client sends.
|
||||||
|
PassURLPathsToFunctions bool
|
||||||
|
|
||||||
// If set, reads secrets from file-system for enabling basic auth.
|
// If set, reads secrets from file-system for enabling basic auth.
|
||||||
UseBasicAuth bool
|
UseBasicAuth bool
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user