diff --git a/docs/actions/ServeHttp11Action.md b/docs/actions/ServeHttp11Action.md new file mode 100644 index 00000000..be260313 --- /dev/null +++ b/docs/actions/ServeHttp11Action.md @@ -0,0 +1,47 @@ +## serveHttp11Action + +### Description + +Force the downstream connection (client-to-proxy) to use HTTP/1.1. When the global ServeH2 option is enabled, this action overrides it for matched exchanges by only advertising HTTP/1.1 during ALPN negotiation with the client. + +### Evaluation scope + +Evaluation scope defines the timing where this filter will be applied. + +{.alert .alert-info} +::: +**onAuthorityReceived** This scope denotes the moment fluxzy is aware the destination authority. In a regular proxy connection, it will occur the moment where fluxzy parsed the CONNECT request. +::: + +### YAML configuration name + +serveHttp11Action + +### Settings + +This action has no specific characteristic + +### Example of usage + +The following examples apply this action to any exchanges + +Force HTTP/1.1 serving for a specific host even when ServeH2 is globally enabled. + +```yaml +rules: +- filter: + typeKind: AnyFilter + actions: + - typeKind: ServeHttp11Action +``` + + + +### .NET reference + +View definition of [ServeHttp11Action](https://docs.fluxzy.io/api/Fluxzy.Rules.Actions.ServeHttp11Action.html) for .NET integration. + +### See also + +This action has no related action + diff --git a/docs/searchable-items.json b/docs/searchable-items.json index 390f7095..a9c033bc 100644 --- a/docs/searchable-items.json +++ b/docs/searchable-items.json @@ -1 +1 @@ -[{"title":"anyFilter","description":"Select all exchanges","fullTypeName":"Fluxzy.Rules.Filters.AnyFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"commentSearchFilter","description":"Select exchanges by searching a string pattern into the comment property.","fullTypeName":"Fluxzy.Rules.Filters.CommentSearchFilter","category":"Filter","scope":"outOfScope"},{"title":"filterCollection","description":"FilterCollection is a combination of multiple filters with a merging operator (OR / AND).","fullTypeName":"Fluxzy.Rules.Filters.FilterCollection","category":"Filter","scope":"onAuthorityReceived"},{"title":"hasCommentFilter","description":"Select exchanges having comment.","fullTypeName":"Fluxzy.Rules.Filters.HasCommentFilter","category":"Filter","scope":"outOfScope"},{"title":"hasTagFilter","description":"Select exchanges having tag.","fullTypeName":"Fluxzy.Rules.Filters.HasTagFilter","category":"Filter","scope":"outOfScope"},{"title":"ipEgressFilter","description":"Select exchanges according to upstream IP address. Full IP notation is used from IPv6.","fullTypeName":"Fluxzy.Rules.Filters.IpEgressFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"ipIngressFilter","description":"Select exchanges according to client ip address. Full IP notation is used from IPv6.","fullTypeName":"Fluxzy.Rules.Filters.IpIngressFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"isWebSocketFilter","description":"Select websocket exchange.","fullTypeName":"Fluxzy.Rules.Filters.IsWebSocketFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"protobufFilter","description":"Select exchanges having a protobuf request or response body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header on both request and response.","fullTypeName":"Fluxzy.Rules.Filters.ProtobufFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"contentTypeXmlFilter","description":"Select exchanges having XML response body.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ContentTypeXmlFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"cssStyleFilter","description":"Select exchanges having response content type mime matching css.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.CssStyleFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"fontFilter","description":"Select exchanges having response content type matching a font payload.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.FontFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"htmlResponseFilter","description":"Select exchanges having HTML body. The content-type header is checked to determine if the content body is has text/html hint.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.HtmlResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"imageFilter","description":"Select exchanges having response content type mime matching image.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ImageFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"jsonResponseFilter","description":"Select exchanges having JSON response body. The content-type header is checked to determine if the content body is a JSON.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.JsonResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"networkErrorFilter","description":"Select exchanges that fails due to network error.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.NetworkErrorFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"protobufResponseFilter","description":"Select exchanges having protobuf response body. The content-type header is checked to determine if the content body is protobuf.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ProtobufResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"responseHeaderFilter","description":"Select exchanges according to response header values.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ResponseHeaderFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeClientErrorFilter","description":"Select exchanges that HTTP status code indicates a client error (4XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeClientErrorFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeFilter","description":"Select exchanges according to HTTP status code.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeRedirectionFilter","description":"Select exchanges that HTTP status code indicates a redirect (3XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeRedirectionFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeServerErrorFilter","description":"Select exchanges that HTTP status code indicates a server/intermediary error (5XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeServerErrorFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeSuccessFilter","description":"Select exchanges that HTTP status code indicates a successful request (2XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeSuccessFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"absoluteUriFilter","description":"Select exchanges according to URI (scheme, FQDN, path and query). Supports common string search option and regular expression.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.AbsoluteUriFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"agentLabelFilter","description":"Select exchanges according to configured source agent (user agent or process) with a regular string search.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.AgentLabelFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"authorityFilter","description":"Select exchange according to hostname and a port","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.AuthorityFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"deleteFilter","description":"Select exchanges with DELETE method","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.DeleteFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"formRequestFilter","description":"Select request sending \u0027multipart/form-data\u0027 or \u0027application/x-www-form-urlencoded\u0027 body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.FormRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"formUrlEncodedRequestFilter","description":"Select request sending \u0027application/x-www-form-urlencoded\u0027 body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.FormUrlEncodedRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"getFilter","description":"Select exchanges with GET method","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.GetFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"h11TrafficOnlyFilter","description":"Select HTTP/1.1 exchanges only.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.H11TrafficOnlyFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"h2TrafficOnlyFilter","description":"Select H2 exchanges only.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.H2TrafficOnlyFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasAnyCookieOnRequestFilter","description":"Select exchanges having any request cookie","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasAnyCookieOnRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasAuthorizationBearerFilter","description":"Select exchanges having bearer token in authorization.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasAuthorizationBearerFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasAuthorizationFilter","description":"Select exchanges having authorization header.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasAuthorizationFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasCookieOnRequestFilter","description":"Exchange having a request cookie with a specific name","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasCookieOnRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasRequestBodyFilter","description":"Select request having body.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasRequestBodyFilter","category":"Filter","scope":"responseBodyReceivedFromRemote"},{"title":"hasSetCookieOnResponseFilter","description":"Search for a cookie value present in a \u0060set-cookie\u0060 header response.If cookie name is not defined or empty, the filter will returns any cookie having the value.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasSetCookieOnResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"hostFilter","description":"Select exchanges according to hostname (excluding port). To select authority (combination of host:port), use \u003Cgoto\u003EAuthorityFilter\u003C/goto\u003E.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HostFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"isGrpcFilter","description":"Select gRPC exchanges only. Filtering is made by inspecting value of \u0060Content-Type\u0060 header.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.IsGrpcFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"isSecureFilter","description":"Select secure exchange only (non plain HTTP).","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.IsSecureFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"jsonRequestFilter","description":"Select request sending JSON body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.JsonRequestFilter","category":"Filter","scope":"requestBodyReceivedFromClient"},{"title":"methodFilter","description":"Select exchanges according to request method.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.MethodFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"multipartDataRequestFilter","description":"Select request sending \u0027multipart/form-data\u0027 body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.MultipartDataRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"patchFilter","description":"Select exchanges with PATCH method","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PatchFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"pathFilter","description":"Select exchanges according to url path. Path includes query string if any. Path must start with \u0060/\u0060","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PathFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"postFilter","description":"Select POST (request method) only exchanges.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PostFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"processIdFilter","description":"Select exchanges initiated by a process with the specified process ID. Process tracking must be enabled and the connection must originate from localhost.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.ProcessIdFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"processNameFilter","description":"Select exchanges initiated by processes with the specified names. Process names are matched case-insensitively. On Windows, the .exe extension can be omitted. Process tracking must be enabled and the connection must originate from localhost.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.ProcessNameFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"protobufRequestFilter","description":"Select requests sending a protobuf body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.ProtobufRequestFilter","category":"Filter","scope":"requestBodyReceivedFromClient"},{"title":"putFilter","description":"Select exchanges according to request method.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PutFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"queryStringFilter","description":"Select exchanges containing a specific query string. If \u0060name\u0060 is not defined or empty, the search will be performed on any query string values.The search will pass if at least one value match.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.QueryStringFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"requestHeaderFilter","description":"Select exchanges according to request header values.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.RequestHeaderFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"abortAction","description":"Abort an exchange at the transport level. This action will close connection between fluxzy and client which may lead to depended exchanges to be aborted too.","fullTypeName":"Fluxzy.Rules.Actions.AbortAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addAuthorizationBasicAction","description":"Add Authorization Basic to the request header.","fullTypeName":"Fluxzy.Rules.Actions.AddAuthorizationBasicAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addAuthorizationBearerAction","description":"Add Authorization Bearer token to the request header.","fullTypeName":"Fluxzy.Rules.Actions.AddAuthorizationBearerAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addRequestHeaderAction","description":"Append a request header.","fullTypeName":"Fluxzy.Rules.Actions.AddRequestHeaderAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addResponseHeaderAction","description":"Append a response header. H2 pseudo header will be ignored.","fullTypeName":"Fluxzy.Rules.Actions.AddResponseHeaderAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"applyCommentAction","description":"Add comment to exchange. Comment has no effect on the stream behaviour.","fullTypeName":"Fluxzy.Rules.Actions.ApplyCommentAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"applyTagAction","description":"Affect a tag to exchange. Tags are meta-information and do not alter the connection.","fullTypeName":"Fluxzy.Rules.Actions.ApplyTagAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"averageThrottleAction","description":"Throttle and simulate bandwidth condition.","fullTypeName":"Fluxzy.Rules.Actions.AverageThrottleAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"changeRequestMethodAction","description":"Alter the method of an exchange.","fullTypeName":"Fluxzy.Rules.Actions.ChangeRequestMethodAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"changeRequestPathAction","description":"Change request uri path. This action alters only the path of the request. Request path includes query string.","fullTypeName":"Fluxzy.Rules.Actions.ChangeRequestPathAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"delayAction","description":"Add a latency to the exchange.","fullTypeName":"Fluxzy.Rules.Actions.DelayAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"deleteRequestHeaderAction","description":"Remove request headers. This action removes \u003Cb\u003Eevery\u003C/b\u003E occurrence of the header from the request.","fullTypeName":"Fluxzy.Rules.Actions.DeleteRequestHeaderAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"deleteResponseHeaderAction","description":"Remove response headers. This action removes \u003Cb\u003Eevery\u003C/b\u003E occurrence of the header from the response.","fullTypeName":"Fluxzy.Rules.Actions.DeleteResponseHeaderAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"fileAppendAction","description":"Write to a file. Captured variable are interpreted.","fullTypeName":"Fluxzy.Rules.Actions.FileAppendAction","category":"Action","scope":"copySibling"},{"title":"forceHttp11Action","description":"Force the connection between fluxzy and remote to be HTTP/1.1. This value is enforced by ALPN settings set during the SSL/Handshake handshake.","fullTypeName":"Fluxzy.Rules.Actions.ForceHttp11Action","category":"Action","scope":"onAuthorityReceived"},{"title":"forceHttp2Action","description":"Forces the connection between fluxzy and remote to be HTTP/2.0. This value is enforced when setting up ALPN settings during SSL/TLS negotiation. \u003Cbr/\u003EThe exchange will break if the remote does not support HTTP/2.0. \u003Cbr/\u003EThis action will be ignored when the communication is clear (h2c not supported).","fullTypeName":"Fluxzy.Rules.Actions.ForceHttp2Action","category":"Action","scope":"onAuthorityReceived"},{"title":"forceRemotePortAction","description":"Ignores the default port used by the current authority and use the provided port instead.","fullTypeName":"Fluxzy.Rules.Actions.ForceRemotePortAction","category":"Action","scope":"onAuthorityReceived"},{"title":"forceTlsVersionAction","description":"Force the usage of a specific TLS version. Values can be chosen among : Tls, Tls11, Tls12, Tls13, Ssl3, Ssl2. \u003Cbr/\u003EForcing the usage of a specific TLS version can break the exchange if the remote does not support the requested protocol.","fullTypeName":"Fluxzy.Rules.Actions.ForceTlsVersionAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"forwardAction","description":"Forward request to a specific URL. This action makes fluxzy act as a reverse proxy. Unlike [SpoofDnsAction](https://www.fluxzy.io/rule/item/spoofDnsAction), host header is automatically set and protocol switch is supported (http to https, http/1.1 to h2, ...). The URL must be an absolute path.","fullTypeName":"Fluxzy.Rules.Actions.ForwardAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"impersonateAction","description":"Impersonate a browser or client by changing the TLS fingerprint, HTTP/2 settings and headers.","fullTypeName":"Fluxzy.Rules.Actions.ImpersonateAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"mountCertificateAuthorityAction","description":"Reply with the default root certificate used by fluxzy","fullTypeName":"Fluxzy.Rules.Actions.MountCertificateAuthorityAction","category":"Action","scope":"dnsSolveDone"},{"title":"noOpAction","description":"An action doing no operation.","fullTypeName":"Fluxzy.Rules.Actions.NoOpAction","category":"Action","scope":"requestBodyReceivedFromClient"},{"title":"removeCacheAction","description":"Remove all cache directive from request and response headers. This will force the clientto ask the latest version of the requested resource.","fullTypeName":"Fluxzy.Rules.Actions.RemoveCacheAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setClientCertificateAction","description":"Add a client certificate to the exchange. The client certificate will be used for establishing the mTLS authentication if the remote request it. The client certificate can be retrieved from the default store (my) or from a PKCS#12 file (.p12, pfx). \u003Cbr/\u003EThe certificate will not be stored in fluxzy settings and, therefore, must be available at runtime. ","fullTypeName":"Fluxzy.Rules.Actions.SetClientCertificateAction","category":"Action","scope":"onAuthorityReceived"},{"title":"setJa3FingerPrintAction","description":"Set a JA3 fingerprint of ongoing connection.","fullTypeName":"Fluxzy.Rules.Actions.SetJa3FingerPrintAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setUserAgentAction","description":"Change the User-AgentThis action is used to change the User-Agent header of the request from a list of built-in user-agent values.","fullTypeName":"Fluxzy.Rules.Actions.SetUserAgentAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setVariableAction","description":"Set a variable or update an existing","fullTypeName":"Fluxzy.Rules.Actions.SetVariableAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"skipRemoteCertificateValidationAction","description":"Skip validating remote certificate. Fluxzy will ignore any validation errors on the server certificate.","fullTypeName":"Fluxzy.Rules.Actions.SkipRemoteCertificateValidationAction","category":"Action","scope":"onAuthorityReceived"},{"title":"skipSslTunnelingAction","description":"Instructs fluxzy to not decrypt the current traffic. The associated filter must be on OnAuthorityReceived scope in order to make this action effective. ","fullTypeName":"Fluxzy.Rules.Actions.SkipSslTunnelingAction","category":"Action","scope":"onAuthorityReceived"},{"title":"spoofDnsAction","description":"Fix statically the remote ip or port disregards to the dns or host resolution of the current running system. Use this action to force the resolution of a hostname to a fixed IP address. ","fullTypeName":"Fluxzy.Rules.Actions.SpoofDnsAction","category":"Action","scope":"onAuthorityReceived"},{"title":"stdErrAction","description":"Write text to standard error. Captured variable are interpreted.","fullTypeName":"Fluxzy.Rules.Actions.StdErrAction","category":"Action","scope":"copySibling"},{"title":"stdOutAction","description":"Write text to standard output. Captured variable are interpreted.","fullTypeName":"Fluxzy.Rules.Actions.StdOutAction","category":"Action","scope":"outOfScope"},{"title":"updateRequestHeaderAction","description":"Update and existing request header. If the header does not exists in the original request, the header will be added. \u003Cbr/\u003EUse {{previous}} keyword to refer to the original value of the header. \u003Cbr/\u003E\u003Cstrong\u003ENote\u003C/strong\u003E Headers that alter the connection behaviour will be ignored.","fullTypeName":"Fluxzy.Rules.Actions.UpdateRequestHeaderAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"updateResponseHeaderAction","description":"Update and existing response header. If the header does not exists in the original response, the header will be added.\u003Cbr/\u003EUse {{previous}} keyword to refer to the original value of the header.\u003Cbr/\u003E\u003Cstrong\u003ENote\u003C/strong\u003E Headers that alter the connection behaviour will be ignored.","fullTypeName":"Fluxzy.Rules.Actions.UpdateResponseHeaderAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"upStreamProxyAction","description":"Use an upstream proxy.","fullTypeName":"Fluxzy.Rules.Actions.UpStreamProxyAction","category":"Action","scope":"onAuthorityReceived"},{"title":"useCertificateAction","description":"Use a specific server certificate. Certificate can be retrieved from user store or from a PKCS12 file","fullTypeName":"Fluxzy.Rules.Actions.UseCertificateAction","category":"Action","scope":"onAuthorityReceived"},{"title":"useDnsOverHttpsAction","description":"Use DoH (DNS over HTTPS) to resolve domain names instead of the default DNS provided by the OS","fullTypeName":"Fluxzy.Rules.Actions.UseDnsOverHttpsAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addBasicAuthenticationAction","description":"Add a basic authentication (RFC 7617) to incoming exchanges with an username and a password","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.AddBasicAuthenticationAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"applySessionAction","description":"Apply captured session data to requests. Adds cookies from session store and optionally applies stored headers. Works in conjunction with CaptureSessionAction.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.ApplySessionAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"captureSessionAction","description":"Capture session data from responses. Captures Set-Cookie headers and optionally other headers like Authorization. Can also capture cookies from request headers for intercepting ongoing sessions. Stored data can be replayed using ApplySessionAction.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.CaptureSessionAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"clearSessionAction","description":"Clear stored session data for a specific domain or all domains.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.ClearSessionAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"injectHtmlTagAction","description":"This action stream a response body and inject a text after the first specified html tag.This action can be used to inject a html code snippet after opening \u0060\u003Chead\u003E\u0060 tag in any traversing html page.This action supports chunked transfer stream and the following body encodings: gzip, deflate, brotli and lzw.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.InjectHtmlTagAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"mockedResponseAction","description":"Reply with a pre-made response from a raw text or file","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.MockedResponseAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"mountWelcomePageAction","description":"Reply with fluxzy welcome page","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.MountWelcomePageAction","category":"Action","scope":"dnsSolveDone"},{"title":"rejectAction","description":"Block the request and return HTTP 403 Forbidden response. Use this action to explicitly deny access to specific resources. This is a simple blocking action with no configuration required.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RejectAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"rejectWithMessageAction","description":"Block the request with a custom HTTP error response including a body message. Useful for providing detailed blocking reasons to end users. Supports text/plain, text/html, and application/json content types.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RejectWithMessageAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"rejectWithStatusCodeAction","description":"Block the request and return a custom HTTP error response. Allows specifying the status code (e.g., 403, 404, 502) to return to the client. The response body will contain the standard reason phrase for the status code.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RejectWithStatusCodeAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"removeResponseCookieAction","description":"Remove a response cookie by setting the expiration date to a past date.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RemoveResponseCookieAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"serveDirectoryAction","description":"Serve a folder as a static web site. This action is made for mocking purpose and not production ready for a web site.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.ServeDirectoryAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setRequestCookieAction","description":"Add a cookie to request. This action is performed by adding/replacing \u0060Cookie\u0060 header in request.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.SetRequestCookieAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setResponseCookieAction","description":"Add a response cookie. This action is performed by adding \u0060Set-Cookie\u0060 header in response.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.SetResponseCookieAction","category":"Action","scope":"responseHeaderReceivedFromRemote"}] \ No newline at end of file +[{"title":"anyFilter","description":"Select all exchanges","fullTypeName":"Fluxzy.Rules.Filters.AnyFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"commentSearchFilter","description":"Select exchanges by searching a string pattern into the comment property.","fullTypeName":"Fluxzy.Rules.Filters.CommentSearchFilter","category":"Filter","scope":"outOfScope"},{"title":"filterCollection","description":"FilterCollection is a combination of multiple filters with a merging operator (OR / AND).","fullTypeName":"Fluxzy.Rules.Filters.FilterCollection","category":"Filter","scope":"onAuthorityReceived"},{"title":"hasCommentFilter","description":"Select exchanges having comment.","fullTypeName":"Fluxzy.Rules.Filters.HasCommentFilter","category":"Filter","scope":"outOfScope"},{"title":"hasTagFilter","description":"Select exchanges having tag.","fullTypeName":"Fluxzy.Rules.Filters.HasTagFilter","category":"Filter","scope":"outOfScope"},{"title":"ipEgressFilter","description":"Select exchanges according to upstream IP address. Full IP notation is used from IPv6.","fullTypeName":"Fluxzy.Rules.Filters.IpEgressFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"ipIngressFilter","description":"Select exchanges according to client ip address. Full IP notation is used from IPv6.","fullTypeName":"Fluxzy.Rules.Filters.IpIngressFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"isWebSocketFilter","description":"Select websocket exchange.","fullTypeName":"Fluxzy.Rules.Filters.IsWebSocketFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"protobufFilter","description":"Select exchanges having a protobuf request or response body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header on both request and response.","fullTypeName":"Fluxzy.Rules.Filters.ProtobufFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"contentTypeXmlFilter","description":"Select exchanges having XML response body.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ContentTypeXmlFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"cssStyleFilter","description":"Select exchanges having response content type mime matching css.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.CssStyleFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"fontFilter","description":"Select exchanges having response content type matching a font payload.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.FontFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"htmlResponseFilter","description":"Select exchanges having HTML body. The content-type header is checked to determine if the content body is has text/html hint.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.HtmlResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"imageFilter","description":"Select exchanges having response content type mime matching image.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ImageFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"jsonResponseFilter","description":"Select exchanges having JSON response body. The content-type header is checked to determine if the content body is a JSON.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.JsonResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"networkErrorFilter","description":"Select exchanges that fails due to network error.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.NetworkErrorFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"protobufResponseFilter","description":"Select exchanges having protobuf response body. The content-type header is checked to determine if the content body is protobuf.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ProtobufResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"responseHeaderFilter","description":"Select exchanges according to response header values.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.ResponseHeaderFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeClientErrorFilter","description":"Select exchanges that HTTP status code indicates a client error (4XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeClientErrorFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeFilter","description":"Select exchanges according to HTTP status code.","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeRedirectionFilter","description":"Select exchanges that HTTP status code indicates a redirect (3XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeRedirectionFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeServerErrorFilter","description":"Select exchanges that HTTP status code indicates a server/intermediary error (5XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeServerErrorFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"statusCodeSuccessFilter","description":"Select exchanges that HTTP status code indicates a successful request (2XX).","fullTypeName":"Fluxzy.Rules.Filters.ResponseFilters.StatusCodeSuccessFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"absoluteUriFilter","description":"Select exchanges according to URI (scheme, FQDN, path and query). Supports common string search option and regular expression.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.AbsoluteUriFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"agentLabelFilter","description":"Select exchanges according to configured source agent (user agent or process) with a regular string search.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.AgentLabelFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"authorityFilter","description":"Select exchange according to hostname and a port","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.AuthorityFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"deleteFilter","description":"Select exchanges with DELETE method","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.DeleteFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"formRequestFilter","description":"Select request sending \u0027multipart/form-data\u0027 or \u0027application/x-www-form-urlencoded\u0027 body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.FormRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"formUrlEncodedRequestFilter","description":"Select request sending \u0027application/x-www-form-urlencoded\u0027 body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.FormUrlEncodedRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"getFilter","description":"Select exchanges with GET method","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.GetFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"h11TrafficOnlyFilter","description":"Select HTTP/1.1 exchanges only.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.H11TrafficOnlyFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"h2TrafficOnlyFilter","description":"Select H2 exchanges only.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.H2TrafficOnlyFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasAnyCookieOnRequestFilter","description":"Select exchanges having any request cookie","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasAnyCookieOnRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasAuthorizationBearerFilter","description":"Select exchanges having bearer token in authorization.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasAuthorizationBearerFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasAuthorizationFilter","description":"Select exchanges having authorization header.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasAuthorizationFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasCookieOnRequestFilter","description":"Exchange having a request cookie with a specific name","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasCookieOnRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"hasRequestBodyFilter","description":"Select request having body.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasRequestBodyFilter","category":"Filter","scope":"responseBodyReceivedFromRemote"},{"title":"hasSetCookieOnResponseFilter","description":"Search for a cookie value present in a \u0060set-cookie\u0060 header response.If cookie name is not defined or empty, the filter will returns any cookie having the value.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HasSetCookieOnResponseFilter","category":"Filter","scope":"responseHeaderReceivedFromRemote"},{"title":"hostFilter","description":"Select exchanges according to hostname (excluding port). To select authority (combination of host:port), use \u003Cgoto\u003EAuthorityFilter\u003C/goto\u003E.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.HostFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"isGrpcFilter","description":"Select gRPC exchanges only. Filtering is made by inspecting value of \u0060Content-Type\u0060 header.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.IsGrpcFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"isSecureFilter","description":"Select secure exchange only (non plain HTTP).","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.IsSecureFilter","category":"Filter","scope":"onAuthorityReceived"},{"title":"jsonRequestFilter","description":"Select request sending JSON body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.JsonRequestFilter","category":"Filter","scope":"requestBodyReceivedFromClient"},{"title":"methodFilter","description":"Select exchanges according to request method.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.MethodFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"multipartDataRequestFilter","description":"Select request sending \u0027multipart/form-data\u0027 body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.MultipartDataRequestFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"patchFilter","description":"Select exchanges with PATCH method","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PatchFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"pathFilter","description":"Select exchanges according to url path. Path includes query string if any. Path must start with \u0060/\u0060","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PathFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"postFilter","description":"Select POST (request method) only exchanges.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PostFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"processIdFilter","description":"Select exchanges initiated by a process with the specified process ID. Process tracking must be enabled and the connection must originate from localhost.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.ProcessIdFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"processNameFilter","description":"Select exchanges initiated by processes with the specified names. Process names are matched case-insensitively. On Windows, the .exe extension can be omitted. Process tracking must be enabled and the connection must originate from localhost.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.ProcessNameFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"protobufRequestFilter","description":"Select requests sending a protobuf body. Filtering is made by inspecting value of \u0060Content-Type\u0060 header.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.ProtobufRequestFilter","category":"Filter","scope":"requestBodyReceivedFromClient"},{"title":"putFilter","description":"Select exchanges according to request method.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.PutFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"queryStringFilter","description":"Select exchanges containing a specific query string. If \u0060name\u0060 is not defined or empty, the search will be performed on any query string values.The search will pass if at least one value match.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.QueryStringFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"requestHeaderFilter","description":"Select exchanges according to request header values.","fullTypeName":"Fluxzy.Rules.Filters.RequestFilters.RequestHeaderFilter","category":"Filter","scope":"requestHeaderReceivedFromClient"},{"title":"abortAction","description":"Abort an exchange at the transport level. This action will close connection between fluxzy and client which may lead to depended exchanges to be aborted too.","fullTypeName":"Fluxzy.Rules.Actions.AbortAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addAuthorizationBasicAction","description":"Add Authorization Basic to the request header.","fullTypeName":"Fluxzy.Rules.Actions.AddAuthorizationBasicAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addAuthorizationBearerAction","description":"Add Authorization Bearer token to the request header.","fullTypeName":"Fluxzy.Rules.Actions.AddAuthorizationBearerAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addRequestHeaderAction","description":"Append a request header.","fullTypeName":"Fluxzy.Rules.Actions.AddRequestHeaderAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addResponseHeaderAction","description":"Append a response header. H2 pseudo header will be ignored.","fullTypeName":"Fluxzy.Rules.Actions.AddResponseHeaderAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"applyCommentAction","description":"Add comment to exchange. Comment has no effect on the stream behaviour.","fullTypeName":"Fluxzy.Rules.Actions.ApplyCommentAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"applyTagAction","description":"Affect a tag to exchange. Tags are meta-information and do not alter the connection.","fullTypeName":"Fluxzy.Rules.Actions.ApplyTagAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"averageThrottleAction","description":"Throttle and simulate bandwidth condition.","fullTypeName":"Fluxzy.Rules.Actions.AverageThrottleAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"changeRequestMethodAction","description":"Alter the method of an exchange.","fullTypeName":"Fluxzy.Rules.Actions.ChangeRequestMethodAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"changeRequestPathAction","description":"Change request uri path. This action alters only the path of the request. Request path includes query string.","fullTypeName":"Fluxzy.Rules.Actions.ChangeRequestPathAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"delayAction","description":"Add a latency to the exchange.","fullTypeName":"Fluxzy.Rules.Actions.DelayAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"deleteRequestHeaderAction","description":"Remove request headers. This action removes \u003Cb\u003Eevery\u003C/b\u003E occurrence of the header from the request.","fullTypeName":"Fluxzy.Rules.Actions.DeleteRequestHeaderAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"deleteResponseHeaderAction","description":"Remove response headers. This action removes \u003Cb\u003Eevery\u003C/b\u003E occurrence of the header from the response.","fullTypeName":"Fluxzy.Rules.Actions.DeleteResponseHeaderAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"fileAppendAction","description":"Write to a file. Captured variable are interpreted.","fullTypeName":"Fluxzy.Rules.Actions.FileAppendAction","category":"Action","scope":"copySibling"},{"title":"forceHttp11Action","description":"Force the connection between fluxzy and remote to be HTTP/1.1. This value is enforced by ALPN settings set during the SSL/Handshake handshake.","fullTypeName":"Fluxzy.Rules.Actions.ForceHttp11Action","category":"Action","scope":"onAuthorityReceived"},{"title":"forceHttp2Action","description":"Forces the connection between fluxzy and remote to be HTTP/2.0. This value is enforced when setting up ALPN settings during SSL/TLS negotiation. \u003Cbr/\u003EThe exchange will break if the remote does not support HTTP/2.0. \u003Cbr/\u003EThis action will be ignored when the communication is clear (h2c not supported).","fullTypeName":"Fluxzy.Rules.Actions.ForceHttp2Action","category":"Action","scope":"onAuthorityReceived"},{"title":"forceRemotePortAction","description":"Ignores the default port used by the current authority and use the provided port instead.","fullTypeName":"Fluxzy.Rules.Actions.ForceRemotePortAction","category":"Action","scope":"onAuthorityReceived"},{"title":"forceTlsVersionAction","description":"Force the usage of a specific TLS version. Values can be chosen among : Tls, Tls11, Tls12, Tls13, Ssl3, Ssl2. \u003Cbr/\u003EForcing the usage of a specific TLS version can break the exchange if the remote does not support the requested protocol.","fullTypeName":"Fluxzy.Rules.Actions.ForceTlsVersionAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"forwardAction","description":"Forward request to a specific URL. This action makes fluxzy act as a reverse proxy. Unlike [SpoofDnsAction](https://www.fluxzy.io/rule/item/spoofDnsAction), host header is automatically set and protocol switch is supported (http to https, http/1.1 to h2, ...). The URL must be an absolute path.","fullTypeName":"Fluxzy.Rules.Actions.ForwardAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"impersonateAction","description":"Impersonate a browser or client by changing the TLS fingerprint, HTTP/2 settings and headers.","fullTypeName":"Fluxzy.Rules.Actions.ImpersonateAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"mountCertificateAuthorityAction","description":"Reply with the default root certificate used by fluxzy","fullTypeName":"Fluxzy.Rules.Actions.MountCertificateAuthorityAction","category":"Action","scope":"dnsSolveDone"},{"title":"noOpAction","description":"An action doing no operation.","fullTypeName":"Fluxzy.Rules.Actions.NoOpAction","category":"Action","scope":"requestBodyReceivedFromClient"},{"title":"removeCacheAction","description":"Remove all cache directive from request and response headers. This will force the clientto ask the latest version of the requested resource.","fullTypeName":"Fluxzy.Rules.Actions.RemoveCacheAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"serveHttp11Action","description":"Force the downstream connection (client-to-proxy) to use HTTP/1.1. When the global ServeH2 option is enabled, this action overrides it for matched exchanges by only advertising HTTP/1.1 during ALPN negotiation with the client.","fullTypeName":"Fluxzy.Rules.Actions.ServeHttp11Action","category":"Action","scope":"onAuthorityReceived"},{"title":"setClientCertificateAction","description":"Add a client certificate to the exchange. The client certificate will be used for establishing the mTLS authentication if the remote request it. The client certificate can be retrieved from the default store (my) or from a PKCS#12 file (.p12, pfx). \u003Cbr/\u003EThe certificate will not be stored in fluxzy settings and, therefore, must be available at runtime. ","fullTypeName":"Fluxzy.Rules.Actions.SetClientCertificateAction","category":"Action","scope":"onAuthorityReceived"},{"title":"setJa3FingerPrintAction","description":"Set a JA3 fingerprint of ongoing connection.","fullTypeName":"Fluxzy.Rules.Actions.SetJa3FingerPrintAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setUserAgentAction","description":"Change the User-AgentThis action is used to change the User-Agent header of the request from a list of built-in user-agent values.","fullTypeName":"Fluxzy.Rules.Actions.SetUserAgentAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setVariableAction","description":"Set a variable or update an existing","fullTypeName":"Fluxzy.Rules.Actions.SetVariableAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"skipRemoteCertificateValidationAction","description":"Skip validating remote certificate. Fluxzy will ignore any validation errors on the server certificate.","fullTypeName":"Fluxzy.Rules.Actions.SkipRemoteCertificateValidationAction","category":"Action","scope":"onAuthorityReceived"},{"title":"skipSslTunnelingAction","description":"Instructs fluxzy to not decrypt the current traffic. The associated filter must be on OnAuthorityReceived scope in order to make this action effective. ","fullTypeName":"Fluxzy.Rules.Actions.SkipSslTunnelingAction","category":"Action","scope":"onAuthorityReceived"},{"title":"spoofDnsAction","description":"Fix statically the remote ip or port disregards to the dns or host resolution of the current running system. Use this action to force the resolution of a hostname to a fixed IP address. ","fullTypeName":"Fluxzy.Rules.Actions.SpoofDnsAction","category":"Action","scope":"onAuthorityReceived"},{"title":"stdErrAction","description":"Write text to standard error. Captured variable are interpreted.","fullTypeName":"Fluxzy.Rules.Actions.StdErrAction","category":"Action","scope":"copySibling"},{"title":"stdOutAction","description":"Write text to standard output. Captured variable are interpreted.","fullTypeName":"Fluxzy.Rules.Actions.StdOutAction","category":"Action","scope":"outOfScope"},{"title":"updateRequestHeaderAction","description":"Update and existing request header. If the header does not exists in the original request, the header will be added. \u003Cbr/\u003EUse {{previous}} keyword to refer to the original value of the header. \u003Cbr/\u003E\u003Cstrong\u003ENote\u003C/strong\u003E Headers that alter the connection behaviour will be ignored.","fullTypeName":"Fluxzy.Rules.Actions.UpdateRequestHeaderAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"updateResponseHeaderAction","description":"Update and existing response header. If the header does not exists in the original response, the header will be added.\u003Cbr/\u003EUse {{previous}} keyword to refer to the original value of the header.\u003Cbr/\u003E\u003Cstrong\u003ENote\u003C/strong\u003E Headers that alter the connection behaviour will be ignored.","fullTypeName":"Fluxzy.Rules.Actions.UpdateResponseHeaderAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"upStreamProxyAction","description":"Use an upstream proxy.","fullTypeName":"Fluxzy.Rules.Actions.UpStreamProxyAction","category":"Action","scope":"onAuthorityReceived"},{"title":"useCertificateAction","description":"Use a specific server certificate. Certificate can be retrieved from user store or from a PKCS12 file","fullTypeName":"Fluxzy.Rules.Actions.UseCertificateAction","category":"Action","scope":"onAuthorityReceived"},{"title":"useDnsOverHttpsAction","description":"Use DoH (DNS over HTTPS) to resolve domain names instead of the default DNS provided by the OS","fullTypeName":"Fluxzy.Rules.Actions.UseDnsOverHttpsAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"addBasicAuthenticationAction","description":"Add a basic authentication (RFC 7617) to incoming exchanges with an username and a password","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.AddBasicAuthenticationAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"applySessionAction","description":"Apply captured session data to requests. Adds cookies from session store and optionally applies stored headers. Works in conjunction with CaptureSessionAction.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.ApplySessionAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"captureSessionAction","description":"Capture session data from responses. Captures Set-Cookie headers and optionally other headers like Authorization. Can also capture cookies from request headers for intercepting ongoing sessions. Stored data can be replayed using ApplySessionAction.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.CaptureSessionAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"clearSessionAction","description":"Clear stored session data for a specific domain or all domains.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.ClearSessionAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"injectHtmlTagAction","description":"This action stream a response body and inject a text after the first specified html tag.This action can be used to inject a html code snippet after opening \u0060\u003Chead\u003E\u0060 tag in any traversing html page.This action supports chunked transfer stream and the following body encodings: gzip, deflate, brotli and lzw.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.InjectHtmlTagAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"mockedResponseAction","description":"Reply with a pre-made response from a raw text or file","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.MockedResponseAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"mountWelcomePageAction","description":"Reply with fluxzy welcome page","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.MountWelcomePageAction","category":"Action","scope":"dnsSolveDone"},{"title":"rejectAction","description":"Block the request and return HTTP 403 Forbidden response. Use this action to explicitly deny access to specific resources. This is a simple blocking action with no configuration required.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RejectAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"rejectWithMessageAction","description":"Block the request with a custom HTTP error response including a body message. Useful for providing detailed blocking reasons to end users. Supports text/plain, text/html, and application/json content types.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RejectWithMessageAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"rejectWithStatusCodeAction","description":"Block the request and return a custom HTTP error response. Allows specifying the status code (e.g., 403, 404, 502) to return to the client. The response body will contain the standard reason phrase for the status code.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RejectWithStatusCodeAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"removeResponseCookieAction","description":"Remove a response cookie by setting the expiration date to a past date.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.RemoveResponseCookieAction","category":"Action","scope":"responseHeaderReceivedFromRemote"},{"title":"serveDirectoryAction","description":"Serve a folder as a static web site. This action is made for mocking purpose and not production ready for a web site.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.ServeDirectoryAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setRequestCookieAction","description":"Add a cookie to request. This action is performed by adding/replacing \u0060Cookie\u0060 header in request.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.SetRequestCookieAction","category":"Action","scope":"requestHeaderReceivedFromClient"},{"title":"setResponseCookieAction","description":"Add a response cookie. This action is performed by adding \u0060Set-Cookie\u0060 header in response.","fullTypeName":"Fluxzy.Rules.Actions.HighLevelActions.SetResponseCookieAction","category":"Action","scope":"responseHeaderReceivedFromRemote"}] \ No newline at end of file diff --git a/src/Fluxzy.Core/Core/ExchangeContext.cs b/src/Fluxzy.Core/Core/ExchangeContext.cs index 6e5068e0..4fffe409 100644 --- a/src/Fluxzy.Core/Core/ExchangeContext.cs +++ b/src/Fluxzy.Core/Core/ExchangeContext.cs @@ -210,6 +210,12 @@ public ExchangeContext( public bool AlwaysSendClientCertificate { get; set; } + /// + /// When true, force the downstream (client-to-proxy) connection to use HTTP/1.1 + /// by only advertising HTTP/1.1 in the ALPN negotiation, regardless of the global ServeH2 setting. + /// + public bool ForceServeHttp11 { get; set; } + /// /// Register a response body substitution /// diff --git a/src/Fluxzy.Core/Core/Impl/SecureConnectionUpdater.cs b/src/Fluxzy.Core/Core/Impl/SecureConnectionUpdater.cs index 0d7db531..b4d6e489 100644 --- a/src/Fluxzy.Core/Core/Impl/SecureConnectionUpdater.cs +++ b/src/Fluxzy.Core/Core/Impl/SecureConnectionUpdater.cs @@ -73,9 +73,11 @@ public async Task AuthenticateAsServer( try { + var effectiveServeH2 = _serveH2 && !context.ForceServeHttp11; + var sslProtocols = SslProtocols.None; - if (_serveH2) { + if (effectiveServeH2) { sslProtocols = SslProtocols.Tls12; #if NET8_0_OR_GREATER @@ -85,7 +87,7 @@ public async Task AuthenticateAsServer( var sslServerAuthenticationOptions = new SslServerAuthenticationOptions { - ApplicationProtocols = _serveH2 ? H11AndH2Protocols : H11Protocols, + ApplicationProtocols = effectiveServeH2 ? H11AndH2Protocols : H11Protocols, EnabledSslProtocols = sslProtocols, ClientCertificateRequired = false, CertificateRevocationCheckMode = X509RevocationMode.NoCheck, diff --git a/src/Fluxzy.Core/Rules/Actions/ServeHttp11Action.cs b/src/Fluxzy.Core/Rules/Actions/ServeHttp11Action.cs new file mode 100644 index 00000000..12f34506 --- /dev/null +++ b/src/Fluxzy.Core/Rules/Actions/ServeHttp11Action.cs @@ -0,0 +1,40 @@ +// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak + +using System.Collections.Generic; +using System.Threading.Tasks; +using Fluxzy.Core; +using Fluxzy.Core.Breakpoints; + +namespace Fluxzy.Rules.Actions +{ + /// + /// Force the downstream connection (client-to-proxy) to use HTTP/1.1 by only advertising + /// HTTP/1.1 during ALPN negotiation, even when the global ServeH2 option is enabled. + /// + [ActionMetadata( + "Force the downstream connection (client-to-proxy) to use HTTP/1.1. " + + "When the global ServeH2 option is enabled, this action overrides it for matched exchanges " + + "by only advertising HTTP/1.1 during ALPN negotiation with the client.")] + public class ServeHttp11Action : Action + { + public override FilterScope ActionScope => FilterScope.OnAuthorityReceived; + + public override string DefaultDescription => "Serve HTTP/1.1 to client"; + + public override ValueTask InternalAlter( + ExchangeContext context, Exchange? exchange, Connection? connection, FilterScope scope, + BreakPointManager breakPointManager) + { + context.ForceServeHttp11 = true; + + return default; + } + + public override IEnumerable GetExamples() + { + yield return new ActionExample( + "Force HTTP/1.1 serving for a specific host even when ServeH2 is globally enabled", + new ServeHttp11Action()); + } + } +} diff --git a/test/Fluxzy.Tests/UnitTests/H2Serve/ServeHttp11ActionTests.cs b/test/Fluxzy.Tests/UnitTests/H2Serve/ServeHttp11ActionTests.cs new file mode 100644 index 00000000..c1bbf28c --- /dev/null +++ b/test/Fluxzy.Tests/UnitTests/H2Serve/ServeHttp11ActionTests.cs @@ -0,0 +1,135 @@ +// Copyright 2021 - Haga Rakotoharivelo - https://github.com/haga-rak + +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using Fluxzy.Rules.Actions; +using Fluxzy.Rules.Filters; +using Fluxzy.Rules.Filters.RequestFilters; +using Fluxzy.Tests._Fixtures; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Xunit; + +namespace Fluxzy.Tests.UnitTests.H2Serve +{ + public class ServeHttp11ActionTests + { + /// + /// When ServeH2 is enabled globally but ServeHttp11Action is applied, + /// the client should receive an HTTP/1.1 response. + /// + [Fact] + public async Task ServeHttp11Action_OverridesGlobalServeH2() + { + var host = await InProcessHost.Create(app => + { + app.MapGet("/test", async (HttpContext ctx) => + { + ctx.Response.ContentType = "text/plain"; + await ctx.Response.WriteAsync("OK"); + }); + }); + + await using var _ = host; + + var setting = FluxzySetting.CreateLocalRandomPort(); + setting.SetServeH2(true); + + setting.AddAlterationRules(new SkipRemoteCertificateValidationAction(), AnyFilter.Default); + + setting.ConfigureRule() + .WhenAny() + .Do(new ServeHttp11Action()); + + await using var proxy = new Proxy(setting); + var endPoints = proxy.Run(); + var proxyEndPoint = endPoints.First(); + + // Client prefers H2 but will accept H1.1 if server only offers it + using var client = Socks5ClientFactory.Create(proxyEndPoint); + client.BaseAddress = new Uri(host.BaseUrl); + client.DefaultRequestVersion = new Version(2, 0); + client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower; + + var response = await client.GetAsync("/test"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(new Version(1, 1), response.Version); + + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("OK", body); + } + + /// + /// When ServeH2 is enabled and ServeHttp11Action is NOT applied, + /// the client should receive an HTTP/2 response (control test). + /// + [Fact] + public async Task WithoutServeHttp11Action_ClientGetsH2() + { + await using var setup = await ProxiedHostSetup.Create( + configureSetting: setting => + { + setting.SetServeH2(true); + }, + configureRoutes: app => + { + app.MapGet("/test", async (HttpContext ctx) => + { + ctx.Response.ContentType = "text/plain"; + await ctx.Response.WriteAsync("OK"); + }); + }, + httpVersion: new Version(2, 0)); + + var response = await setup.Client.GetAsync("/test"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(new Version(2, 0), response.Version); + } + + /// + /// When ServeHttp11Action is applied per-host filter, only matching hosts should be downgraded. + /// + [Fact] + public async Task ServeHttp11Action_AppliedPerHost() + { + var host = await InProcessHost.Create(app => + { + app.MapGet("/test", async (HttpContext ctx) => + { + ctx.Response.ContentType = "text/plain"; + await ctx.Response.WriteAsync("OK"); + }); + }); + + await using var _ = host; + + var setting = FluxzySetting.CreateLocalRandomPort(); + setting.SetServeH2(true); + + setting.AddAlterationRules(new SkipRemoteCertificateValidationAction(), AnyFilter.Default); + + setting.ConfigureRule() + .When(new HostFilter("localhost")) + .Do(new ServeHttp11Action()); + + await using var proxy = new Proxy(setting); + var endPoints = proxy.Run(); + var proxyEndPoint = endPoints.First(); + + using var client = Socks5ClientFactory.Create(proxyEndPoint); + client.BaseAddress = new Uri(host.BaseUrl); + client.DefaultRequestVersion = new Version(2, 0); + client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower; + + var response = await client.GetAsync("/test"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(new Version(1, 1), response.Version); + } + } +}