From 45121f21b8dc63c569cab453cdf019ac220c5d63 Mon Sep 17 00:00:00 2001 From: Meghna Srivastava Date: Thu, 2 Oct 2025 12:24:10 +0530 Subject: [PATCH 1/3] Create a proxy middlewear register and a proxy factory file --- proxy/middlewear/proxy/factory.go | 36 +++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 proxy/middlewear/proxy/factory.go diff --git a/proxy/middlewear/proxy/factory.go b/proxy/middlewear/proxy/factory.go new file mode 100644 index 000000000..4460146e1 --- /dev/null +++ b/proxy/middlewear/proxy/factory.go @@ -0,0 +1,36 @@ +package proxy + +import ( + "github.com/luraproject/lura/config" + "github.com/luraproject/lura/proxy/middleware" +) + +// Factory builds proxies with middleware support +type Factory struct { + baseFactory FactoryFunc +} + +// FactoryFunc is the standard proxy factory function +type FactoryFunc func(cfg *config.Backend) Proxy + +// NewFactory creates a factory with a given base function +func NewFactory(base FactoryFunc) *Factory { + return &Factory{baseFactory: base} +} + +// New builds a proxy with middlewares applied from config +func (f *Factory) New(cfg *config.Backend) Proxy { + p := f.baseFactory(cfg) + + if raw, ok := cfg.ExtraConfig["middlewares"]; ok { + if list, ok := raw.([]string); ok { + for _, name := range list { + if mw, exists := middleware.Get(name); exists { + p = mw(p) // wrap with middleware + } + } + } + } + + return p +} \ No newline at end of file From fc1785fb1ead2b43b6ee4d5270f05dd857c5bcc0 Mon Sep 17 00:00:00 2001 From: Meghna Srivastava Date: Thu, 2 Oct 2025 12:28:22 +0530 Subject: [PATCH 2/3] Add registry.go for proxy middlewear --- proxy/middlewear/proxy/registry.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 proxy/middlewear/proxy/registry.go diff --git a/proxy/middlewear/proxy/registry.go b/proxy/middlewear/proxy/registry.go new file mode 100644 index 000000000..ab8eff5fd --- /dev/null +++ b/proxy/middlewear/proxy/registry.go @@ -0,0 +1,19 @@ +package middleware + +import "github.com/luraproject/lura/proxy" + +// ProxyMiddleware wraps a proxy function +type ProxyMiddleware func(proxy.Proxy) proxy.Proxy + +var registry = map[string]ProxyMiddleware{} + +// Register allows custom middlewares to be added +func Register(name string, mw ProxyMiddleware) { + registry[name] = mw +} + +// Get retrieves a middleware by name +func Get(name string) (ProxyMiddleware, bool) { + mw, ok := registry[name] + return mw, ok +} \ No newline at end of file From 63e916b4b10608ce6d223131589c03dcf17eee3a Mon Sep 17 00:00:00 2001 From: Meghna Srivastava Date: Thu, 2 Oct 2025 12:34:13 +0530 Subject: [PATCH 3/3] Update factory.go for proxy middlewear --- proxy/middlewear/proxy/factory.go | 116 +++++++++++++++++++++++------- 1 file changed, 92 insertions(+), 24 deletions(-) diff --git a/proxy/middlewear/proxy/factory.go b/proxy/middlewear/proxy/factory.go index 4460146e1..8673d3dfa 100644 --- a/proxy/middlewear/proxy/factory.go +++ b/proxy/middlewear/proxy/factory.go @@ -1,36 +1,104 @@ +// SPDX-License-Identifier: Apache-2.0 + package proxy import ( - "github.com/luraproject/lura/config" - "github.com/luraproject/lura/proxy/middleware" + "github.com/luraproject/lura/v2/config" + "github.com/luraproject/lura/v2/logging" + "github.com/luraproject/lura/v2/sd" ) -// Factory builds proxies with middleware support -type Factory struct { - baseFactory FactoryFunc +// Factory creates proxies based on the received endpoint configuration. +// +// Both, factories and backend factories, create proxies but factories are designed as a stack makers +// because they are intended to generate the complete proxy stack for a given frontend endpoint +// the app would expose and they could wrap several proxies provided by a backend factory +type Factory interface { + New(cfg *config.EndpointConfig) (Proxy, error) +} + +// FactoryFunc type is an adapter to allow the use of ordinary functions as proxy factories. +// If f is a function with the appropriate signature, FactoryFunc(f) is a Factory that calls f. +type FactoryFunc func(*config.EndpointConfig) (Proxy, error) + +// New implements the Factory interface +func (f FactoryFunc) New(cfg *config.EndpointConfig) (Proxy, error) { return f(cfg) } + +// DefaultFactory returns a default http proxy factory with the injected logger +func DefaultFactory(logger logging.Logger) Factory { + return NewDefaultFactory(httpProxy, logger) +} + +// DefaultFactoryWithSubscriber returns a default proxy factory with the injected logger and subscriber factory +func DefaultFactoryWithSubscriber(logger logging.Logger, sF sd.SubscriberFactory) Factory { + return NewDefaultFactoryWithSubscriber(httpProxy, logger, sF) +} + +// NewDefaultFactory returns a default proxy factory with the injected proxy builder and logger +func NewDefaultFactory(backendFactory BackendFactory, logger logging.Logger) Factory { + sf := func(remote *config.Backend) sd.Subscriber { + return sd.GetRegister().Get(remote.SD)(remote) + } + return NewDefaultFactoryWithSubscriber(backendFactory, logger, sf) +} + +// NewDefaultFactoryWithSubscriber returns a default proxy factory with the injected proxy builder, +// logger and subscriber factory +func NewDefaultFactoryWithSubscriber(backendFactory BackendFactory, logger logging.Logger, sF sd.SubscriberFactory) Factory { + return defaultFactory{backendFactory, logger, sF} +} + +type defaultFactory struct { + backendFactory BackendFactory + logger logging.Logger + subscriberFactory sd.SubscriberFactory } -// FactoryFunc is the standard proxy factory function -type FactoryFunc func(cfg *config.Backend) Proxy +// New implements the Factory interface +func (pf defaultFactory) New(cfg *config.EndpointConfig) (p Proxy, err error) { + switch len(cfg.Backend) { + case 0: + err = ErrNoBackends + case 1: + p, err = pf.newSingle(cfg) + default: + p, err = pf.newMulti(cfg) + } + if err != nil { + return + } -// NewFactory creates a factory with a given base function -func NewFactory(base FactoryFunc) *Factory { - return &Factory{baseFactory: base} + p = NewPluginMiddleware(pf.logger, cfg)(p) + p = NewStaticMiddleware(pf.logger, cfg)(p) + return } -// New builds a proxy with middlewares applied from config -func (f *Factory) New(cfg *config.Backend) Proxy { - p := f.baseFactory(cfg) +func (pf defaultFactory) newMulti(cfg *config.EndpointConfig) (p Proxy, err error) { + backendProxy := make([]Proxy, len(cfg.Backend)) + for i, backend := range cfg.Backend { + backendProxy[i] = pf.newStack(backend) + } + p = NewMergeDataMiddleware(pf.logger, cfg)(backendProxy...) + p = NewFlatmapMiddleware(pf.logger, cfg)(p) + return +} - if raw, ok := cfg.ExtraConfig["middlewares"]; ok { - if list, ok := raw.([]string); ok { - for _, name := range list { - if mw, exists := middleware.Get(name); exists { - p = mw(p) // wrap with middleware - } - } - } - } +func (pf defaultFactory) newSingle(cfg *config.EndpointConfig) (Proxy, error) { + return pf.newStack(cfg.Backend[0]), nil +} - return p -} \ No newline at end of file +func (pf defaultFactory) newStack(backend *config.Backend) (p Proxy) { + p = pf.backendFactory(backend) + p = NewBackendPluginMiddleware(pf.logger, backend)(p) + p = NewGraphQLMiddleware(pf.logger, backend)(p) + p = NewFilterHeadersMiddleware(pf.logger, backend)(p) + p = NewLoadBalancedMiddlewareWithSubscriberAndLogger(pf.logger, pf.subscriberFactory(backend))(p) + if backend.ConcurrentCalls > 1 { + p = NewConcurrentMiddlewareWithLogger(pf.logger, backend)(p) + } + p = NewRequestBuilderMiddlewareWithLogger(pf.logger, backend)(p) + // we need to filter the input query strings before the request is constructed + // so the query strings are properly added to the URL: + p = NewFilterQueryStringsMiddleware(pf.logger, backend)(p) + return +}