package rewriter import ( "context" "net/http" "forge.cadoles.com/cadoles/bouncer/internal/proxy/director" "forge.cadoles.com/cadoles/bouncer/internal/rule" ruleHTTP "forge.cadoles.com/cadoles/bouncer/internal/rule/http" "github.com/pkg/errors" ) type RequestVars struct { Request RequestVar `expr:"request"` OriginalURL URLVar `expr:"original_url"` } type URLVar struct { Scheme string `expr:"scheme"` Opaque string `expr:"opaque"` User UserVar `expr:"user"` Host string `expr:"host"` Path string `expr:"path"` RawPath string `expr:"raw_path"` RawQuery string `expr:"raw_query"` Fragment string `expr:"fragment"` RawFragment string `expr:"raw_fragment"` } type UserVar struct { Username string `expr:"username"` Password string `expr:"password"` } type RequestVar struct { Method string `expr:"method"` URL URLVar `expr:"url"` RawURL string `expr:"raw_url"` Proto string `expr:"proto"` ProtoMajor int `expr:"proto_major"` ProtoMinor int `expr:"proto_minor"` Header map[string][]string `expr:"header"` ContentLength int64 `expr:"content_length"` TransferEncoding []string `expr:"transfer_encoding"` Host string `expr:"host"` Trailer map[string][]string `expr:"trailer"` RemoteAddr string `expr:"remote_addr"` RequestURI string `expr:"request_uri"` } func (l *Layer) applyRequestRules(ctx context.Context, r *http.Request, layerRevision int, options *LayerOptions) error { rules := options.Rules.Request if len(rules) == 0 { return nil } engine, err := l.getRequestRuleEngine(ctx, layerRevision, options) if err != nil { return errors.WithStack(err) } originalURL, err := director.OriginalURL(ctx) if err != nil { return errors.WithStack(err) } vars := &RequestVars{ OriginalURL: URLVar{ Scheme: originalURL.Scheme, Opaque: originalURL.Opaque, User: UserVar{ Username: originalURL.User.Username(), Password: func() string { passwd, _ := originalURL.User.Password() return passwd }(), }, Host: originalURL.Host, Path: originalURL.Path, RawPath: originalURL.RawPath, RawQuery: originalURL.RawQuery, Fragment: originalURL.Fragment, RawFragment: originalURL.RawFragment, }, Request: RequestVar{ Method: r.Method, URL: URLVar{ Scheme: r.URL.Scheme, Opaque: r.URL.Opaque, User: UserVar{ Username: r.URL.User.Username(), Password: func() string { passwd, _ := r.URL.User.Password() return passwd }(), }, Host: r.URL.Host, Path: r.URL.Path, RawPath: r.URL.RawPath, RawQuery: r.URL.RawQuery, Fragment: r.URL.Fragment, RawFragment: r.URL.RawFragment, }, RawURL: r.URL.String(), Proto: r.Proto, ProtoMajor: r.ProtoMajor, ProtoMinor: r.ProtoMinor, Header: r.Header, ContentLength: r.ContentLength, TransferEncoding: r.TransferEncoding, Host: r.Host, Trailer: r.Trailer, RemoteAddr: r.RemoteAddr, RequestURI: r.RequestURI, }, } ctx = ruleHTTP.WithRequest(ctx, r) if _, err := engine.Apply(ctx, vars); err != nil { return errors.WithStack(err) } return nil } func (l *Layer) getRequestRuleEngine(ctx context.Context, layerRevision int, options *LayerOptions) (*rule.Engine[*RequestVars], error) { engine, err := l.requestRuleEngine.Get(ctx, layerRevision, options) if err != nil { return nil, errors.WithStack(err) } return engine, nil } type ResponseVars struct { OriginalURL URLVar `expr:"original_url"` Request RequestVar `expr:"request"` Response ResponseVar `expr:"response"` } type ResponseVar struct { Status string `expr:"status"` StatusCode int `expr:"status_code"` Proto string `expr:"proto"` ProtoMajor int `expr:"proto_major"` ProtoMinor int `expr:"proto_minor"` Header map[string][]string `expr:"header"` ContentLength int64 `expr:"content_length"` TransferEncoding []string `expr:"transfer_encoding"` Uncompressed bool `expr:"uncompressed"` Trailer map[string][]string `expr:"trailer"` } func (l *Layer) applyResponseRules(ctx context.Context, r *http.Response, layerRevision int, options *LayerOptions) error { rules := options.Rules.Response if len(rules) == 0 { return nil } engine, err := l.getResponseRuleEngine(ctx, layerRevision, options) if err != nil { return errors.WithStack(err) } originalURL, err := director.OriginalURL(ctx) if err != nil { return errors.WithStack(err) } vars := &ResponseVars{ OriginalURL: URLVar{ Scheme: originalURL.Scheme, Opaque: originalURL.Opaque, User: UserVar{ Username: originalURL.User.Username(), Password: func() string { passwd, _ := originalURL.User.Password() return passwd }(), }, Host: originalURL.Host, Path: originalURL.Path, RawPath: originalURL.RawPath, RawQuery: originalURL.RawQuery, Fragment: originalURL.Fragment, RawFragment: originalURL.RawFragment, }, Request: RequestVar{ Method: r.Request.Method, URL: URLVar{ Scheme: r.Request.URL.Scheme, Opaque: r.Request.URL.Opaque, User: UserVar{ Username: r.Request.URL.User.Username(), Password: func() string { passwd, _ := r.Request.URL.User.Password() return passwd }(), }, Host: r.Request.URL.Host, Path: r.Request.URL.Path, RawPath: r.Request.URL.RawPath, RawQuery: r.Request.URL.RawQuery, Fragment: r.Request.URL.Fragment, RawFragment: r.Request.URL.RawFragment, }, RawURL: r.Request.URL.String(), Proto: r.Request.Proto, ProtoMajor: r.Request.ProtoMajor, ProtoMinor: r.Request.ProtoMinor, Header: r.Request.Header, ContentLength: r.Request.ContentLength, TransferEncoding: r.Request.TransferEncoding, Host: r.Request.Host, Trailer: r.Request.Trailer, RemoteAddr: r.Request.RemoteAddr, RequestURI: r.Request.RequestURI, }, Response: ResponseVar{ Proto: r.Proto, ProtoMajor: r.ProtoMajor, ProtoMinor: r.ProtoMinor, Header: r.Header, ContentLength: r.ContentLength, TransferEncoding: r.TransferEncoding, Trailer: r.Trailer, Status: r.Status, StatusCode: r.StatusCode, }, } ctx = ruleHTTP.WithResponse(ctx, r) if _, err := engine.Apply(ctx, vars); err != nil { return errors.WithStack(err) } return nil } func (l *Layer) getResponseRuleEngine(ctx context.Context, layerRevision int, options *LayerOptions) (*rule.Engine[*ResponseVars], error) { engine, err := l.responseRuleEngine.Get(ctx, layerRevision, options) if err != nil { return nil, errors.WithStack(err) } return engine, nil }