package rewriter import ( "net/http" proxy "forge.cadoles.com/Cadoles/go-proxy" "forge.cadoles.com/Cadoles/go-proxy/wildcard" "forge.cadoles.com/cadoles/bouncer/internal/proxy/director" "forge.cadoles.com/cadoles/bouncer/internal/proxy/director/layer/util" "forge.cadoles.com/cadoles/bouncer/internal/rule" ruleHTTP "forge.cadoles.com/cadoles/bouncer/internal/rule/http" "forge.cadoles.com/cadoles/bouncer/internal/store" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) const LayerType store.LayerType = "rewriter" type Layer struct { requestRuleEngine *util.RevisionedRuleEngine[*RequestVars, *LayerOptions] responseRuleEngine *util.RevisionedRuleEngine[*ResponseVars, *LayerOptions] } func (l *Layer) LayerType() store.LayerType { return LayerType } func (l *Layer) Middleware(layer *store.Layer) proxy.Middleware { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() options, err := fromStoreOptions(layer.Options) if err != nil { logger.Error(ctx, "could not parse layer options", logger.E(errors.WithStack(err))) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } matches := wildcard.MatchAny(r.URL.String(), options.MatchURLs...) if !matches { next.ServeHTTP(w, r) return } if err := l.applyRequestRules(ctx, r, layer.Revision, options); err != nil { logger.Error(ctx, "could not apply request rules", logger.E(errors.WithStack(err))) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } next.ServeHTTP(w, r) } return http.HandlerFunc(fn) } } // ResponseTransformer implements director.ResponseTransformerLayer. func (l *Layer) ResponseTransformer(layer *store.Layer) proxy.ResponseTransformer { return func(r *http.Response) error { options, err := fromStoreOptions(layer.Options) if err != nil { return errors.WithStack(err) } matches := wildcard.MatchAny(r.Request.URL.String(), options.MatchURLs...) if !matches { return nil } ctx := r.Request.Context() if err := l.applyResponseRules(ctx, r, layer.Revision, options); err != nil { return errors.WithStack(err) } return nil } } func New(funcs ...OptionFunc) *Layer { return &Layer{ requestRuleEngine: util.NewRevisionedRuleEngine(func(options *LayerOptions) (*rule.Engine[*RequestVars], error) { engine, err := rule.NewEngine[*RequestVars]( rule.WithRules(options.Rules.Request...), ruleHTTP.WithRequestFuncs(), ) if err != nil { return nil, errors.WithStack(err) } return engine, nil }), responseRuleEngine: util.NewRevisionedRuleEngine(func(options *LayerOptions) (*rule.Engine[*ResponseVars], error) { engine, err := rule.NewEngine[*ResponseVars]( rule.WithRules(options.Rules.Response...), ruleHTTP.WithResponseFuncs(), ) if err != nil { return nil, errors.WithStack(err) } return engine, nil }), } } var ( _ director.MiddlewareLayer = &Layer{} _ director.ResponseTransformerLayer = &Layer{} )