package rewriter import ( "net/http" "forge.cadoles.com/cadoles/bouncer/internal/rule" ruleHTTP "forge.cadoles.com/cadoles/bouncer/internal/rule/http" "github.com/pkg/errors" ) type RequestEnv struct { Request RequestInfo `expr:"request"` } type URLEnv struct { Scheme string `expr:"scheme"` Opaque string `expr:"opaque"` User UserInfoEnv `expr:"user"` Host string `expr:"host"` Path string `expr:"path"` RawPath string `expr:"rawPath"` RawQuery string `expr:"rawQuery"` Fragment string `expr:"fragment"` RawFragment string `expr:"rawFragment"` } type UserInfoEnv struct { Username string `expr:"username"` Password string `expr:"password"` } type RequestInfo struct { Method string `expr:"method"` URL URLEnv `expr:"url"` RawURL string `expr:"rawUrl"` Proto string `expr:"proto"` ProtoMajor int `expr:"protoMajor"` ProtoMinor int `expr:"protoMinor"` Header map[string][]string `expr:"header"` ContentLength int64 `expr:"contentLength"` TransferEncoding []string `expr:"transferEncoding"` Host string `expr:"host"` Trailer map[string][]string `expr:"trailer"` RemoteAddr string `expr:"remoteAddr"` RequestURI string `expr:"requestUri"` } func (l *Layer) applyRequestRules(r *http.Request, options *LayerOptions) error { rules := options.Rules.Request if len(rules) == 0 { return nil } engine, err := l.getRequestRuleEngine(r, options) if err != nil { return errors.WithStack(err) } env := &RequestEnv{ Request: RequestInfo{ Method: r.Method, URL: URLEnv{ Scheme: r.URL.Scheme, Opaque: r.URL.Opaque, User: UserInfoEnv{ 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, }, } if _, err := engine.Apply(env); err != nil { return errors.WithStack(err) } return nil } func (l *Layer) getRequestRuleEngine(r *http.Request, options *LayerOptions) (*rule.Engine[*RequestEnv], error) { engine, err := rule.NewEngine[*RequestEnv]( rule.WithRules(options.Rules.Request...), ruleHTTP.WithRequestFuncs(r), ) if err != nil { return nil, errors.WithStack(err) } return engine, nil } type ResponseEnv struct { Request RequestInfo `expr:"request"` Response ResponseInfo `expr:"response"` } type ResponseInfo struct { Status string `expr:"status"` StatusCode int `expr:"statusCode"` Proto string `expr:"proto"` ProtoMajor int `expr:"protoMajor"` ProtoMinor int `expr:"protoMinor"` Header map[string][]string `expr:"header"` ContentLength int64 `expr:"contentLength"` TransferEncoding []string `expr:"transferEncoding"` Uncompressed bool `expr:"uncompressed"` Trailer map[string][]string `expr:"trailer"` } func (l *Layer) applyResponseRules(r *http.Response, options *LayerOptions) error { rules := options.Rules.Response if len(rules) == 0 { return nil } engine, err := l.getResponseRuleEngine(r, options) if err != nil { return errors.WithStack(err) } env := &ResponseEnv{ Request: RequestInfo{ Method: r.Request.Method, URL: URLEnv{ Scheme: r.Request.URL.Scheme, Opaque: r.Request.URL.Opaque, User: UserInfoEnv{ 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: ResponseInfo{ 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, }, } if _, err := engine.Apply(env); err != nil { return errors.WithStack(err) } return nil } func (l *Layer) getResponseRuleEngine(r *http.Response, options *LayerOptions) (*rule.Engine[*ResponseEnv], error) { engine, err := rule.NewEngine[*ResponseEnv]( rule.WithRules(options.Rules.Response...), ruleHTTP.WithResponseFuncs(r), ) if err != nil { return nil, errors.WithStack(err) } return engine, nil }