An HTTP/1 header block exceeded MaxHeaderCount or MaxHeaderAggregateBytes
+while HeaderLimits was Enforce. Not an IOException,
+so it is not treated as a client disconnect.
An HTTP/1 header block exceeded MaxHeaderCount or MaxHeaderAggregateBytes
+while HeaderLimits was Enforce. Not an IOException,
+so it is not treated as a client disconnect.
Returns a snapshot identical to this one with AllowAmbiguousFraming set explicitly.
+A partial config overlay uses this so an omitted flag does not clear a value set by the profile.
+
+
+
Declaration
+
+
public ProxyPolicyModes WithAllowAmbiguousFraming(bool enabled)
Bytes of HTTP/2 DATA that may be parked per stream waiting for send-window credit.
+Worst-case memory is concurrent streams times this value. Default 4 MiB + 16 KiB. Ceiling 64 MiB.
+
+
+
Declaration
+
+
public int MaxDeferredOutboundBytesPerStream { get; }
Validates and constructs a ProxyResourceLimits snapshot. Every non-nullable
-bound must be strictly positive; every nullable bound, if supplied, must also be strictly
-positive (use null to disable rather than a sentinel number).
+
+
MaxTrailerHeaderBlockBytes
+
Maximum trailer header block size in bytes. Default 16 KiB.
Declaration
-
public static ProxyResourceLimits Create(long maxHeaderLineBytes, int maxHeaderCount, long maxHeaderAggregateBytes, long? maxEncodedBodyBytes, long? maxDecodedBodyBytes, double? maxDecompressionRatio, int? maxConcurrentClients, int maxConcurrentStreamsPerConnection, int? maxPeerInitiatedIncompleteStreamResets, int maxOpenHeaderBlockFrames, TimeSpan maxOpenHeaderBlockDuration, bool connectionPoolingEnabled, int maxCachedConnectionsPerHost, int? maxCertificateCacheEntries)
Validates and constructs a ProxyResourceLimits snapshot. Every non-nullable
+bound must be strictly positive; every nullable bound, if supplied, must also be strictly
+positive (use null to disable rather than a sentinel number).
+
+
+
Declaration
+
+
public static ProxyResourceLimits Create(long maxHeaderLineBytes, int maxHeaderCount, long maxHeaderAggregateBytes, long? maxEncodedBodyBytes, long? maxDecodedBodyBytes, double? maxDecompressionRatio, int? maxConcurrentClients, int maxConcurrentStreamsPerConnection, int? maxPeerInitiatedIncompleteStreamResets, int maxOpenHeaderBlockFrames, TimeSpan maxOpenHeaderBlockDuration, bool connectionPoolingEnabled, int maxCachedConnectionsPerHost, int? maxCertificateCacheEntries)
diff --git a/docs/api/toc.json b/docs/api/toc.json
index d50245d4b..0cfbe2781 100644
--- a/docs/api/toc.json
+++ b/docs/api/toc.json
@@ -1,2 +1,2 @@
-{"items":[{"name":"Titanium.Web.Proxy","href":"Titanium.Web.Proxy.html","topicHref":"Titanium.Web.Proxy.html","topicUid":"Titanium.Web.Proxy","type":"Namespace","items":[{"name":"ClientProcessId","href":"Titanium.Web.Proxy.ClientProcessId.html","topicHref":"Titanium.Web.Proxy.ClientProcessId.html","topicUid":"Titanium.Web.Proxy.ClientProcessId","type":"Class"},{"name":"MitmExclusionDefaults","href":"Titanium.Web.Proxy.MitmExclusionDefaults.html","topicHref":"Titanium.Web.Proxy.MitmExclusionDefaults.html","topicUid":"Titanium.Web.Proxy.MitmExclusionDefaults","type":"Class"},{"name":"MitmExclusionMode","href":"Titanium.Web.Proxy.MitmExclusionMode.html","topicHref":"Titanium.Web.Proxy.MitmExclusionMode.html","topicUid":"Titanium.Web.Proxy.MitmExclusionMode","type":"Enum"},{"name":"ProxyLimits","href":"Titanium.Web.Proxy.ProxyLimits.html","topicHref":"Titanium.Web.Proxy.ProxyLimits.html","topicUid":"Titanium.Web.Proxy.ProxyLimits","type":"Class"},{"name":"ProxyServer","href":"Titanium.Web.Proxy.ProxyServer.html","topicHref":"Titanium.Web.Proxy.ProxyServer.html","topicUid":"Titanium.Web.Proxy.ProxyServer","type":"Class"},{"name":"SystemProxyBypassRuleMode","href":"Titanium.Web.Proxy.SystemProxyBypassRuleMode.html","topicHref":"Titanium.Web.Proxy.SystemProxyBypassRuleMode.html","topicUid":"Titanium.Web.Proxy.SystemProxyBypassRuleMode","type":"Enum"},{"name":"SystemProxyChangeResult","href":"Titanium.Web.Proxy.SystemProxyChangeResult.html","topicHref":"Titanium.Web.Proxy.SystemProxyChangeResult.html","topicUid":"Titanium.Web.Proxy.SystemProxyChangeResult","type":"Struct"},{"name":"SystemProxyLoopbackPlacement","href":"Titanium.Web.Proxy.SystemProxyLoopbackPlacement.html","topicHref":"Titanium.Web.Proxy.SystemProxyLoopbackPlacement.html","topicUid":"Titanium.Web.Proxy.SystemProxyLoopbackPlacement","type":"Enum"},{"name":"SystemProxySettings","href":"Titanium.Web.Proxy.SystemProxySettings.html","topicHref":"Titanium.Web.Proxy.SystemProxySettings.html","topicUid":"Titanium.Web.Proxy.SystemProxySettings","type":"Class"},{"name":"WebSocketDecoder","href":"Titanium.Web.Proxy.WebSocketDecoder.html","topicHref":"Titanium.Web.Proxy.WebSocketDecoder.html","topicUid":"Titanium.Web.Proxy.WebSocketDecoder","type":"Class"},{"name":"WebSocketFrame","href":"Titanium.Web.Proxy.WebSocketFrame.html","topicHref":"Titanium.Web.Proxy.WebSocketFrame.html","topicUid":"Titanium.Web.Proxy.WebSocketFrame","type":"Class"},{"name":"WebSocketFrameEncoder","href":"Titanium.Web.Proxy.WebSocketFrameEncoder.html","topicHref":"Titanium.Web.Proxy.WebSocketFrameEncoder.html","topicUid":"Titanium.Web.Proxy.WebSocketFrameEncoder","type":"Class"},{"name":"WebSocketFrameWriter","href":"Titanium.Web.Proxy.WebSocketFrameWriter.html","topicHref":"Titanium.Web.Proxy.WebSocketFrameWriter.html","topicUid":"Titanium.Web.Proxy.WebSocketFrameWriter","type":"Class"},{"name":"WebSocketProtocolException","href":"Titanium.Web.Proxy.WebSocketProtocolException.html","topicHref":"Titanium.Web.Proxy.WebSocketProtocolException.html","topicUid":"Titanium.Web.Proxy.WebSocketProtocolException","type":"Class"},{"name":"WebsocketOpCode","href":"Titanium.Web.Proxy.WebsocketOpCode.html","topicHref":"Titanium.Web.Proxy.WebsocketOpCode.html","topicUid":"Titanium.Web.Proxy.WebsocketOpCode","type":"Enum"}]},{"name":"Titanium.Web.Proxy.Caching","href":"Titanium.Web.Proxy.Caching.html","topicHref":"Titanium.Web.Proxy.Caching.html","topicUid":"Titanium.Web.Proxy.Caching","type":"Namespace","items":[{"name":"HttpResponseCacheMiddleware","href":"Titanium.Web.Proxy.Caching.HttpResponseCacheMiddleware.html","topicHref":"Titanium.Web.Proxy.Caching.HttpResponseCacheMiddleware.html","topicUid":"Titanium.Web.Proxy.Caching.HttpResponseCacheMiddleware","type":"Class"},{"name":"MemoryHttpResponseCache","href":"Titanium.Web.Proxy.Caching.MemoryHttpResponseCache.html","topicHref":"Titanium.Web.Proxy.Caching.MemoryHttpResponseCache.html","topicUid":"Titanium.Web.Proxy.Caching.MemoryHttpResponseCache","type":"Class"}]},{"name":"Titanium.Web.Proxy.Clusters","href":"Titanium.Web.Proxy.Clusters.html","topicHref":"Titanium.Web.Proxy.Clusters.html","topicUid":"Titanium.Web.Proxy.Clusters","type":"Namespace","items":[{"name":"ClusterManager","href":"Titanium.Web.Proxy.Clusters.ClusterManager.html","topicHref":"Titanium.Web.Proxy.Clusters.ClusterManager.html","topicUid":"Titanium.Web.Proxy.Clusters.ClusterManager","type":"Class"},{"name":"DestinationHealthTracker","href":"Titanium.Web.Proxy.Clusters.DestinationHealthTracker.html","topicHref":"Titanium.Web.Proxy.Clusters.DestinationHealthTracker.html","topicUid":"Titanium.Web.Proxy.Clusters.DestinationHealthTracker","type":"Class"},{"name":"LoadBalancer","href":"Titanium.Web.Proxy.Clusters.LoadBalancer.html","topicHref":"Titanium.Web.Proxy.Clusters.LoadBalancer.html","topicUid":"Titanium.Web.Proxy.Clusters.LoadBalancer","type":"Class"}]},{"name":"Titanium.Web.Proxy.Diagnostics","href":"Titanium.Web.Proxy.Diagnostics.html","topicHref":"Titanium.Web.Proxy.Diagnostics.html","topicUid":"Titanium.Web.Proxy.Diagnostics","type":"Namespace","items":[{"name":"ClientTlsTiming","href":"Titanium.Web.Proxy.Diagnostics.ClientTlsTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.ClientTlsTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.ClientTlsTiming","type":"Class"},{"name":"HttpRequestTiming","href":"Titanium.Web.Proxy.Diagnostics.HttpRequestTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.HttpRequestTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.HttpRequestTiming","type":"Class"},{"name":"TunnelConnectTiming","href":"Titanium.Web.Proxy.Diagnostics.TunnelConnectTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.TunnelConnectTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.TunnelConnectTiming","type":"Class"},{"name":"UpstreamConnectionTiming","href":"Titanium.Web.Proxy.Diagnostics.UpstreamConnectionTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.UpstreamConnectionTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.UpstreamConnectionTiming","type":"Class"}]},{"name":"Titanium.Web.Proxy.EventArguments","href":"Titanium.Web.Proxy.EventArguments.html","topicHref":"Titanium.Web.Proxy.EventArguments.html","topicUid":"Titanium.Web.Proxy.EventArguments","type":"Namespace","items":[{"name":"AsyncEventHandler","href":"Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html","topicHref":"Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html","topicUid":"Titanium.Web.Proxy.EventArguments.AsyncEventHandler`1","type":"Delegate"},{"name":"BeforeBodyWriteEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeBodyWriteEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeBodyWriteEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeBodyWriteEventArgs","type":"Class"},{"name":"BeforeHttpAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeHttpAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeHttpAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeHttpAuthenticateEventArgs","type":"Class"},{"name":"BeforeQuicAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeQuicAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeQuicAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeQuicAuthenticateEventArgs","type":"Class"},{"name":"BeforeSslAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs","type":"Class"},{"name":"CertificateSelectionEventArgs","href":"Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs","type":"Class"},{"name":"CertificateValidationEventArgs","href":"Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs","type":"Class"},{"name":"MultipartRequestPartSentEventArgs","href":"Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs","type":"Class"},{"name":"ProxyEventArgsBase","href":"Titanium.Web.Proxy.EventArguments.ProxyEventArgsBase.html","topicHref":"Titanium.Web.Proxy.EventArguments.ProxyEventArgsBase.html","topicUid":"Titanium.Web.Proxy.EventArguments.ProxyEventArgsBase","type":"Class"},{"name":"SessionEventArgs","href":"Titanium.Web.Proxy.EventArguments.SessionEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.SessionEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.SessionEventArgs","type":"Class"},{"name":"SessionEventArgsBase","href":"Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html","topicHref":"Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html","topicUid":"Titanium.Web.Proxy.EventArguments.SessionEventArgsBase","type":"Class"},{"name":"SocksAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.SocksAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.SocksAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.SocksAuthenticateEventArgs","type":"Class"},{"name":"TunnelConnectFailureEventArgs","href":"Titanium.Web.Proxy.EventArguments.TunnelConnectFailureEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.TunnelConnectFailureEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.TunnelConnectFailureEventArgs","type":"Class"},{"name":"TunnelConnectSessionEventArgs","href":"Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs","type":"Class"},{"name":"WebSocketFrameDirection","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameDirection.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameDirection.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameDirection","type":"Enum"},{"name":"WebSocketFrameElidedEventArgs","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs","type":"Class"},{"name":"WebSocketFrameInterceptAction","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptAction.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptAction.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptAction","type":"Enum"},{"name":"WebSocketFrameInterceptEventArgs","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptEventArgs","type":"Class"}]},{"name":"Titanium.Web.Proxy.Exceptions","href":"Titanium.Web.Proxy.Exceptions.html","topicHref":"Titanium.Web.Proxy.Exceptions.html","topicUid":"Titanium.Web.Proxy.Exceptions","type":"Namespace","items":[{"name":"BodyNotFoundException","href":"Titanium.Web.Proxy.Exceptions.BodyNotFoundException.html","topicHref":"Titanium.Web.Proxy.Exceptions.BodyNotFoundException.html","topicUid":"Titanium.Web.Proxy.Exceptions.BodyNotFoundException","type":"Class"},{"name":"OutboundDestinationBlockedException","href":"Titanium.Web.Proxy.Exceptions.OutboundDestinationBlockedException.html","topicHref":"Titanium.Web.Proxy.Exceptions.OutboundDestinationBlockedException.html","topicUid":"Titanium.Web.Proxy.Exceptions.OutboundDestinationBlockedException","type":"Class"},{"name":"ProxyAuthorizationException","href":"Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException","type":"Class"},{"name":"ProxyConnectException","href":"Titanium.Web.Proxy.Exceptions.ProxyConnectException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyConnectException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyConnectException","type":"Class"},{"name":"ProxyException","href":"Titanium.Web.Proxy.Exceptions.ProxyException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyException","type":"Class"},{"name":"ProxyHttpException","href":"Titanium.Web.Proxy.Exceptions.ProxyHttpException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyHttpException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyHttpException","type":"Class"},{"name":"ProxyTimeoutException","href":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutException","type":"Class"},{"name":"ProxyTimeoutKind","href":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutKind.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutKind.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutKind","type":"Enum"},{"name":"UpstreamProxyConnectException","href":"Titanium.Web.Proxy.Exceptions.UpstreamProxyConnectException.html","topicHref":"Titanium.Web.Proxy.Exceptions.UpstreamProxyConnectException.html","topicUid":"Titanium.Web.Proxy.Exceptions.UpstreamProxyConnectException","type":"Class"}]},{"name":"Titanium.Web.Proxy.Helpers","href":"Titanium.Web.Proxy.Helpers.html","topicHref":"Titanium.Web.Proxy.Helpers.html","topicUid":"Titanium.Web.Proxy.Helpers","type":"Namespace","items":[{"name":"UnixProxyBypassMapper","href":"Titanium.Web.Proxy.Helpers.UnixProxyBypassMapper.html","topicHref":"Titanium.Web.Proxy.Helpers.UnixProxyBypassMapper.html","topicUid":"Titanium.Web.Proxy.Helpers.UnixProxyBypassMapper","type":"Class"}]},{"name":"Titanium.Web.Proxy.Http","href":"Titanium.Web.Proxy.Http.html","topicHref":"Titanium.Web.Proxy.Http.html","topicUid":"Titanium.Web.Proxy.Http","type":"Namespace","items":[{"name":"ConnectRequest","href":"Titanium.Web.Proxy.Http.ConnectRequest.html","topicHref":"Titanium.Web.Proxy.Http.ConnectRequest.html","topicUid":"Titanium.Web.Proxy.Http.ConnectRequest","type":"Class"},{"name":"ConnectResponse","href":"Titanium.Web.Proxy.Http.ConnectResponse.html","topicHref":"Titanium.Web.Proxy.Http.ConnectResponse.html","topicUid":"Titanium.Web.Proxy.Http.ConnectResponse","type":"Class"},{"name":"HeaderCollection","href":"Titanium.Web.Proxy.Http.HeaderCollection.html","topicHref":"Titanium.Web.Proxy.Http.HeaderCollection.html","topicUid":"Titanium.Web.Proxy.Http.HeaderCollection","type":"Class"},{"name":"HeaderCollection.Enumerator","href":"Titanium.Web.Proxy.Http.HeaderCollection.Enumerator.html","topicHref":"Titanium.Web.Proxy.Http.HeaderCollection.Enumerator.html","topicUid":"Titanium.Web.Proxy.Http.HeaderCollection.Enumerator","type":"Struct"},{"name":"HttpWebClient","href":"Titanium.Web.Proxy.Http.HttpWebClient.html","topicHref":"Titanium.Web.Proxy.Http.HttpWebClient.html","topicUid":"Titanium.Web.Proxy.Http.HttpWebClient","type":"Class"},{"name":"KnownHeader","href":"Titanium.Web.Proxy.Http.KnownHeader.html","topicHref":"Titanium.Web.Proxy.Http.KnownHeader.html","topicUid":"Titanium.Web.Proxy.Http.KnownHeader","type":"Class"},{"name":"KnownHeaders","href":"Titanium.Web.Proxy.Http.KnownHeaders.html","topicHref":"Titanium.Web.Proxy.Http.KnownHeaders.html","topicUid":"Titanium.Web.Proxy.Http.KnownHeaders","type":"Class"},{"name":"ProxyResults","href":"Titanium.Web.Proxy.Http.ProxyResults.html","topicHref":"Titanium.Web.Proxy.Http.ProxyResults.html","topicUid":"Titanium.Web.Proxy.Http.ProxyResults","type":"Class"},{"name":"Request","href":"Titanium.Web.Proxy.Http.Request.html","topicHref":"Titanium.Web.Proxy.Http.Request.html","topicUid":"Titanium.Web.Proxy.Http.Request","type":"Class"},{"name":"RequestResponseBase","href":"Titanium.Web.Proxy.Http.RequestResponseBase.html","topicHref":"Titanium.Web.Proxy.Http.RequestResponseBase.html","topicUid":"Titanium.Web.Proxy.Http.RequestResponseBase","type":"Class"},{"name":"Response","href":"Titanium.Web.Proxy.Http.Response.html","topicHref":"Titanium.Web.Proxy.Http.Response.html","topicUid":"Titanium.Web.Proxy.Http.Response","type":"Class"},{"name":"StreamingProxyResult","href":"Titanium.Web.Proxy.Http.StreamingProxyResult.html","topicHref":"Titanium.Web.Proxy.Http.StreamingProxyResult.html","topicUid":"Titanium.Web.Proxy.Http.StreamingProxyResult","type":"Struct"},{"name":"TunnelType","href":"Titanium.Web.Proxy.Http.TunnelType.html","topicHref":"Titanium.Web.Proxy.Http.TunnelType.html","topicUid":"Titanium.Web.Proxy.Http.TunnelType","type":"Enum"}]},{"name":"Titanium.Web.Proxy.Http.Responses","href":"Titanium.Web.Proxy.Http.Responses.html","topicHref":"Titanium.Web.Proxy.Http.Responses.html","topicUid":"Titanium.Web.Proxy.Http.Responses","type":"Namespace","items":[{"name":"GenericResponse","href":"Titanium.Web.Proxy.Http.Responses.GenericResponse.html","topicHref":"Titanium.Web.Proxy.Http.Responses.GenericResponse.html","topicUid":"Titanium.Web.Proxy.Http.Responses.GenericResponse","type":"Class"},{"name":"OkResponse","href":"Titanium.Web.Proxy.Http.Responses.OkResponse.html","topicHref":"Titanium.Web.Proxy.Http.Responses.OkResponse.html","topicUid":"Titanium.Web.Proxy.Http.Responses.OkResponse","type":"Class"},{"name":"RedirectResponse","href":"Titanium.Web.Proxy.Http.Responses.RedirectResponse.html","topicHref":"Titanium.Web.Proxy.Http.Responses.RedirectResponse.html","topicUid":"Titanium.Web.Proxy.Http.Responses.RedirectResponse","type":"Class"}]},{"name":"Titanium.Web.Proxy.Http3","href":"Titanium.Web.Proxy.Http3.html","topicHref":"Titanium.Web.Proxy.Http3.html","topicUid":"Titanium.Web.Proxy.Http3","type":"Namespace","items":[{"name":"Http3NativeBootstrap","href":"Titanium.Web.Proxy.Http3.Http3NativeBootstrap.html","topicHref":"Titanium.Web.Proxy.Http3.Http3NativeBootstrap.html","topicUid":"Titanium.Web.Proxy.Http3.Http3NativeBootstrap","type":"Class"}]},{"name":"Titanium.Web.Proxy.Logging","href":"Titanium.Web.Proxy.Logging.html","topicHref":"Titanium.Web.Proxy.Logging.html","topicUid":"Titanium.Web.Proxy.Logging","type":"Namespace","items":[{"name":"ProxyLoggingOptions","href":"Titanium.Web.Proxy.Logging.ProxyLoggingOptions.html","topicHref":"Titanium.Web.Proxy.Logging.ProxyLoggingOptions.html","topicUid":"Titanium.Web.Proxy.Logging.ProxyLoggingOptions","type":"Class"}]},{"name":"Titanium.Web.Proxy.Middleware","href":"Titanium.Web.Proxy.Middleware.html","topicHref":"Titanium.Web.Proxy.Middleware.html","topicUid":"Titanium.Web.Proxy.Middleware","type":"Namespace","items":[{"name":"ProxyMiddlewarePipeline","href":"Titanium.Web.Proxy.Middleware.ProxyMiddlewarePipeline.html","topicHref":"Titanium.Web.Proxy.Middleware.ProxyMiddlewarePipeline.html","topicUid":"Titanium.Web.Proxy.Middleware.ProxyMiddlewarePipeline","type":"Class"}]},{"name":"Titanium.Web.Proxy.Models","href":"Titanium.Web.Proxy.Models.html","topicHref":"Titanium.Web.Proxy.Models.html","topicUid":"Titanium.Web.Proxy.Models","type":"Namespace","items":[{"name":"DecryptFailureBypassEntry","href":"Titanium.Web.Proxy.Models.DecryptFailureBypassEntry.html","topicHref":"Titanium.Web.Proxy.Models.DecryptFailureBypassEntry.html","topicUid":"Titanium.Web.Proxy.Models.DecryptFailureBypassEntry","type":"Class"},{"name":"ExplicitProxyEndPoint","href":"Titanium.Web.Proxy.Models.ExplicitProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.ExplicitProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.ExplicitProxyEndPoint","type":"Class"},{"name":"ExternalProxy","href":"Titanium.Web.Proxy.Models.ExternalProxy.html","topicHref":"Titanium.Web.Proxy.Models.ExternalProxy.html","topicUid":"Titanium.Web.Proxy.Models.ExternalProxy","type":"Class"},{"name":"ExternalProxyType","href":"Titanium.Web.Proxy.Models.ExternalProxyType.html","topicHref":"Titanium.Web.Proxy.Models.ExternalProxyType.html","topicUid":"Titanium.Web.Proxy.Models.ExternalProxyType","type":"Enum"},{"name":"HttpHeader","href":"Titanium.Web.Proxy.Models.HttpHeader.html","topicHref":"Titanium.Web.Proxy.Models.HttpHeader.html","topicUid":"Titanium.Web.Proxy.Models.HttpHeader","type":"Class"},{"name":"HttpInterceptionContext","href":"Titanium.Web.Proxy.Models.HttpInterceptionContext.html","topicHref":"Titanium.Web.Proxy.Models.HttpInterceptionContext.html","topicUid":"Titanium.Web.Proxy.Models.HttpInterceptionContext","type":"Struct"},{"name":"IExternalProxy","href":"Titanium.Web.Proxy.Models.IExternalProxy.html","topicHref":"Titanium.Web.Proxy.Models.IExternalProxy.html","topicUid":"Titanium.Web.Proxy.Models.IExternalProxy","type":"Interface"},{"name":"OriginHttpVersionPolicy","href":"Titanium.Web.Proxy.Models.OriginHttpVersionPolicy.html","topicHref":"Titanium.Web.Proxy.Models.OriginHttpVersionPolicy.html","topicUid":"Titanium.Web.Proxy.Models.OriginHttpVersionPolicy","type":"Enum"},{"name":"ProxyAuthenticationContext","href":"Titanium.Web.Proxy.Models.ProxyAuthenticationContext.html","topicHref":"Titanium.Web.Proxy.Models.ProxyAuthenticationContext.html","topicUid":"Titanium.Web.Proxy.Models.ProxyAuthenticationContext","type":"Class"},{"name":"ProxyAuthenticationResult","href":"Titanium.Web.Proxy.Models.ProxyAuthenticationResult.html","topicHref":"Titanium.Web.Proxy.Models.ProxyAuthenticationResult.html","topicUid":"Titanium.Web.Proxy.Models.ProxyAuthenticationResult","type":"Enum"},{"name":"ProxyEndPoint","href":"Titanium.Web.Proxy.Models.ProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.ProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.ProxyEndPoint","type":"Class"},{"name":"ProxyProtocolType","href":"Titanium.Web.Proxy.Models.ProxyProtocolType.html","topicHref":"Titanium.Web.Proxy.Models.ProxyProtocolType.html","topicUid":"Titanium.Web.Proxy.Models.ProxyProtocolType","type":"Enum"},{"name":"SocksProxyEndPoint","href":"Titanium.Web.Proxy.Models.SocksProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.SocksProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.SocksProxyEndPoint","type":"Class"},{"name":"TransparentBaseProxyEndPoint","href":"Titanium.Web.Proxy.Models.TransparentBaseProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.TransparentBaseProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.TransparentBaseProxyEndPoint","type":"Class"},{"name":"TransparentProxyEndPoint","href":"Titanium.Web.Proxy.Models.TransparentProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.TransparentProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.TransparentProxyEndPoint","type":"Class"},{"name":"TransparentQuicProxyEndPoint","href":"Titanium.Web.Proxy.Models.TransparentQuicProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.TransparentQuicProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.TransparentQuicProxyEndPoint","type":"Class"},{"name":"UpstreamHttpProtocol","href":"Titanium.Web.Proxy.Models.UpstreamHttpProtocol.html","topicHref":"Titanium.Web.Proxy.Models.UpstreamHttpProtocol.html","topicUid":"Titanium.Web.Proxy.Models.UpstreamHttpProtocol","type":"Enum"},{"name":"WinAuthCredentials","href":"Titanium.Web.Proxy.Models.WinAuthCredentials.html","topicHref":"Titanium.Web.Proxy.Models.WinAuthCredentials.html","topicUid":"Titanium.Web.Proxy.Models.WinAuthCredentials","type":"Class"}]},{"name":"Titanium.Web.Proxy.Network","href":"Titanium.Web.Proxy.Network.html","topicHref":"Titanium.Web.Proxy.Network.html","topicUid":"Titanium.Web.Proxy.Network","type":"Namespace","items":[{"name":"CertificateEngine","href":"Titanium.Web.Proxy.Network.CertificateEngine.html","topicHref":"Titanium.Web.Proxy.Network.CertificateEngine.html","topicUid":"Titanium.Web.Proxy.Network.CertificateEngine","type":"Enum"},{"name":"CertificateKeyAlgorithm","href":"Titanium.Web.Proxy.Network.CertificateKeyAlgorithm.html","topicHref":"Titanium.Web.Proxy.Network.CertificateKeyAlgorithm.html","topicUid":"Titanium.Web.Proxy.Network.CertificateKeyAlgorithm","type":"Enum"},{"name":"CertificateManager","href":"Titanium.Web.Proxy.Network.CertificateManager.html","topicHref":"Titanium.Web.Proxy.Network.CertificateManager.html","topicUid":"Titanium.Web.Proxy.Network.CertificateManager","type":"Class"},{"name":"CertificateOsTrustKind","href":"Titanium.Web.Proxy.Network.CertificateOsTrustKind.html","topicHref":"Titanium.Web.Proxy.Network.CertificateOsTrustKind.html","topicUid":"Titanium.Web.Proxy.Network.CertificateOsTrustKind","type":"Enum"},{"name":"CertificateOsTrustResult","href":"Titanium.Web.Proxy.Network.CertificateOsTrustResult.html","topicHref":"Titanium.Web.Proxy.Network.CertificateOsTrustResult.html","topicUid":"Titanium.Web.Proxy.Network.CertificateOsTrustResult","type":"Class"},{"name":"DefaultCertificateDiskCache","href":"Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html","topicHref":"Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html","topicUid":"Titanium.Web.Proxy.Network.DefaultCertificateDiskCache","type":"Class"},{"name":"FirefoxCertificateTrust","href":"Titanium.Web.Proxy.Network.FirefoxCertificateTrust.html","topicHref":"Titanium.Web.Proxy.Network.FirefoxCertificateTrust.html","topicUid":"Titanium.Web.Proxy.Network.FirefoxCertificateTrust","type":"Class"},{"name":"ICertificateCache","href":"Titanium.Web.Proxy.Network.ICertificateCache.html","topicHref":"Titanium.Web.Proxy.Network.ICertificateCache.html","topicUid":"Titanium.Web.Proxy.Network.ICertificateCache","type":"Interface"}]},{"name":"Titanium.Web.Proxy.Network.Quic","href":"Titanium.Web.Proxy.Network.Quic.html","topicHref":"Titanium.Web.Proxy.Network.Quic.html","topicUid":"Titanium.Web.Proxy.Network.Quic","type":"Namespace","items":[{"name":"IOriginalDestinationResolver","href":"Titanium.Web.Proxy.Network.Quic.IOriginalDestinationResolver.html","topicHref":"Titanium.Web.Proxy.Network.Quic.IOriginalDestinationResolver.html","topicUid":"Titanium.Web.Proxy.Network.Quic.IOriginalDestinationResolver","type":"Interface"}]},{"name":"Titanium.Web.Proxy.Options","href":"Titanium.Web.Proxy.Options.html","topicHref":"Titanium.Web.Proxy.Options.html","topicUid":"Titanium.Web.Proxy.Options","type":"Namespace","items":[{"name":"PolicyFamily","href":"Titanium.Web.Proxy.Options.PolicyFamily.html","topicHref":"Titanium.Web.Proxy.Options.PolicyFamily.html","topicUid":"Titanium.Web.Proxy.Options.PolicyFamily","type":"Enum"},{"name":"PolicyMode","href":"Titanium.Web.Proxy.Options.PolicyMode.html","topicHref":"Titanium.Web.Proxy.Options.PolicyMode.html","topicUid":"Titanium.Web.Proxy.Options.PolicyMode","type":"Enum"},{"name":"ProxyPolicyModes","href":"Titanium.Web.Proxy.Options.ProxyPolicyModes.html","topicHref":"Titanium.Web.Proxy.Options.ProxyPolicyModes.html","topicUid":"Titanium.Web.Proxy.Options.ProxyPolicyModes","type":"Class"},{"name":"ProxyProfile","href":"Titanium.Web.Proxy.Options.ProxyProfile.html","topicHref":"Titanium.Web.Proxy.Options.ProxyProfile.html","topicUid":"Titanium.Web.Proxy.Options.ProxyProfile","type":"Enum"},{"name":"ProxyProfileSettings","href":"Titanium.Web.Proxy.Options.ProxyProfileSettings.html","topicHref":"Titanium.Web.Proxy.Options.ProxyProfileSettings.html","topicUid":"Titanium.Web.Proxy.Options.ProxyProfileSettings","type":"Class"},{"name":"ProxyResourceLimits","href":"Titanium.Web.Proxy.Options.ProxyResourceLimits.html","topicHref":"Titanium.Web.Proxy.Options.ProxyResourceLimits.html","topicUid":"Titanium.Web.Proxy.Options.ProxyResourceLimits","type":"Class"},{"name":"ProxyTimeoutOptions","href":"Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html","topicHref":"Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html","topicUid":"Titanium.Web.Proxy.Options.ProxyTimeoutOptions","type":"Class"},{"name":"ResolvedSessionPolicy","href":"Titanium.Web.Proxy.Options.ResolvedSessionPolicy.html","topicHref":"Titanium.Web.Proxy.Options.ResolvedSessionPolicy.html","topicUid":"Titanium.Web.Proxy.Options.ResolvedSessionPolicy","type":"Class"}]},{"name":"Titanium.Web.Proxy.Routing","href":"Titanium.Web.Proxy.Routing.html","topicHref":"Titanium.Web.Proxy.Routing.html","topicUid":"Titanium.Web.Proxy.Routing","type":"Namespace","items":[{"name":"ReverseProxyFastPath","href":"Titanium.Web.Proxy.Routing.ReverseProxyFastPath.html","topicHref":"Titanium.Web.Proxy.Routing.ReverseProxyFastPath.html","topicUid":"Titanium.Web.Proxy.Routing.ReverseProxyFastPath","type":"Class"},{"name":"RouteMatcher","href":"Titanium.Web.Proxy.Routing.RouteMatcher.html","topicHref":"Titanium.Web.Proxy.Routing.RouteMatcher.html","topicUid":"Titanium.Web.Proxy.Routing.RouteMatcher","type":"Class"}]},{"name":"Titanium.Web.Proxy.StreamExtended","href":"Titanium.Web.Proxy.StreamExtended.html","topicHref":"Titanium.Web.Proxy.StreamExtended.html","topicUid":"Titanium.Web.Proxy.StreamExtended","type":"Namespace","items":[{"name":"ClientHelloInfo","href":"Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html","topicHref":"Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html","topicUid":"Titanium.Web.Proxy.StreamExtended.ClientHelloInfo","type":"Class"},{"name":"ServerHelloInfo","href":"Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html","topicHref":"Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html","topicUid":"Titanium.Web.Proxy.StreamExtended.ServerHelloInfo","type":"Class"}]},{"name":"Titanium.Web.Proxy.StreamExtended.BufferPool","href":"Titanium.Web.Proxy.StreamExtended.BufferPool.html","topicHref":"Titanium.Web.Proxy.StreamExtended.BufferPool.html","topicUid":"Titanium.Web.Proxy.StreamExtended.BufferPool","type":"Namespace","items":[{"name":"IBufferPool","href":"Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html","topicHref":"Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html","topicUid":"Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool","type":"Interface"}]},{"name":"Titanium.Web.Proxy.StreamExtended.Models","href":"Titanium.Web.Proxy.StreamExtended.Models.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Models.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Models","type":"Namespace","items":[{"name":"SslExtension","href":"Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Models.SslExtension","type":"Class"}]},{"name":"Titanium.Web.Proxy.StreamExtended.Network","href":"Titanium.Web.Proxy.StreamExtended.Network.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network","type":"Namespace","items":[{"name":"DataEventArgs","href":"Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs","type":"Class"},{"name":"IHttpStreamReader","href":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader","type":"Interface"},{"name":"IHttpStreamWriter","href":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter","type":"Interface"},{"name":"ILineStream","href":"Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.ILineStream","type":"Interface"},{"name":"IPeekStream","href":"Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.IPeekStream","type":"Interface"}]},{"name":"Titanium.Web.Proxy.Transforms","href":"Titanium.Web.Proxy.Transforms.html","topicHref":"Titanium.Web.Proxy.Transforms.html","topicUid":"Titanium.Web.Proxy.Transforms","type":"Namespace","items":[{"name":"TransformEngine","href":"Titanium.Web.Proxy.Transforms.TransformEngine.html","topicHref":"Titanium.Web.Proxy.Transforms.TransformEngine.html","topicUid":"Titanium.Web.Proxy.Transforms.TransformEngine","type":"Class"}]}],"memberLayout":"SamePage"}
+{"items":[{"name":"Titanium.Web.Proxy","href":"Titanium.Web.Proxy.html","topicHref":"Titanium.Web.Proxy.html","topicUid":"Titanium.Web.Proxy","type":"Namespace","items":[{"name":"ClientProcessId","href":"Titanium.Web.Proxy.ClientProcessId.html","topicHref":"Titanium.Web.Proxy.ClientProcessId.html","topicUid":"Titanium.Web.Proxy.ClientProcessId","type":"Class"},{"name":"MitmExclusionDefaults","href":"Titanium.Web.Proxy.MitmExclusionDefaults.html","topicHref":"Titanium.Web.Proxy.MitmExclusionDefaults.html","topicUid":"Titanium.Web.Proxy.MitmExclusionDefaults","type":"Class"},{"name":"MitmExclusionMode","href":"Titanium.Web.Proxy.MitmExclusionMode.html","topicHref":"Titanium.Web.Proxy.MitmExclusionMode.html","topicUid":"Titanium.Web.Proxy.MitmExclusionMode","type":"Enum"},{"name":"ProxyLimits","href":"Titanium.Web.Proxy.ProxyLimits.html","topicHref":"Titanium.Web.Proxy.ProxyLimits.html","topicUid":"Titanium.Web.Proxy.ProxyLimits","type":"Class"},{"name":"ProxyServer","href":"Titanium.Web.Proxy.ProxyServer.html","topicHref":"Titanium.Web.Proxy.ProxyServer.html","topicUid":"Titanium.Web.Proxy.ProxyServer","type":"Class"},{"name":"SystemProxyBypassRuleMode","href":"Titanium.Web.Proxy.SystemProxyBypassRuleMode.html","topicHref":"Titanium.Web.Proxy.SystemProxyBypassRuleMode.html","topicUid":"Titanium.Web.Proxy.SystemProxyBypassRuleMode","type":"Enum"},{"name":"SystemProxyChangeResult","href":"Titanium.Web.Proxy.SystemProxyChangeResult.html","topicHref":"Titanium.Web.Proxy.SystemProxyChangeResult.html","topicUid":"Titanium.Web.Proxy.SystemProxyChangeResult","type":"Struct"},{"name":"SystemProxyLoopbackPlacement","href":"Titanium.Web.Proxy.SystemProxyLoopbackPlacement.html","topicHref":"Titanium.Web.Proxy.SystemProxyLoopbackPlacement.html","topicUid":"Titanium.Web.Proxy.SystemProxyLoopbackPlacement","type":"Enum"},{"name":"SystemProxySettings","href":"Titanium.Web.Proxy.SystemProxySettings.html","topicHref":"Titanium.Web.Proxy.SystemProxySettings.html","topicUid":"Titanium.Web.Proxy.SystemProxySettings","type":"Class"},{"name":"WebSocketDecoder","href":"Titanium.Web.Proxy.WebSocketDecoder.html","topicHref":"Titanium.Web.Proxy.WebSocketDecoder.html","topicUid":"Titanium.Web.Proxy.WebSocketDecoder","type":"Class"},{"name":"WebSocketFrame","href":"Titanium.Web.Proxy.WebSocketFrame.html","topicHref":"Titanium.Web.Proxy.WebSocketFrame.html","topicUid":"Titanium.Web.Proxy.WebSocketFrame","type":"Class"},{"name":"WebSocketFrameEncoder","href":"Titanium.Web.Proxy.WebSocketFrameEncoder.html","topicHref":"Titanium.Web.Proxy.WebSocketFrameEncoder.html","topicUid":"Titanium.Web.Proxy.WebSocketFrameEncoder","type":"Class"},{"name":"WebSocketFrameWriter","href":"Titanium.Web.Proxy.WebSocketFrameWriter.html","topicHref":"Titanium.Web.Proxy.WebSocketFrameWriter.html","topicUid":"Titanium.Web.Proxy.WebSocketFrameWriter","type":"Class"},{"name":"WebSocketProtocolException","href":"Titanium.Web.Proxy.WebSocketProtocolException.html","topicHref":"Titanium.Web.Proxy.WebSocketProtocolException.html","topicUid":"Titanium.Web.Proxy.WebSocketProtocolException","type":"Class"},{"name":"WebsocketOpCode","href":"Titanium.Web.Proxy.WebsocketOpCode.html","topicHref":"Titanium.Web.Proxy.WebsocketOpCode.html","topicUid":"Titanium.Web.Proxy.WebsocketOpCode","type":"Enum"}]},{"name":"Titanium.Web.Proxy.Caching","href":"Titanium.Web.Proxy.Caching.html","topicHref":"Titanium.Web.Proxy.Caching.html","topicUid":"Titanium.Web.Proxy.Caching","type":"Namespace","items":[{"name":"HttpResponseCacheMiddleware","href":"Titanium.Web.Proxy.Caching.HttpResponseCacheMiddleware.html","topicHref":"Titanium.Web.Proxy.Caching.HttpResponseCacheMiddleware.html","topicUid":"Titanium.Web.Proxy.Caching.HttpResponseCacheMiddleware","type":"Class"},{"name":"MemoryHttpResponseCache","href":"Titanium.Web.Proxy.Caching.MemoryHttpResponseCache.html","topicHref":"Titanium.Web.Proxy.Caching.MemoryHttpResponseCache.html","topicUid":"Titanium.Web.Proxy.Caching.MemoryHttpResponseCache","type":"Class"}]},{"name":"Titanium.Web.Proxy.Clusters","href":"Titanium.Web.Proxy.Clusters.html","topicHref":"Titanium.Web.Proxy.Clusters.html","topicUid":"Titanium.Web.Proxy.Clusters","type":"Namespace","items":[{"name":"ClusterManager","href":"Titanium.Web.Proxy.Clusters.ClusterManager.html","topicHref":"Titanium.Web.Proxy.Clusters.ClusterManager.html","topicUid":"Titanium.Web.Proxy.Clusters.ClusterManager","type":"Class"},{"name":"DestinationHealthTracker","href":"Titanium.Web.Proxy.Clusters.DestinationHealthTracker.html","topicHref":"Titanium.Web.Proxy.Clusters.DestinationHealthTracker.html","topicUid":"Titanium.Web.Proxy.Clusters.DestinationHealthTracker","type":"Class"},{"name":"LoadBalancer","href":"Titanium.Web.Proxy.Clusters.LoadBalancer.html","topicHref":"Titanium.Web.Proxy.Clusters.LoadBalancer.html","topicUid":"Titanium.Web.Proxy.Clusters.LoadBalancer","type":"Class"}]},{"name":"Titanium.Web.Proxy.Diagnostics","href":"Titanium.Web.Proxy.Diagnostics.html","topicHref":"Titanium.Web.Proxy.Diagnostics.html","topicUid":"Titanium.Web.Proxy.Diagnostics","type":"Namespace","items":[{"name":"ClientTlsTiming","href":"Titanium.Web.Proxy.Diagnostics.ClientTlsTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.ClientTlsTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.ClientTlsTiming","type":"Class"},{"name":"HttpRequestTiming","href":"Titanium.Web.Proxy.Diagnostics.HttpRequestTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.HttpRequestTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.HttpRequestTiming","type":"Class"},{"name":"TunnelConnectTiming","href":"Titanium.Web.Proxy.Diagnostics.TunnelConnectTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.TunnelConnectTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.TunnelConnectTiming","type":"Class"},{"name":"UpstreamConnectionTiming","href":"Titanium.Web.Proxy.Diagnostics.UpstreamConnectionTiming.html","topicHref":"Titanium.Web.Proxy.Diagnostics.UpstreamConnectionTiming.html","topicUid":"Titanium.Web.Proxy.Diagnostics.UpstreamConnectionTiming","type":"Class"}]},{"name":"Titanium.Web.Proxy.EventArguments","href":"Titanium.Web.Proxy.EventArguments.html","topicHref":"Titanium.Web.Proxy.EventArguments.html","topicUid":"Titanium.Web.Proxy.EventArguments","type":"Namespace","items":[{"name":"AsyncEventHandler","href":"Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html","topicHref":"Titanium.Web.Proxy.EventArguments.AsyncEventHandler-1.html","topicUid":"Titanium.Web.Proxy.EventArguments.AsyncEventHandler`1","type":"Delegate"},{"name":"BeforeBodyWriteEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeBodyWriteEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeBodyWriteEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeBodyWriteEventArgs","type":"Class"},{"name":"BeforeHttpAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeHttpAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeHttpAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeHttpAuthenticateEventArgs","type":"Class"},{"name":"BeforeQuicAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeQuicAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeQuicAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeQuicAuthenticateEventArgs","type":"Class"},{"name":"BeforeSslAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.BeforeSslAuthenticateEventArgs","type":"Class"},{"name":"CertificateSelectionEventArgs","href":"Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.CertificateSelectionEventArgs","type":"Class"},{"name":"CertificateValidationEventArgs","href":"Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.CertificateValidationEventArgs","type":"Class"},{"name":"MultipartRequestPartSentEventArgs","href":"Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.MultipartRequestPartSentEventArgs","type":"Class"},{"name":"ProxyEventArgsBase","href":"Titanium.Web.Proxy.EventArguments.ProxyEventArgsBase.html","topicHref":"Titanium.Web.Proxy.EventArguments.ProxyEventArgsBase.html","topicUid":"Titanium.Web.Proxy.EventArguments.ProxyEventArgsBase","type":"Class"},{"name":"SessionEventArgs","href":"Titanium.Web.Proxy.EventArguments.SessionEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.SessionEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.SessionEventArgs","type":"Class"},{"name":"SessionEventArgsBase","href":"Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html","topicHref":"Titanium.Web.Proxy.EventArguments.SessionEventArgsBase.html","topicUid":"Titanium.Web.Proxy.EventArguments.SessionEventArgsBase","type":"Class"},{"name":"SocksAuthenticateEventArgs","href":"Titanium.Web.Proxy.EventArguments.SocksAuthenticateEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.SocksAuthenticateEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.SocksAuthenticateEventArgs","type":"Class"},{"name":"TunnelConnectFailureEventArgs","href":"Titanium.Web.Proxy.EventArguments.TunnelConnectFailureEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.TunnelConnectFailureEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.TunnelConnectFailureEventArgs","type":"Class"},{"name":"TunnelConnectSessionEventArgs","href":"Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.TunnelConnectSessionEventArgs","type":"Class"},{"name":"WebSocketFrameDirection","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameDirection.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameDirection.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameDirection","type":"Enum"},{"name":"WebSocketFrameElidedEventArgs","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs","type":"Class"},{"name":"WebSocketFrameInterceptAction","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptAction.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptAction.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptAction","type":"Enum"},{"name":"WebSocketFrameInterceptEventArgs","href":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptEventArgs.html","topicHref":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptEventArgs.html","topicUid":"Titanium.Web.Proxy.EventArguments.WebSocketFrameInterceptEventArgs","type":"Class"}]},{"name":"Titanium.Web.Proxy.Exceptions","href":"Titanium.Web.Proxy.Exceptions.html","topicHref":"Titanium.Web.Proxy.Exceptions.html","topicUid":"Titanium.Web.Proxy.Exceptions","type":"Namespace","items":[{"name":"BodyNotFoundException","href":"Titanium.Web.Proxy.Exceptions.BodyNotFoundException.html","topicHref":"Titanium.Web.Proxy.Exceptions.BodyNotFoundException.html","topicUid":"Titanium.Web.Proxy.Exceptions.BodyNotFoundException","type":"Class"},{"name":"OutboundDestinationBlockedException","href":"Titanium.Web.Proxy.Exceptions.OutboundDestinationBlockedException.html","topicHref":"Titanium.Web.Proxy.Exceptions.OutboundDestinationBlockedException.html","topicUid":"Titanium.Web.Proxy.Exceptions.OutboundDestinationBlockedException","type":"Class"},{"name":"ProxyAuthorizationException","href":"Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyAuthorizationException","type":"Class"},{"name":"ProxyConnectException","href":"Titanium.Web.Proxy.Exceptions.ProxyConnectException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyConnectException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyConnectException","type":"Class"},{"name":"ProxyException","href":"Titanium.Web.Proxy.Exceptions.ProxyException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyException","type":"Class"},{"name":"ProxyHttpException","href":"Titanium.Web.Proxy.Exceptions.ProxyHttpException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyHttpException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyHttpException","type":"Class"},{"name":"ProxyTimeoutException","href":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutException.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutException.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutException","type":"Class"},{"name":"ProxyTimeoutKind","href":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutKind.html","topicHref":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutKind.html","topicUid":"Titanium.Web.Proxy.Exceptions.ProxyTimeoutKind","type":"Enum"},{"name":"UpstreamProxyConnectException","href":"Titanium.Web.Proxy.Exceptions.UpstreamProxyConnectException.html","topicHref":"Titanium.Web.Proxy.Exceptions.UpstreamProxyConnectException.html","topicUid":"Titanium.Web.Proxy.Exceptions.UpstreamProxyConnectException","type":"Class"}]},{"name":"Titanium.Web.Proxy.Helpers","href":"Titanium.Web.Proxy.Helpers.html","topicHref":"Titanium.Web.Proxy.Helpers.html","topicUid":"Titanium.Web.Proxy.Helpers","type":"Namespace","items":[{"name":"UnixProxyBypassMapper","href":"Titanium.Web.Proxy.Helpers.UnixProxyBypassMapper.html","topicHref":"Titanium.Web.Proxy.Helpers.UnixProxyBypassMapper.html","topicUid":"Titanium.Web.Proxy.Helpers.UnixProxyBypassMapper","type":"Class"}]},{"name":"Titanium.Web.Proxy.Http","href":"Titanium.Web.Proxy.Http.html","topicHref":"Titanium.Web.Proxy.Http.html","topicUid":"Titanium.Web.Proxy.Http","type":"Namespace","items":[{"name":"ConnectRequest","href":"Titanium.Web.Proxy.Http.ConnectRequest.html","topicHref":"Titanium.Web.Proxy.Http.ConnectRequest.html","topicUid":"Titanium.Web.Proxy.Http.ConnectRequest","type":"Class"},{"name":"ConnectResponse","href":"Titanium.Web.Proxy.Http.ConnectResponse.html","topicHref":"Titanium.Web.Proxy.Http.ConnectResponse.html","topicUid":"Titanium.Web.Proxy.Http.ConnectResponse","type":"Class"},{"name":"HeaderCollection","href":"Titanium.Web.Proxy.Http.HeaderCollection.html","topicHref":"Titanium.Web.Proxy.Http.HeaderCollection.html","topicUid":"Titanium.Web.Proxy.Http.HeaderCollection","type":"Class"},{"name":"HeaderCollection.Enumerator","href":"Titanium.Web.Proxy.Http.HeaderCollection.Enumerator.html","topicHref":"Titanium.Web.Proxy.Http.HeaderCollection.Enumerator.html","topicUid":"Titanium.Web.Proxy.Http.HeaderCollection.Enumerator","type":"Struct"},{"name":"HeaderLimitsExceededException","href":"Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html","topicHref":"Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html","topicUid":"Titanium.Web.Proxy.Http.HeaderLimitsExceededException","type":"Class"},{"name":"HttpWebClient","href":"Titanium.Web.Proxy.Http.HttpWebClient.html","topicHref":"Titanium.Web.Proxy.Http.HttpWebClient.html","topicUid":"Titanium.Web.Proxy.Http.HttpWebClient","type":"Class"},{"name":"KnownHeader","href":"Titanium.Web.Proxy.Http.KnownHeader.html","topicHref":"Titanium.Web.Proxy.Http.KnownHeader.html","topicUid":"Titanium.Web.Proxy.Http.KnownHeader","type":"Class"},{"name":"KnownHeaders","href":"Titanium.Web.Proxy.Http.KnownHeaders.html","topicHref":"Titanium.Web.Proxy.Http.KnownHeaders.html","topicUid":"Titanium.Web.Proxy.Http.KnownHeaders","type":"Class"},{"name":"ProxyResults","href":"Titanium.Web.Proxy.Http.ProxyResults.html","topicHref":"Titanium.Web.Proxy.Http.ProxyResults.html","topicUid":"Titanium.Web.Proxy.Http.ProxyResults","type":"Class"},{"name":"Request","href":"Titanium.Web.Proxy.Http.Request.html","topicHref":"Titanium.Web.Proxy.Http.Request.html","topicUid":"Titanium.Web.Proxy.Http.Request","type":"Class"},{"name":"RequestResponseBase","href":"Titanium.Web.Proxy.Http.RequestResponseBase.html","topicHref":"Titanium.Web.Proxy.Http.RequestResponseBase.html","topicUid":"Titanium.Web.Proxy.Http.RequestResponseBase","type":"Class"},{"name":"Response","href":"Titanium.Web.Proxy.Http.Response.html","topicHref":"Titanium.Web.Proxy.Http.Response.html","topicUid":"Titanium.Web.Proxy.Http.Response","type":"Class"},{"name":"StreamingProxyResult","href":"Titanium.Web.Proxy.Http.StreamingProxyResult.html","topicHref":"Titanium.Web.Proxy.Http.StreamingProxyResult.html","topicUid":"Titanium.Web.Proxy.Http.StreamingProxyResult","type":"Struct"},{"name":"TunnelType","href":"Titanium.Web.Proxy.Http.TunnelType.html","topicHref":"Titanium.Web.Proxy.Http.TunnelType.html","topicUid":"Titanium.Web.Proxy.Http.TunnelType","type":"Enum"}]},{"name":"Titanium.Web.Proxy.Http.Responses","href":"Titanium.Web.Proxy.Http.Responses.html","topicHref":"Titanium.Web.Proxy.Http.Responses.html","topicUid":"Titanium.Web.Proxy.Http.Responses","type":"Namespace","items":[{"name":"GenericResponse","href":"Titanium.Web.Proxy.Http.Responses.GenericResponse.html","topicHref":"Titanium.Web.Proxy.Http.Responses.GenericResponse.html","topicUid":"Titanium.Web.Proxy.Http.Responses.GenericResponse","type":"Class"},{"name":"OkResponse","href":"Titanium.Web.Proxy.Http.Responses.OkResponse.html","topicHref":"Titanium.Web.Proxy.Http.Responses.OkResponse.html","topicUid":"Titanium.Web.Proxy.Http.Responses.OkResponse","type":"Class"},{"name":"RedirectResponse","href":"Titanium.Web.Proxy.Http.Responses.RedirectResponse.html","topicHref":"Titanium.Web.Proxy.Http.Responses.RedirectResponse.html","topicUid":"Titanium.Web.Proxy.Http.Responses.RedirectResponse","type":"Class"}]},{"name":"Titanium.Web.Proxy.Http3","href":"Titanium.Web.Proxy.Http3.html","topicHref":"Titanium.Web.Proxy.Http3.html","topicUid":"Titanium.Web.Proxy.Http3","type":"Namespace","items":[{"name":"Http3NativeBootstrap","href":"Titanium.Web.Proxy.Http3.Http3NativeBootstrap.html","topicHref":"Titanium.Web.Proxy.Http3.Http3NativeBootstrap.html","topicUid":"Titanium.Web.Proxy.Http3.Http3NativeBootstrap","type":"Class"}]},{"name":"Titanium.Web.Proxy.Logging","href":"Titanium.Web.Proxy.Logging.html","topicHref":"Titanium.Web.Proxy.Logging.html","topicUid":"Titanium.Web.Proxy.Logging","type":"Namespace","items":[{"name":"ProxyLoggingOptions","href":"Titanium.Web.Proxy.Logging.ProxyLoggingOptions.html","topicHref":"Titanium.Web.Proxy.Logging.ProxyLoggingOptions.html","topicUid":"Titanium.Web.Proxy.Logging.ProxyLoggingOptions","type":"Class"}]},{"name":"Titanium.Web.Proxy.Middleware","href":"Titanium.Web.Proxy.Middleware.html","topicHref":"Titanium.Web.Proxy.Middleware.html","topicUid":"Titanium.Web.Proxy.Middleware","type":"Namespace","items":[{"name":"ProxyMiddlewarePipeline","href":"Titanium.Web.Proxy.Middleware.ProxyMiddlewarePipeline.html","topicHref":"Titanium.Web.Proxy.Middleware.ProxyMiddlewarePipeline.html","topicUid":"Titanium.Web.Proxy.Middleware.ProxyMiddlewarePipeline","type":"Class"}]},{"name":"Titanium.Web.Proxy.Models","href":"Titanium.Web.Proxy.Models.html","topicHref":"Titanium.Web.Proxy.Models.html","topicUid":"Titanium.Web.Proxy.Models","type":"Namespace","items":[{"name":"DecryptFailureBypassEntry","href":"Titanium.Web.Proxy.Models.DecryptFailureBypassEntry.html","topicHref":"Titanium.Web.Proxy.Models.DecryptFailureBypassEntry.html","topicUid":"Titanium.Web.Proxy.Models.DecryptFailureBypassEntry","type":"Class"},{"name":"ExplicitProxyEndPoint","href":"Titanium.Web.Proxy.Models.ExplicitProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.ExplicitProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.ExplicitProxyEndPoint","type":"Class"},{"name":"ExternalProxy","href":"Titanium.Web.Proxy.Models.ExternalProxy.html","topicHref":"Titanium.Web.Proxy.Models.ExternalProxy.html","topicUid":"Titanium.Web.Proxy.Models.ExternalProxy","type":"Class"},{"name":"ExternalProxyType","href":"Titanium.Web.Proxy.Models.ExternalProxyType.html","topicHref":"Titanium.Web.Proxy.Models.ExternalProxyType.html","topicUid":"Titanium.Web.Proxy.Models.ExternalProxyType","type":"Enum"},{"name":"HttpHeader","href":"Titanium.Web.Proxy.Models.HttpHeader.html","topicHref":"Titanium.Web.Proxy.Models.HttpHeader.html","topicUid":"Titanium.Web.Proxy.Models.HttpHeader","type":"Class"},{"name":"HttpInterceptionContext","href":"Titanium.Web.Proxy.Models.HttpInterceptionContext.html","topicHref":"Titanium.Web.Proxy.Models.HttpInterceptionContext.html","topicUid":"Titanium.Web.Proxy.Models.HttpInterceptionContext","type":"Struct"},{"name":"IExternalProxy","href":"Titanium.Web.Proxy.Models.IExternalProxy.html","topicHref":"Titanium.Web.Proxy.Models.IExternalProxy.html","topicUid":"Titanium.Web.Proxy.Models.IExternalProxy","type":"Interface"},{"name":"OriginHttpVersionPolicy","href":"Titanium.Web.Proxy.Models.OriginHttpVersionPolicy.html","topicHref":"Titanium.Web.Proxy.Models.OriginHttpVersionPolicy.html","topicUid":"Titanium.Web.Proxy.Models.OriginHttpVersionPolicy","type":"Enum"},{"name":"ProxyAuthenticationContext","href":"Titanium.Web.Proxy.Models.ProxyAuthenticationContext.html","topicHref":"Titanium.Web.Proxy.Models.ProxyAuthenticationContext.html","topicUid":"Titanium.Web.Proxy.Models.ProxyAuthenticationContext","type":"Class"},{"name":"ProxyAuthenticationResult","href":"Titanium.Web.Proxy.Models.ProxyAuthenticationResult.html","topicHref":"Titanium.Web.Proxy.Models.ProxyAuthenticationResult.html","topicUid":"Titanium.Web.Proxy.Models.ProxyAuthenticationResult","type":"Enum"},{"name":"ProxyEndPoint","href":"Titanium.Web.Proxy.Models.ProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.ProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.ProxyEndPoint","type":"Class"},{"name":"ProxyProtocolType","href":"Titanium.Web.Proxy.Models.ProxyProtocolType.html","topicHref":"Titanium.Web.Proxy.Models.ProxyProtocolType.html","topicUid":"Titanium.Web.Proxy.Models.ProxyProtocolType","type":"Enum"},{"name":"SocksProxyEndPoint","href":"Titanium.Web.Proxy.Models.SocksProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.SocksProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.SocksProxyEndPoint","type":"Class"},{"name":"TransparentBaseProxyEndPoint","href":"Titanium.Web.Proxy.Models.TransparentBaseProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.TransparentBaseProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.TransparentBaseProxyEndPoint","type":"Class"},{"name":"TransparentProxyEndPoint","href":"Titanium.Web.Proxy.Models.TransparentProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.TransparentProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.TransparentProxyEndPoint","type":"Class"},{"name":"TransparentQuicProxyEndPoint","href":"Titanium.Web.Proxy.Models.TransparentQuicProxyEndPoint.html","topicHref":"Titanium.Web.Proxy.Models.TransparentQuicProxyEndPoint.html","topicUid":"Titanium.Web.Proxy.Models.TransparentQuicProxyEndPoint","type":"Class"},{"name":"UpstreamHttpProtocol","href":"Titanium.Web.Proxy.Models.UpstreamHttpProtocol.html","topicHref":"Titanium.Web.Proxy.Models.UpstreamHttpProtocol.html","topicUid":"Titanium.Web.Proxy.Models.UpstreamHttpProtocol","type":"Enum"},{"name":"WinAuthCredentials","href":"Titanium.Web.Proxy.Models.WinAuthCredentials.html","topicHref":"Titanium.Web.Proxy.Models.WinAuthCredentials.html","topicUid":"Titanium.Web.Proxy.Models.WinAuthCredentials","type":"Class"}]},{"name":"Titanium.Web.Proxy.Network","href":"Titanium.Web.Proxy.Network.html","topicHref":"Titanium.Web.Proxy.Network.html","topicUid":"Titanium.Web.Proxy.Network","type":"Namespace","items":[{"name":"CertificateEngine","href":"Titanium.Web.Proxy.Network.CertificateEngine.html","topicHref":"Titanium.Web.Proxy.Network.CertificateEngine.html","topicUid":"Titanium.Web.Proxy.Network.CertificateEngine","type":"Enum"},{"name":"CertificateKeyAlgorithm","href":"Titanium.Web.Proxy.Network.CertificateKeyAlgorithm.html","topicHref":"Titanium.Web.Proxy.Network.CertificateKeyAlgorithm.html","topicUid":"Titanium.Web.Proxy.Network.CertificateKeyAlgorithm","type":"Enum"},{"name":"CertificateManager","href":"Titanium.Web.Proxy.Network.CertificateManager.html","topicHref":"Titanium.Web.Proxy.Network.CertificateManager.html","topicUid":"Titanium.Web.Proxy.Network.CertificateManager","type":"Class"},{"name":"CertificateOsTrustKind","href":"Titanium.Web.Proxy.Network.CertificateOsTrustKind.html","topicHref":"Titanium.Web.Proxy.Network.CertificateOsTrustKind.html","topicUid":"Titanium.Web.Proxy.Network.CertificateOsTrustKind","type":"Enum"},{"name":"CertificateOsTrustResult","href":"Titanium.Web.Proxy.Network.CertificateOsTrustResult.html","topicHref":"Titanium.Web.Proxy.Network.CertificateOsTrustResult.html","topicUid":"Titanium.Web.Proxy.Network.CertificateOsTrustResult","type":"Class"},{"name":"DefaultCertificateDiskCache","href":"Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html","topicHref":"Titanium.Web.Proxy.Network.DefaultCertificateDiskCache.html","topicUid":"Titanium.Web.Proxy.Network.DefaultCertificateDiskCache","type":"Class"},{"name":"FirefoxCertificateTrust","href":"Titanium.Web.Proxy.Network.FirefoxCertificateTrust.html","topicHref":"Titanium.Web.Proxy.Network.FirefoxCertificateTrust.html","topicUid":"Titanium.Web.Proxy.Network.FirefoxCertificateTrust","type":"Class"},{"name":"ICertificateCache","href":"Titanium.Web.Proxy.Network.ICertificateCache.html","topicHref":"Titanium.Web.Proxy.Network.ICertificateCache.html","topicUid":"Titanium.Web.Proxy.Network.ICertificateCache","type":"Interface"}]},{"name":"Titanium.Web.Proxy.Network.Quic","href":"Titanium.Web.Proxy.Network.Quic.html","topicHref":"Titanium.Web.Proxy.Network.Quic.html","topicUid":"Titanium.Web.Proxy.Network.Quic","type":"Namespace","items":[{"name":"IOriginalDestinationResolver","href":"Titanium.Web.Proxy.Network.Quic.IOriginalDestinationResolver.html","topicHref":"Titanium.Web.Proxy.Network.Quic.IOriginalDestinationResolver.html","topicUid":"Titanium.Web.Proxy.Network.Quic.IOriginalDestinationResolver","type":"Interface"}]},{"name":"Titanium.Web.Proxy.Options","href":"Titanium.Web.Proxy.Options.html","topicHref":"Titanium.Web.Proxy.Options.html","topicUid":"Titanium.Web.Proxy.Options","type":"Namespace","items":[{"name":"PolicyFamily","href":"Titanium.Web.Proxy.Options.PolicyFamily.html","topicHref":"Titanium.Web.Proxy.Options.PolicyFamily.html","topicUid":"Titanium.Web.Proxy.Options.PolicyFamily","type":"Enum"},{"name":"PolicyMode","href":"Titanium.Web.Proxy.Options.PolicyMode.html","topicHref":"Titanium.Web.Proxy.Options.PolicyMode.html","topicUid":"Titanium.Web.Proxy.Options.PolicyMode","type":"Enum"},{"name":"ProxyPolicyModes","href":"Titanium.Web.Proxy.Options.ProxyPolicyModes.html","topicHref":"Titanium.Web.Proxy.Options.ProxyPolicyModes.html","topicUid":"Titanium.Web.Proxy.Options.ProxyPolicyModes","type":"Class"},{"name":"ProxyProfile","href":"Titanium.Web.Proxy.Options.ProxyProfile.html","topicHref":"Titanium.Web.Proxy.Options.ProxyProfile.html","topicUid":"Titanium.Web.Proxy.Options.ProxyProfile","type":"Enum"},{"name":"ProxyProfileSettings","href":"Titanium.Web.Proxy.Options.ProxyProfileSettings.html","topicHref":"Titanium.Web.Proxy.Options.ProxyProfileSettings.html","topicUid":"Titanium.Web.Proxy.Options.ProxyProfileSettings","type":"Class"},{"name":"ProxyResourceLimits","href":"Titanium.Web.Proxy.Options.ProxyResourceLimits.html","topicHref":"Titanium.Web.Proxy.Options.ProxyResourceLimits.html","topicUid":"Titanium.Web.Proxy.Options.ProxyResourceLimits","type":"Class"},{"name":"ProxyTimeoutOptions","href":"Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html","topicHref":"Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html","topicUid":"Titanium.Web.Proxy.Options.ProxyTimeoutOptions","type":"Class"},{"name":"ResolvedSessionPolicy","href":"Titanium.Web.Proxy.Options.ResolvedSessionPolicy.html","topicHref":"Titanium.Web.Proxy.Options.ResolvedSessionPolicy.html","topicUid":"Titanium.Web.Proxy.Options.ResolvedSessionPolicy","type":"Class"}]},{"name":"Titanium.Web.Proxy.Routing","href":"Titanium.Web.Proxy.Routing.html","topicHref":"Titanium.Web.Proxy.Routing.html","topicUid":"Titanium.Web.Proxy.Routing","type":"Namespace","items":[{"name":"ReverseProxyFastPath","href":"Titanium.Web.Proxy.Routing.ReverseProxyFastPath.html","topicHref":"Titanium.Web.Proxy.Routing.ReverseProxyFastPath.html","topicUid":"Titanium.Web.Proxy.Routing.ReverseProxyFastPath","type":"Class"},{"name":"RouteMatcher","href":"Titanium.Web.Proxy.Routing.RouteMatcher.html","topicHref":"Titanium.Web.Proxy.Routing.RouteMatcher.html","topicUid":"Titanium.Web.Proxy.Routing.RouteMatcher","type":"Class"}]},{"name":"Titanium.Web.Proxy.StreamExtended","href":"Titanium.Web.Proxy.StreamExtended.html","topicHref":"Titanium.Web.Proxy.StreamExtended.html","topicUid":"Titanium.Web.Proxy.StreamExtended","type":"Namespace","items":[{"name":"ClientHelloInfo","href":"Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html","topicHref":"Titanium.Web.Proxy.StreamExtended.ClientHelloInfo.html","topicUid":"Titanium.Web.Proxy.StreamExtended.ClientHelloInfo","type":"Class"},{"name":"ServerHelloInfo","href":"Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html","topicHref":"Titanium.Web.Proxy.StreamExtended.ServerHelloInfo.html","topicUid":"Titanium.Web.Proxy.StreamExtended.ServerHelloInfo","type":"Class"}]},{"name":"Titanium.Web.Proxy.StreamExtended.BufferPool","href":"Titanium.Web.Proxy.StreamExtended.BufferPool.html","topicHref":"Titanium.Web.Proxy.StreamExtended.BufferPool.html","topicUid":"Titanium.Web.Proxy.StreamExtended.BufferPool","type":"Namespace","items":[{"name":"IBufferPool","href":"Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html","topicHref":"Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool.html","topicUid":"Titanium.Web.Proxy.StreamExtended.BufferPool.IBufferPool","type":"Interface"}]},{"name":"Titanium.Web.Proxy.StreamExtended.Models","href":"Titanium.Web.Proxy.StreamExtended.Models.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Models.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Models","type":"Namespace","items":[{"name":"SslExtension","href":"Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Models.SslExtension.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Models.SslExtension","type":"Class"}]},{"name":"Titanium.Web.Proxy.StreamExtended.Network","href":"Titanium.Web.Proxy.StreamExtended.Network.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network","type":"Namespace","items":[{"name":"DataEventArgs","href":"Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.DataEventArgs","type":"Class"},{"name":"IHttpStreamReader","href":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamReader","type":"Interface"},{"name":"IHttpStreamWriter","href":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.IHttpStreamWriter","type":"Interface"},{"name":"ILineStream","href":"Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.ILineStream.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.ILineStream","type":"Interface"},{"name":"IPeekStream","href":"Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html","topicHref":"Titanium.Web.Proxy.StreamExtended.Network.IPeekStream.html","topicUid":"Titanium.Web.Proxy.StreamExtended.Network.IPeekStream","type":"Interface"}]},{"name":"Titanium.Web.Proxy.Transforms","href":"Titanium.Web.Proxy.Transforms.html","topicHref":"Titanium.Web.Proxy.Transforms.html","topicUid":"Titanium.Web.Proxy.Transforms","type":"Namespace","items":[{"name":"TransformEngine","href":"Titanium.Web.Proxy.Transforms.TransformEngine.html","topicHref":"Titanium.Web.Proxy.Transforms.TransformEngine.html","topicUid":"Titanium.Web.Proxy.Transforms.TransformEngine","type":"Class"}]}],"memberLayout":"SamePage"}
diff --git a/docs/index.json b/docs/index.json
index d78964710..123092119 100644
--- a/docs/index.json
+++ b/docs/index.json
@@ -244,6 +244,11 @@
"title": "Class HeaderCollection | Titanium Web Proxy",
"summary": "Class HeaderCollection The http header collection. Inheritance object HeaderCollection Implements IEnumerable IEnumerable Inherited Members object.Equals(object) object.Equals(object, object) object.GetHashCode() object.GetType() object.MemberwiseClone() object.ReferenceEquals(object, object) object.ToString() Namespace: Titanium.Web.Proxy.Http Assembly: Titanium.Web.Proxy.dll Syntax [TypeConverter(typeof(ExpandableObjectConverter))] public class HeaderCollection : IEnumerable, IEnumerable Constructors | Edit this page View Source HeaderCollection() Initializes a new instance of the HeaderCollection class. Declaration public HeaderCollection() Properties | Edit this page View Source Headers Unique Request header collection. Declaration public ReadOnlyDictionary Headers { get; } Property Value Type Description ReadOnlyDictionary | Edit this page View Source NonUniqueHeaders Non-unique headers. Values are read-only views over the internal lists so callers cannot Add/Clear storage that still belongs to this collection. Declaration public ReadOnlyDictionary> NonUniqueHeaders { get; } Property Value Type Description ReadOnlyDictionary> Methods | Edit this page View Source AddHeader(string, string) Add a new header with given name and value Declaration public void AddHeader(string name, string value) Parameters Type Name Description string name string value | Edit this page View Source AddHeader(HttpHeader) Adds the given header object to Request Declaration public void AddHeader(HttpHeader newHeader) Parameters Type Name Description HttpHeader newHeader | Edit this page View Source AddHeaders(IEnumerable>?) Adds the given header objects to Request Declaration public void AddHeaders(IEnumerable>? newHeaders) Parameters Type Name Description IEnumerable> newHeaders | Edit this page View Source AddHeaders(IEnumerable>?) Adds the given header objects to Request Declaration public void AddHeaders(IEnumerable>? newHeaders) Parameters Type Name Description IEnumerable> newHeaders | Edit this page View Source AddHeaders(IEnumerable?) Adds the given header objects to Request Declaration public void AddHeaders(IEnumerable? newHeaders) Parameters Type Name Description IEnumerable newHeaders | Edit this page View Source Clear() Removes all the headers. Declaration public void Clear() | Edit this page View Source GetAllHeaders() Returns all headers Declaration public List GetAllHeaders() Returns Type Description List | Edit this page View Source GetEnumerator() Returns an enumerator that iterates through the collection. Declaration public HeaderCollection.Enumerator GetEnumerator() Returns Type Description HeaderCollection.Enumerator An enumerator that can be used to iterate through the collection. Remarks Returns the concrete HeaderCollection.Enumerator struct rather than an interface type, matching the pattern used by List/Dictionary in the BCL: a foreach over a variable statically typed as HeaderCollection binds to this overload directly and allocates nothing, whereas the previous implementation (chaining Concat(IEnumerable, IEnumerable) and SelectMany(IEnumerable, Func>) over the two backing dictionaries) allocated two LINQ iterator objects on every call. This matters here specifically because every outgoing request and response header block is enumerated at least once to serialize it onto the wire, making this one of the hottest paths in the proxy. Code that holds this collection through the IEnumerable interface (e.g. LINQ operators) still gets a correct enumerator via the explicit interface implementation below, at the cost of one boxing allocation - unavoidable through that surface, and unchanged from before this optimization. | Edit this page View Source GetFirstHeader(string) Declaration public HttpHeader? GetFirstHeader(string name) Parameters Type Name Description string name Returns Type Description HttpHeader | Edit this page View Source GetHeaders(string) Returns all headers with given name if exists Returns null if doesn't exist Declaration public List? GetHeaders(string name) Parameters Type Name Description string name Returns Type Description List | Edit this page View Source HeaderExists(string) True if header exists Declaration public bool HeaderExists(string name) Parameters Type Name Description string name Returns Type Description bool | Edit this page View Source RemoveHeader(string) removes all headers with given name Declaration public bool RemoveHeader(string headerName) Parameters Type Name Description string headerName Returns Type Description bool True if header was removed False if no header exists with given name | Edit this page View Source RemoveHeader(KnownHeader) removes all headers with given name Declaration public bool RemoveHeader(KnownHeader headerName) Parameters Type Name Description KnownHeader headerName Returns Type Description bool True if header was removed False if no header exists with given name | Edit this page View Source RemoveHeader(HttpHeader) Removes given header object if it exist Declaration public bool RemoveHeader(HttpHeader header) Parameters Type Name Description HttpHeader header Returns true if header exists and was removed Returns Type Description bool Implements IEnumerable IEnumerable"
},
+ "api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html": {
+ "href": "api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html",
+ "title": "Class HeaderLimitsExceededException | Titanium Web Proxy",
+ "summary": "Class HeaderLimitsExceededException An HTTP/1 header block exceeded MaxHeaderCount or MaxHeaderAggregateBytes while HeaderLimits was Enforce. Not an IOException, so it is not treated as a client disconnect. Inheritance object Exception HeaderLimitsExceededException Implements ISerializable Inherited Members Exception.GetBaseException() Exception.GetType() Exception.ToString() Exception.Data Exception.HelpLink Exception.HResult Exception.InnerException Exception.Message Exception.Source Exception.StackTrace Exception.TargetSite object.Equals(object) object.Equals(object, object) object.GetHashCode() object.ReferenceEquals(object, object) Namespace: Titanium.Web.Proxy.Http Assembly: Titanium.Web.Proxy.dll Syntax public sealed class HeaderLimitsExceededException : Exception, ISerializable Constructors | Edit this page View Source HeaderLimitsExceededException(bool, long, long, bool) Creates an exception for one header-block breach. Declaration public HeaderLimitsExceededException(bool isCount, long observed, long limit, bool isRequest) Parameters Type Name Description bool isCount long observed long limit bool isRequest Properties | Edit this page View Source IsCount True when the breach is the header count. False when it is the byte total. Declaration public bool IsCount { get; } Property Value Type Description bool | Edit this page View Source IsRequest True when the breach was on request headers. Declaration public bool IsRequest { get; } Property Value Type Description bool | Edit this page View Source Limit The configured limit that was crossed. Declaration public long Limit { get; } Property Value Type Description long | Edit this page View Source Observed The count or byte total that crossed the limit. Declaration public long Observed { get; } Property Value Type Description long Implements ISerializable"
+ },
"api/Titanium.Web.Proxy.Http.HttpWebClient.html": {
"href": "api/Titanium.Web.Proxy.Http.HttpWebClient.html",
"title": "Class HttpWebClient | Titanium Web Proxy",
@@ -312,7 +317,7 @@
"api/Titanium.Web.Proxy.Http.html": {
"href": "api/Titanium.Web.Proxy.Http.html",
"title": "Namespace Titanium.Web.Proxy.Http | Titanium Web Proxy",
- "summary": "Namespace Titanium.Web.Proxy.Http Classes ConnectRequest The tcp tunnel Connect request. ConnectResponse The tcp tunnel connect response object. HeaderCollection The http header collection. HttpWebClient Used to communicate with the server over HTTP(S) KnownHeader KnownHeaders Well known http headers. ProxyResults Factory methods that build synthetic Response or StreamingProxyResult objects for use with Respond(Response, bool) and RespondStreaming(StreamingProxyResult, bool). Request Http(s) request object RequestResponseBase Abstract base class for similar objects shared by both request and response objects. Response Http(s) response object Structs HeaderCollection.Enumerator Walks first the unique-header dictionary's values, then each list in the non-unique-header dictionary's values in turn - the same effective order as the previous Concat(...SelectMany(...)) implementation - without allocating any LINQ iterator objects. See the remarks on GetEnumerator() for why this is worth a hand-written enumerator instead of the equivalent LINQ expression. StreamingProxyResult Pairs a synthetic response (status and headers) with a delegate that writes the body on the fly. Pass to RespondStreaming(StreamingProxyResult, bool) to stream without buffering the whole body in memory. Enums TunnelType"
+ "summary": "Namespace Titanium.Web.Proxy.Http Classes ConnectRequest The tcp tunnel Connect request. ConnectResponse The tcp tunnel connect response object. HeaderCollection The http header collection. HeaderLimitsExceededException An HTTP/1 header block exceeded MaxHeaderCount or MaxHeaderAggregateBytes while HeaderLimits was Enforce. Not an IOException, so it is not treated as a client disconnect. HttpWebClient Used to communicate with the server over HTTP(S) KnownHeader KnownHeaders Well known http headers. ProxyResults Factory methods that build synthetic Response or StreamingProxyResult objects for use with Respond(Response, bool) and RespondStreaming(StreamingProxyResult, bool). Request Http(s) request object RequestResponseBase Abstract base class for similar objects shared by both request and response objects. Response Http(s) response object Structs HeaderCollection.Enumerator Walks first the unique-header dictionary's values, then each list in the non-unique-header dictionary's values in turn - the same effective order as the previous Concat(...SelectMany(...)) implementation - without allocating any LINQ iterator objects. See the remarks on GetEnumerator() for why this is worth a hand-written enumerator instead of the equivalent LINQ expression. StreamingProxyResult Pairs a synthetic response (status and headers) with a delegate that writes the body on the fly. Pass to RespondStreaming(StreamingProxyResult, bool) to stream without buffering the whole body in memory. Enums TunnelType"
},
"api/Titanium.Web.Proxy.Http3.Http3NativeBootstrap.html": {
"href": "api/Titanium.Web.Proxy.Http3.Http3NativeBootstrap.html",
@@ -517,7 +522,7 @@
"api/Titanium.Web.Proxy.Options.ProxyPolicyModes.html": {
"href": "api/Titanium.Web.Proxy.Options.ProxyPolicyModes.html",
"title": "Class ProxyPolicyModes | Titanium Web Proxy",
- "summary": "Class ProxyPolicyModes Immutable snapshot of the PolicyMode selected for each PolicyFamily, plus the one deliberately-separate AllowAmbiguousFraming escape hatch. Read live by enforcement call sites through PolicyModes, which the plan's rollout section requires to be a runtime switch: replacing the whole snapshot (see PolicyModes's setter) rather than mutating a field lets an operator drop every family to Observe without redeploying, while every in-flight request that already read the previous snapshot keeps behaving consistently with whichever snapshot it observed. AllowAmbiguousFraming is not a PolicyFamily member and has no corresponding PolicyMode: framing, chunk parsing and Content-Length/Transfer-Encoding resolution have no safe \"detect but let it through\" middle ground, so this is a single named, binary, off-by-default flag that relays malformed framing instead of rejecting it - useful only for security research that needs to observe how a client or origin reacts to smuggling-shaped input through the proxy. No profile sets it: Create(PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode) never accepts it as a parameter, and the only way to turn it on is the explicit WithAllowAmbiguousFramingEnabled() call, so enabling it can never be a side effect of selecting a profile. Inheritance object ProxyPolicyModes Inherited Members object.Equals(object) object.Equals(object, object) object.GetHashCode() object.GetType() object.ReferenceEquals(object, object) object.ToString() Namespace: Titanium.Web.Proxy.Options Assembly: Titanium.Web.Proxy.dll Syntax public sealed class ProxyPolicyModes Properties | Edit this page View Source AllEnforce Every family enforced - today's shipped behavior, and the starting point every profile builds from. Declaration public static ProxyPolicyModes AllEnforce { get; } Property Value Type Description ProxyPolicyModes | Edit this page View Source AllowAmbiguousFraming Off by default and absent from every profile - see the type-level remarks. Never true unless WithAllowAmbiguousFramingEnabled() was called explicitly. Declaration public bool AllowAmbiguousFraming { get; } Property Value Type Description bool | Edit this page View Source this[PolicyFamily] Returns the mode selected for family. Declaration public PolicyMode this[PolicyFamily family] { get; } Parameters Type Name Description PolicyFamily family Property Value Type Description PolicyMode Methods | Edit this page View Source Create(PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode) Builds a snapshot with an explicit mode for the first seven families. WebSocketFrameBudget starts at Enforce (use With(PolicyFamily, PolicyMode) to change it) so this shipped signature stays binary-compatible. AllowAmbiguousFraming starts false. Declaration public static ProxyPolicyModes Create(PolicyMode bodyBudget, PolicyMode decompressionRatio, PolicyMode headerLimits, PolicyMode admissionControl, PolicyMode http2AbuseBudget, PolicyMode http2RelayValidation = PolicyMode.Disabled, PolicyMode http1ReplaySafety = PolicyMode.Observe) Parameters Type Name Description PolicyMode bodyBudget PolicyMode decompressionRatio PolicyMode headerLimits PolicyMode admissionControl PolicyMode http2AbuseBudget PolicyMode http2RelayValidation PolicyMode http1ReplaySafety Returns Type Description ProxyPolicyModes | Edit this page View Source With(PolicyFamily, PolicyMode) Returns a snapshot identical to this one but with mode for family. Declaration public ProxyPolicyModes With(PolicyFamily family, PolicyMode mode) Parameters Type Name Description PolicyFamily family PolicyMode mode Returns Type Description ProxyPolicyModes | Edit this page View Source WithAllObservedExceptDisabled() Returns a snapshot identical to this one but with every family dropped to Observe, except families already Disabled (which stay disabled - Observe would silently turn a family back on). This is the \"drop to Observe without redeploying\" runtime switch the plan's rollout section requires. Declaration public ProxyPolicyModes WithAllObservedExceptDisabled() Returns Type Description ProxyPolicyModes | Edit this page View Source WithAllowAmbiguousFramingEnabled() The single, explicit, isolated act of relaying ambiguous HTTP/1 framing instead of rejecting it. See the type-level remarks; never call this as a side effect of applying a profile. Declaration public ProxyPolicyModes WithAllowAmbiguousFramingEnabled() Returns Type Description ProxyPolicyModes"
+ "summary": "Class ProxyPolicyModes Immutable snapshot of the PolicyMode selected for each PolicyFamily, plus the one deliberately-separate AllowAmbiguousFraming escape hatch. Read live by enforcement call sites through PolicyModes, which the plan's rollout section requires to be a runtime switch: replacing the whole snapshot (see PolicyModes's setter) rather than mutating a field lets an operator drop every family to Observe without redeploying, while every in-flight request that already read the previous snapshot keeps behaving consistently with whichever snapshot it observed. AllowAmbiguousFraming is not a PolicyFamily member and has no corresponding PolicyMode: framing, chunk parsing and Content-Length/Transfer-Encoding resolution have no safe \"detect but let it through\" middle ground, so this is a single named, binary, off-by-default flag that relays malformed framing instead of rejecting it - useful only for security research that needs to observe how a client or origin reacts to smuggling-shaped input through the proxy. No profile sets it: Create(PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode) never accepts it as a parameter, and the only way to turn it on is the explicit WithAllowAmbiguousFramingEnabled() call, so enabling it can never be a side effect of selecting a profile. Inheritance object ProxyPolicyModes Inherited Members object.Equals(object) object.Equals(object, object) object.GetHashCode() object.GetType() object.ReferenceEquals(object, object) object.ToString() Namespace: Titanium.Web.Proxy.Options Assembly: Titanium.Web.Proxy.dll Syntax public sealed class ProxyPolicyModes Properties | Edit this page View Source AllEnforce Every family enforced - today's shipped behavior, and the starting point every profile builds from. Declaration public static ProxyPolicyModes AllEnforce { get; } Property Value Type Description ProxyPolicyModes | Edit this page View Source AllowAmbiguousFraming Off by default and absent from every profile - see the type-level remarks. Never true unless WithAllowAmbiguousFramingEnabled() was called explicitly. Declaration public bool AllowAmbiguousFraming { get; } Property Value Type Description bool | Edit this page View Source this[PolicyFamily] Returns the mode selected for family. Declaration public PolicyMode this[PolicyFamily family] { get; } Parameters Type Name Description PolicyFamily family Property Value Type Description PolicyMode Methods | Edit this page View Source Create(PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode, PolicyMode) Builds a snapshot with an explicit mode for the first seven families. WebSocketFrameBudget starts at Enforce (use With(PolicyFamily, PolicyMode) to change it) so this shipped signature stays binary-compatible. AllowAmbiguousFraming starts false. Declaration public static ProxyPolicyModes Create(PolicyMode bodyBudget, PolicyMode decompressionRatio, PolicyMode headerLimits, PolicyMode admissionControl, PolicyMode http2AbuseBudget, PolicyMode http2RelayValidation = PolicyMode.Disabled, PolicyMode http1ReplaySafety = PolicyMode.Observe) Parameters Type Name Description PolicyMode bodyBudget PolicyMode decompressionRatio PolicyMode headerLimits PolicyMode admissionControl PolicyMode http2AbuseBudget PolicyMode http2RelayValidation PolicyMode http1ReplaySafety Returns Type Description ProxyPolicyModes | Edit this page View Source With(PolicyFamily, PolicyMode) Returns a snapshot identical to this one but with mode for family. Declaration public ProxyPolicyModes With(PolicyFamily family, PolicyMode mode) Parameters Type Name Description PolicyFamily family PolicyMode mode Returns Type Description ProxyPolicyModes | Edit this page View Source WithAllObservedExceptDisabled() Returns a snapshot identical to this one but with every family dropped to Observe, except families already Disabled (which stay disabled - Observe would silently turn a family back on). This is the \"drop to Observe without redeploying\" runtime switch the plan's rollout section requires. Declaration public ProxyPolicyModes WithAllObservedExceptDisabled() Returns Type Description ProxyPolicyModes | Edit this page View Source WithAllowAmbiguousFraming(bool) Returns a snapshot identical to this one with AllowAmbiguousFraming set explicitly. A partial config overlay uses this so an omitted flag does not clear a value set by the profile. Declaration public ProxyPolicyModes WithAllowAmbiguousFraming(bool enabled) Parameters Type Name Description bool enabled Returns Type Description ProxyPolicyModes | Edit this page View Source WithAllowAmbiguousFramingEnabled() The single, explicit, isolated act of relaying ambiguous HTTP/1 framing instead of rejecting it. See the type-level remarks; never call this as a side effect of applying a profile. Declaration public ProxyPolicyModes WithAllowAmbiguousFramingEnabled() Returns Type Description ProxyPolicyModes"
},
"api/Titanium.Web.Proxy.Options.ProxyProfile.html": {
"href": "api/Titanium.Web.Proxy.Options.ProxyProfile.html",
@@ -532,7 +537,7 @@
"api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html": {
"href": "api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html",
"title": "Class ProxyResourceLimits | Titanium Web Proxy",
- "summary": "Class ProxyResourceLimits Immutable, validated snapshot of the resource bounds a peer can make the proxy allocate: header shape, body/decompression budgets, concurrency and abuse-rate ceilings, and pool / certificate-cache sizing. Constructed only through Create(long, int, long, long?, long?, double?, int?, int, int?, int, TimeSpan, bool, int, int?), which validates every field up front, so an invalid limit is a construction-time exception rather than a runtime surprise discovered mid-connection. A limit that can legitimately be turned off is typed as nullable with null meaning \"disabled\" - an explicit state - rather than overloading 0 or a negative number to mean the same thing. Limits that must always be enforced because disabling them would leave the proxy itself exploitable (the concurrent-stream cap, the open-header-block frame bound) are non-nullable and always validated to be strictly positive. This type has no back-reference to ProxyServer and no mutable state after construction: it is meant to be handed down to subsystems by value, not looked up through a service locator or an ambient static, so the dependency graph among consumers stays acyclic per the plan's \"Constraints on the policy layer\" section. Inheritance object ProxyResourceLimits Inherited Members object.Equals(object) object.Equals(object, object) object.GetHashCode() object.GetType() object.ReferenceEquals(object, object) object.ToString() Namespace: Titanium.Web.Proxy.Options Assembly: Titanium.Web.Proxy.dll Syntax public sealed class ProxyResourceLimits Properties | Edit this page View Source ConnectionPoolingEnabled Whether upstream TCP connection pooling is enabled. Disabling pooling is an explicit choice represented by this flag, not by giving MaxCachedConnectionsPerHost a sentinel value that also has to be validated as \"not zero, not negative, unless it means disabled\". Declaration public bool ConnectionPoolingEnabled { get; } Property Value Type Description bool | Edit this page View Source Default Today's shipped values, carried forward as the Balanced profile's starting point. Header, decompression, CONTINUATION, and reset budgets stay high enough that ordinary browser traffic does not trip them. MaxConcurrentStreamsPerConnection is 1000, matching Chrome's own concurrent-stream setting: 256 reset later requests on a single x.com connection (REFUSED_STREAM) while the same page succeeded with no proxy. The check runs only when a stream is opened, so traffic under the old cap takes the same DATA path. PublicFacing pins the cap back to 256. MaxCertificateCacheEntries is the one deliberate exception to \"unchanged for existing traffic\": measurement showed process memory holding steady at ~100 MB above baseline after closing every browser tab and idling for minutes, tracking the number of distinct MITM'd hosts rather than any live connection or session count. Unbounded in-memory certificate retention is a defect, not a compatibility guarantee, so the shipped default now bounds it at 1024 entries (roughly 10 MB, comfortably above real single-session browsing) rather than leaving it unbounded like every other nullable limit here defaults to. Declaration public static ProxyResourceLimits Default { get; } Property Value Type Description ProxyResourceLimits | Edit this page View Source MaxCachedConnectionsPerHost Maximum pooled connections cached per remote host. Only meaningful when ConnectionPoolingEnabled is true; always validated as strictly positive regardless, so a future caller cannot re-introduce the \"0 spins forever holding the pool lock\" defect by flipping the flag without also fixing this value. Declaration public int MaxCachedConnectionsPerHost { get; } Property Value Type Description int | Edit this page View Source MaxCertificateCacheEntries Maximum number of generated leaf certificates held in the in-memory certificate cache. Each entry holds a full X509Certificate2 with its private key, so unlike most other limits in this type this one defends against unbounded memory growth from ordinary browsing (many distinct MITM'd hosts), not just against an adversarial peer. null disables the bound and is not the shipped default - see Default. Declaration public int? MaxCertificateCacheEntries { get; } Property Value Type Description int? | Edit this page View Source MaxCertificateDiskCacheEntries Maximum number of generated leaf certificate files retained in the on-disk cache (Certificates.CertificateManager.SaveFakeCertificates), pruned independently of MaxCertificateCacheEntries. Disk is far cheaper than the in-memory cache's live X509Certificate2 handles, and a warm disk cache avoids repeating expensive certificate generation across process restarts, so this bound is deliberately independent and typically much larger (or unbounded). null disables the bound. Declaration public int? MaxCertificateDiskCacheEntries { get; } Property Value Type Description int? | Edit this page View Source MaxConcurrentClients Maximum number of concurrently admitted client connections, checked by the admission gate at handler entry/exit rather than the delayed ClientConnectionCount. null disables global admission control. Declaration public int? MaxConcurrentClients { get; } Property Value Type Description int? | Edit this page View Source MaxConcurrentStreamsPerConnection Proxy-owned cap on concurrent HTTP/2 streams per connection. Always enforced: this is the single source of truth consolidating what were previously two independent mechanisms, and is also the value advertised to the origin in the relayed SETTINGS frame so the advertised and enforced values never disagree. Declaration public int MaxConcurrentStreamsPerConnection { get; } Property Value Type Description int | Edit this page View Source MaxDecodedBodyBytes Maximum cumulative decompressed body bytes produced for a single request or response. null disables the budget. Always checked alongside MaxDecompressionRatio, since a ratio alone cannot bound total memory for a small compressed input that expands enormously without also capping the output side. Declaration public long? MaxDecodedBodyBytes { get; } Property Value Type Description long? | Edit this page View Source MaxDecompressionRatio Maximum allowed ratio of decompressed to compressed bytes. null disables the ratio check (relying on MaxDecodedBodyBytes alone). Declaration public double? MaxDecompressionRatio { get; } Property Value Type Description double? | Edit this page View Source MaxEncodedBodyBytes Maximum cumulative compressed/on-wire body bytes read for a single request or response. null disables the budget. Declaration public long? MaxEncodedBodyBytes { get; } Property Value Type Description long? | Edit this page View Source MaxHeaderAggregateBytes Maximum aggregate size of all header fields in one request or response, in bytes. Declaration public long MaxHeaderAggregateBytes { get; } Property Value Type Description long | Edit this page View Source MaxHeaderCount Maximum number of header fields accepted in one request or response. Declaration public int MaxHeaderCount { get; } Property Value Type Description int | Edit this page View Source MaxHeaderLineBytes Maximum length of a single header line (request/status line or one header field), in bytes. Declaration public long MaxHeaderLineBytes { get; } Property Value Type Description long | Edit this page View Source MaxOpenHeaderBlockDuration Maximum wall-clock duration an HTTP/2 header block may stay open (from the initial HEADERS/PUSH_PROMISE frame sent without END_HEADERS through its terminating CONTINUATION), independent of MaxOpenHeaderBlockFrames. Bounds a slow CONTINUATION-trickle variant that stays under the frame-count cap by pacing itself, which a frame-count-only bound cannot catch on its own. Always enforced. Declaration public TimeSpan MaxOpenHeaderBlockDuration { get; } Property Value Type Description TimeSpan | Edit this page View Source MaxOpenHeaderBlockFrames Maximum number of CONTINUATION frames tolerated for a single open HTTP/2 header block, independent of the existing byte cap. Always enforced: zero-length CONTINUATION frames never trip a byte-based check, and only one header block may be open per connection direction, so an unbounded frame count also head-of-line blocks every other stream. Declaration public int MaxOpenHeaderBlockFrames { get; } Property Value Type Description int | Edit this page View Source MaxOriginHttp2ConnectionsPerAuthority Maximum number of concurrent origin HTTP/2 connections the proxy may open for one authority (host+port+scheme) when multiplexing streams from a single client connection (SocketsHttpHandler EnableMultipleHttp2Connections analog). Always at least 1. Declaration public int MaxOriginHttp2ConnectionsPerAuthority { get; } Property Value Type Description int | Edit this page View Source MaxPeerInitiatedIncompleteStreamResets Maximum number of peer-initiated resets of streams that never completed, per connection, before the proxy tears the connection down. Proxy-initiated resets (e.g. in response to a client cancellation) do not count. null disables the reset budget. Declaration public int? MaxPeerInitiatedIncompleteStreamResets { get; } Property Value Type Description int? Methods | Edit this page View Source Create(long, int, long, long?, long?, double?, int?, int, int?, int, TimeSpan, bool, int, int?) Validates and constructs a ProxyResourceLimits snapshot. Every non-nullable bound must be strictly positive; every nullable bound, if supplied, must also be strictly positive (use null to disable rather than a sentinel number). Declaration public static ProxyResourceLimits Create(long maxHeaderLineBytes, int maxHeaderCount, long maxHeaderAggregateBytes, long? maxEncodedBodyBytes, long? maxDecodedBodyBytes, double? maxDecompressionRatio, int? maxConcurrentClients, int maxConcurrentStreamsPerConnection, int? maxPeerInitiatedIncompleteStreamResets, int maxOpenHeaderBlockFrames, TimeSpan maxOpenHeaderBlockDuration, bool connectionPoolingEnabled, int maxCachedConnectionsPerHost, int? maxCertificateCacheEntries) Parameters Type Name Description long maxHeaderLineBytes int maxHeaderCount long maxHeaderAggregateBytes long? maxEncodedBodyBytes long? maxDecodedBodyBytes double? maxDecompressionRatio int? maxConcurrentClients int maxConcurrentStreamsPerConnection int? maxPeerInitiatedIncompleteStreamResets int maxOpenHeaderBlockFrames TimeSpan maxOpenHeaderBlockDuration bool connectionPoolingEnabled int maxCachedConnectionsPerHost int? maxCertificateCacheEntries Returns Type Description ProxyResourceLimits Exceptions Type Condition ArgumentOutOfRangeException A supplied value is zero or negative. | Edit this page View Source WithCertificateCacheBounds(int?, int?) Returns a copy of this instance with MaxCertificateCacheEntries and MaxCertificateDiskCacheEntries replaced, leaving every other limit unchanged. Added instead of extending Create(long, int, long, long?, long?, double?, int?, int, int?, int, TimeSpan, bool, int, int?) - which is public API already shipped with a fixed parameter list - so that adding the independent disk-cache bound could not be a breaking change for existing callers. Declaration public ProxyResourceLimits WithCertificateCacheBounds(int? maxCertificateCacheEntries, int? maxCertificateDiskCacheEntries) Parameters Type Name Description int? maxCertificateCacheEntries See MaxCertificateCacheEntries. null disables the bound. int? maxCertificateDiskCacheEntries See MaxCertificateDiskCacheEntries. null disables the bound. Returns Type Description ProxyResourceLimits Exceptions Type Condition ArgumentOutOfRangeException A supplied value is zero or negative. | Edit this page View Source WithMaxConcurrentStreamsPerConnection(int) Returns a copy with MaxConcurrentStreamsPerConnection replaced. Declaration public ProxyResourceLimits WithMaxConcurrentStreamsPerConnection(int maxConcurrentStreamsPerConnection) Parameters Type Name Description int maxConcurrentStreamsPerConnection Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxOriginHttp2ConnectionsPerAuthority(int) Returns a copy with MaxOriginHttp2ConnectionsPerAuthority replaced. Declaration public ProxyResourceLimits WithMaxOriginHttp2ConnectionsPerAuthority(int maxOriginHttp2ConnectionsPerAuthority) Parameters Type Name Description int maxOriginHttp2ConnectionsPerAuthority Returns Type Description ProxyResourceLimits"
+ "summary": "Class ProxyResourceLimits Immutable, validated snapshot of the resource bounds a peer can make the proxy allocate: header shape, body/decompression budgets, concurrency and abuse-rate ceilings, and pool / certificate-cache sizing. Constructed only through Create(long, int, long, long?, long?, double?, int?, int, int?, int, TimeSpan, bool, int, int?), which validates every field up front, so an invalid limit is a construction-time exception rather than a runtime surprise discovered mid-connection. A limit that can legitimately be turned off is typed as nullable with null meaning \"disabled\" - an explicit state - rather than overloading 0 or a negative number to mean the same thing. Limits that must always be enforced because disabling them would leave the proxy itself exploitable (the concurrent-stream cap, the open-header-block frame bound) are non-nullable and always validated to be strictly positive. This type has no back-reference to ProxyServer and no mutable state after construction: it is meant to be handed down to subsystems by value, not looked up through a service locator or an ambient static, so the dependency graph among consumers stays acyclic per the plan's \"Constraints on the policy layer\" section. Inheritance object ProxyResourceLimits Inherited Members object.Equals(object) object.Equals(object, object) object.GetHashCode() object.GetType() object.ReferenceEquals(object, object) object.ToString() Namespace: Titanium.Web.Proxy.Options Assembly: Titanium.Web.Proxy.dll Syntax public sealed class ProxyResourceLimits Fields | Edit this page View Source MaxDeferredOutboundBytesCeiling Upper bound for MaxDeferredOutboundBytesPerStream. Declaration public const int MaxDeferredOutboundBytesCeiling = 67108864 Field Value Type Description int | Edit this page View Source MaxHttp3FramePayloadCeiling Upper bound for MaxHttp3FramePayloadBytes. Declaration public const long MaxHttp3FramePayloadCeiling = 67108864 Field Value Type Description long Properties | Edit this page View Source ConnectionPoolingEnabled Whether upstream TCP connection pooling is enabled. Disabling pooling is an explicit choice represented by this flag, not by giving MaxCachedConnectionsPerHost a sentinel value that also has to be validated as \"not zero, not negative, unless it means disabled\". Declaration public bool ConnectionPoolingEnabled { get; } Property Value Type Description bool | Edit this page View Source Default Today's shipped values, carried forward as the Balanced profile's starting point. Header, decompression, CONTINUATION, and reset budgets stay high enough that ordinary browser traffic does not trip them. MaxConcurrentStreamsPerConnection is 1000, matching Chrome's own concurrent-stream setting: 256 reset later requests on a single x.com connection (REFUSED_STREAM) while the same page succeeded with no proxy. The check runs only when a stream is opened, so traffic under the old cap takes the same DATA path. PublicFacing pins the cap back to 256. MaxCertificateCacheEntries is the one deliberate exception to \"unchanged for existing traffic\": measurement showed process memory holding steady at ~100 MB above baseline after closing every browser tab and idling for minutes, tracking the number of distinct MITM'd hosts rather than any live connection or session count. Unbounded in-memory certificate retention is a defect, not a compatibility guarantee, so the shipped default now bounds it at 1024 entries (roughly 10 MB, comfortably above real single-session browsing) rather than leaving it unbounded like every other nullable limit here defaults to. Declaration public static ProxyResourceLimits Default { get; } Property Value Type Description ProxyResourceLimits | Edit this page View Source Http2WindowUpdateTimeoutSeconds Seconds to wait for an HTTP/2 WINDOW_UPDATE before failing the stream. Default 60. Declaration public int Http2WindowUpdateTimeoutSeconds { get; } Property Value Type Description int | Edit this page View Source MaxAuthChallengeRounds Maximum Windows/proxy authentication challenge rounds per request. Default 3. Declaration public int MaxAuthChallengeRounds { get; } Property Value Type Description int | Edit this page View Source MaxCachedConnectionsPerHost Maximum pooled connections cached per remote host. Only meaningful when ConnectionPoolingEnabled is true; always validated as strictly positive regardless, so a future caller cannot re-introduce the \"0 spins forever holding the pool lock\" defect by flipping the flag without also fixing this value. Declaration public int MaxCachedConnectionsPerHost { get; } Property Value Type Description int | Edit this page View Source MaxCertificateCacheEntries Maximum number of generated leaf certificates held in the in-memory certificate cache. Each entry holds a full X509Certificate2 with its private key, so unlike most other limits in this type this one defends against unbounded memory growth from ordinary browsing (many distinct MITM'd hosts), not just against an adversarial peer. null disables the bound and is not the shipped default - see Default. Declaration public int? MaxCertificateCacheEntries { get; } Property Value Type Description int? | Edit this page View Source MaxCertificateDiskCacheEntries Maximum number of generated leaf certificate files retained in the on-disk cache (Certificates.CertificateManager.SaveFakeCertificates), pruned independently of MaxCertificateCacheEntries. Disk is far cheaper than the in-memory cache's live X509Certificate2 handles, and a warm disk cache avoids repeating expensive certificate generation across process restarts, so this bound is deliberately independent and typically much larger (or unbounded). null disables the bound. Declaration public int? MaxCertificateDiskCacheEntries { get; } Property Value Type Description int? | Edit this page View Source MaxConcurrentClients Maximum number of concurrently admitted client connections, checked by the admission gate at handler entry/exit rather than the delayed ClientConnectionCount. null disables global admission control. Declaration public int? MaxConcurrentClients { get; } Property Value Type Description int? | Edit this page View Source MaxConcurrentStreamsPerConnection Proxy-owned cap on concurrent HTTP/2 streams per connection. Always enforced: this is the single source of truth consolidating what were previously two independent mechanisms, and is also the value advertised to the origin in the relayed SETTINGS frame so the advertised and enforced values never disagree. Declaration public int MaxConcurrentStreamsPerConnection { get; } Property Value Type Description int | Edit this page View Source MaxDecodedBodyBytes Maximum cumulative decompressed body bytes produced for a single request or response. null disables the budget. Always checked alongside MaxDecompressionRatio, since a ratio alone cannot bound total memory for a small compressed input that expands enormously without also capping the output side. Declaration public long? MaxDecodedBodyBytes { get; } Property Value Type Description long? | Edit this page View Source MaxDecompressionRatio Maximum allowed ratio of decompressed to compressed bytes. null disables the ratio check (relying on MaxDecodedBodyBytes alone). Declaration public double? MaxDecompressionRatio { get; } Property Value Type Description double? | Edit this page View Source MaxDeferredOutboundBytesPerStream Bytes of HTTP/2 DATA that may be parked per stream waiting for send-window credit. Worst-case memory is concurrent streams times this value. Default 4 MiB + 16 KiB. Ceiling 64 MiB. Declaration public int MaxDeferredOutboundBytesPerStream { get; } Property Value Type Description int | Edit this page View Source MaxEncodedBodyBytes Maximum cumulative compressed/on-wire body bytes read for a single request or response. null disables the budget. Declaration public long? MaxEncodedBodyBytes { get; } Property Value Type Description long? | Edit this page View Source MaxHeaderAggregateBytes Maximum aggregate size of all header fields in one request or response, in bytes. Declaration public long MaxHeaderAggregateBytes { get; } Property Value Type Description long | Edit this page View Source MaxHeaderCount Maximum number of header fields accepted in one request or response. Declaration public int MaxHeaderCount { get; } Property Value Type Description int | Edit this page View Source MaxHeaderLineBytes Maximum length of a single header line (request/status line or one header field), in bytes. Declaration public long MaxHeaderLineBytes { get; } Property Value Type Description long | Edit this page View Source MaxHttp2CompressedHeaderBlockBytes Maximum compressed HTTP/2 header block (HEADERS + CONTINUATION) in bytes. Default 256 KiB. Declaration public int MaxHttp2CompressedHeaderBlockBytes { get; } Property Value Type Description int | Edit this page View Source MaxHttp3FramePayloadBytes Maximum HTTP/3 frame payload accepted before H3_EXCESSIVE_LOAD. Default 4 MiB. Ceiling 64 MiB. Declaration public long MaxHttp3FramePayloadBytes { get; } Property Value Type Description long | Edit this page View Source MaxInterimResponses Maximum interim (1xx) responses accepted from one origin before the stream fails. Default 20. Declaration public int MaxInterimResponses { get; } Property Value Type Description int | Edit this page View Source MaxOpenHeaderBlockDuration Maximum wall-clock duration an HTTP/2 header block may stay open (from the initial HEADERS/PUSH_PROMISE frame sent without END_HEADERS through its terminating CONTINUATION), independent of MaxOpenHeaderBlockFrames. Bounds a slow CONTINUATION-trickle variant that stays under the frame-count cap by pacing itself, which a frame-count-only bound cannot catch on its own. Always enforced. Declaration public TimeSpan MaxOpenHeaderBlockDuration { get; } Property Value Type Description TimeSpan | Edit this page View Source MaxOpenHeaderBlockFrames Maximum number of CONTINUATION frames tolerated for a single open HTTP/2 header block, independent of the existing byte cap. Always enforced: zero-length CONTINUATION frames never trip a byte-based check, and only one header block may be open per connection direction, so an unbounded frame count also head-of-line blocks every other stream. Declaration public int MaxOpenHeaderBlockFrames { get; } Property Value Type Description int | Edit this page View Source MaxOriginHttp2ConnectionsPerAuthority Maximum number of concurrent origin HTTP/2 connections the proxy may open for one authority (host+port+scheme) when multiplexing streams from a single client connection (SocketsHttpHandler EnableMultipleHttp2Connections analog). Always at least 1. Declaration public int MaxOriginHttp2ConnectionsPerAuthority { get; } Property Value Type Description int | Edit this page View Source MaxPeerInitiatedIncompleteStreamResets Maximum number of peer-initiated resets of streams that never completed, per connection, before the proxy tears the connection down. Proxy-initiated resets (e.g. in response to a client cancellation) do not count. null disables the reset budget. Declaration public int? MaxPeerInitiatedIncompleteStreamResets { get; } Property Value Type Description int? | Edit this page View Source MaxTrailerHeaderBlockBytes Maximum trailer header block size in bytes. Default 16 KiB. Declaration public int MaxTrailerHeaderBlockBytes { get; } Property Value Type Description int | Edit this page View Source MaxTrailerHeaderCount Maximum trailer header fields on one chunked message. Default 100. Declaration public int MaxTrailerHeaderCount { get; } Property Value Type Description int | Edit this page View Source MaxUpstreamProxyAuthenticationAttempts Maximum CONNECT authentication attempts against an upstream proxy. Default 5. Declaration public int MaxUpstreamProxyAuthenticationAttempts { get; } Property Value Type Description int | Edit this page View Source MaxWinAuthTokenBytes Maximum SSPI token buffer in bytes. Default 12288. Declaration public int MaxWinAuthTokenBytes { get; } Property Value Type Description int Methods | Edit this page View Source Create(long, int, long, long?, long?, double?, int?, int, int?, int, TimeSpan, bool, int, int?) Validates and constructs a ProxyResourceLimits snapshot. Every non-nullable bound must be strictly positive; every nullable bound, if supplied, must also be strictly positive (use null to disable rather than a sentinel number). Declaration public static ProxyResourceLimits Create(long maxHeaderLineBytes, int maxHeaderCount, long maxHeaderAggregateBytes, long? maxEncodedBodyBytes, long? maxDecodedBodyBytes, double? maxDecompressionRatio, int? maxConcurrentClients, int maxConcurrentStreamsPerConnection, int? maxPeerInitiatedIncompleteStreamResets, int maxOpenHeaderBlockFrames, TimeSpan maxOpenHeaderBlockDuration, bool connectionPoolingEnabled, int maxCachedConnectionsPerHost, int? maxCertificateCacheEntries) Parameters Type Name Description long maxHeaderLineBytes int maxHeaderCount long maxHeaderAggregateBytes long? maxEncodedBodyBytes long? maxDecodedBodyBytes double? maxDecompressionRatio int? maxConcurrentClients int maxConcurrentStreamsPerConnection int? maxPeerInitiatedIncompleteStreamResets int maxOpenHeaderBlockFrames TimeSpan maxOpenHeaderBlockDuration bool connectionPoolingEnabled int maxCachedConnectionsPerHost int? maxCertificateCacheEntries Returns Type Description ProxyResourceLimits Exceptions Type Condition ArgumentOutOfRangeException A supplied value is zero or negative. | Edit this page View Source WithAuthenticationBounds(int, int, int) Returns a copy with authentication bounds replaced. Declaration public ProxyResourceLimits WithAuthenticationBounds(int maxAuthChallengeRounds, int maxUpstreamProxyAuthenticationAttempts, int maxWinAuthTokenBytes) Parameters Type Name Description int maxAuthChallengeRounds int maxUpstreamProxyAuthenticationAttempts int maxWinAuthTokenBytes Returns Type Description ProxyResourceLimits | Edit this page View Source WithCertificateCacheBounds(int?, int?) Returns a copy of this instance with MaxCertificateCacheEntries and MaxCertificateDiskCacheEntries replaced, leaving every other limit unchanged. Added instead of extending Create(long, int, long, long?, long?, double?, int?, int, int?, int, TimeSpan, bool, int, int?) - which is public API already shipped with a fixed parameter list - so that adding the independent disk-cache bound could not be a breaking change for existing callers. Declaration public ProxyResourceLimits WithCertificateCacheBounds(int? maxCertificateCacheEntries, int? maxCertificateDiskCacheEntries) Parameters Type Name Description int? maxCertificateCacheEntries See MaxCertificateCacheEntries. null disables the bound. int? maxCertificateDiskCacheEntries See MaxCertificateDiskCacheEntries. null disables the bound. Returns Type Description ProxyResourceLimits Exceptions Type Condition ArgumentOutOfRangeException A supplied value is zero or negative. | Edit this page View Source WithHttp2WindowUpdateTimeoutSeconds(int) Returns a copy with Http2WindowUpdateTimeoutSeconds replaced. Declaration public ProxyResourceLimits WithHttp2WindowUpdateTimeoutSeconds(int http2WindowUpdateTimeoutSeconds) Parameters Type Name Description int http2WindowUpdateTimeoutSeconds Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxConcurrentStreamsPerConnection(int) Returns a copy with MaxConcurrentStreamsPerConnection replaced. Declaration public ProxyResourceLimits WithMaxConcurrentStreamsPerConnection(int maxConcurrentStreamsPerConnection) Parameters Type Name Description int maxConcurrentStreamsPerConnection Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxDeferredOutboundBytesPerStream(int) Returns a copy with MaxDeferredOutboundBytesPerStream replaced. Declaration public ProxyResourceLimits WithMaxDeferredOutboundBytesPerStream(int maxDeferredOutboundBytesPerStream) Parameters Type Name Description int maxDeferredOutboundBytesPerStream Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxHttp2CompressedHeaderBlockBytes(int) Returns a copy with MaxHttp2CompressedHeaderBlockBytes replaced. Declaration public ProxyResourceLimits WithMaxHttp2CompressedHeaderBlockBytes(int maxHttp2CompressedHeaderBlockBytes) Parameters Type Name Description int maxHttp2CompressedHeaderBlockBytes Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxHttp3FramePayloadBytes(long) Returns a copy with MaxHttp3FramePayloadBytes replaced. Declaration public ProxyResourceLimits WithMaxHttp3FramePayloadBytes(long maxHttp3FramePayloadBytes) Parameters Type Name Description long maxHttp3FramePayloadBytes Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxInterimResponses(int) Returns a copy with MaxInterimResponses replaced. Declaration public ProxyResourceLimits WithMaxInterimResponses(int maxInterimResponses) Parameters Type Name Description int maxInterimResponses Returns Type Description ProxyResourceLimits | Edit this page View Source WithMaxOriginHttp2ConnectionsPerAuthority(int) Returns a copy with MaxOriginHttp2ConnectionsPerAuthority replaced. Declaration public ProxyResourceLimits WithMaxOriginHttp2ConnectionsPerAuthority(int maxOriginHttp2ConnectionsPerAuthority) Parameters Type Name Description int maxOriginHttp2ConnectionsPerAuthority Returns Type Description ProxyResourceLimits | Edit this page View Source WithTrailerHeaderBounds(int, int) Returns a copy with trailer header bounds replaced. Declaration public ProxyResourceLimits WithTrailerHeaderBounds(int maxTrailerHeaderCount, int maxTrailerHeaderBlockBytes) Parameters Type Name Description int maxTrailerHeaderCount int maxTrailerHeaderBlockBytes Returns Type Description ProxyResourceLimits"
},
"api/Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html": {
"href": "api/Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html",
diff --git a/docs/xrefmap.yml b/docs/xrefmap.yml
index f6be8c6d7..6c6c17fd4 100644
--- a/docs/xrefmap.yml
+++ b/docs/xrefmap.yml
@@ -3804,6 +3804,83 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Http.HeaderCollection.RemoveHeader
nameWithType: HeaderCollection.RemoveHeader
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException
+ name: HeaderLimitsExceededException
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html
+ commentId: T:Titanium.Web.Proxy.Http.HeaderLimitsExceededException
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException
+ nameWithType: HeaderLimitsExceededException
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.#ctor(System.Boolean,System.Int64,System.Int64,System.Boolean)
+ name: HeaderLimitsExceededException(bool, long, long, bool)
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException__ctor_System_Boolean_System_Int64_System_Int64_System_Boolean_
+ commentId: M:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.#ctor(System.Boolean,System.Int64,System.Int64,System.Boolean)
+ name.vb: New(Boolean, Long, Long, Boolean)
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.HeaderLimitsExceededException(bool, long, long, bool)
+ fullName.vb: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.New(Boolean, Long, Long, Boolean)
+ nameWithType: HeaderLimitsExceededException.HeaderLimitsExceededException(bool, long, long, bool)
+ nameWithType.vb: HeaderLimitsExceededException.New(Boolean, Long, Long, Boolean)
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.#ctor*
+ name: HeaderLimitsExceededException
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException__ctor_
+ commentId: Overload:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.#ctor
+ isSpec: "True"
+ name.vb: New
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.HeaderLimitsExceededException
+ fullName.vb: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.New
+ nameWithType: HeaderLimitsExceededException.HeaderLimitsExceededException
+ nameWithType.vb: HeaderLimitsExceededException.New
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount
+ name: IsCount
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_IsCount
+ commentId: P:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount
+ nameWithType: HeaderLimitsExceededException.IsCount
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount*
+ name: IsCount
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_IsCount_
+ commentId: Overload:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount
+ nameWithType: HeaderLimitsExceededException.IsCount
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest
+ name: IsRequest
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_IsRequest
+ commentId: P:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest
+ nameWithType: HeaderLimitsExceededException.IsRequest
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest*
+ name: IsRequest
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_IsRequest_
+ commentId: Overload:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest
+ nameWithType: HeaderLimitsExceededException.IsRequest
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit
+ name: Limit
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_Limit
+ commentId: P:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit
+ nameWithType: HeaderLimitsExceededException.Limit
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit*
+ name: Limit
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_Limit_
+ commentId: Overload:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit
+ nameWithType: HeaderLimitsExceededException.Limit
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed
+ name: Observed
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_Observed
+ commentId: P:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed
+ nameWithType: HeaderLimitsExceededException.Observed
+- uid: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed*
+ name: Observed
+ href: api/Titanium.Web.Proxy.Http.HeaderLimitsExceededException.html#Titanium_Web_Proxy_Http_HeaderLimitsExceededException_Observed_
+ commentId: Overload:Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed
+ nameWithType: HeaderLimitsExceededException.Observed
- uid: Titanium.Web.Proxy.Http.HttpWebClient
name: HttpWebClient
href: api/Titanium.Web.Proxy.Http.HttpWebClient.html
@@ -8222,6 +8299,22 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllObservedExceptDisabled
nameWithType: ProxyPolicyModes.WithAllObservedExceptDisabled
+- uid: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming(System.Boolean)
+ name: WithAllowAmbiguousFraming(bool)
+ href: api/Titanium.Web.Proxy.Options.ProxyPolicyModes.html#Titanium_Web_Proxy_Options_ProxyPolicyModes_WithAllowAmbiguousFraming_System_Boolean_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming(System.Boolean)
+ name.vb: WithAllowAmbiguousFraming(Boolean)
+ fullName: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming(bool)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming(Boolean)
+ nameWithType: ProxyPolicyModes.WithAllowAmbiguousFraming(bool)
+ nameWithType.vb: ProxyPolicyModes.WithAllowAmbiguousFraming(Boolean)
+- uid: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming*
+ name: WithAllowAmbiguousFraming
+ href: api/Titanium.Web.Proxy.Options.ProxyPolicyModes.html#Titanium_Web_Proxy_Options_ProxyPolicyModes_WithAllowAmbiguousFraming_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming
+ nameWithType: ProxyPolicyModes.WithAllowAmbiguousFraming
- uid: Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFramingEnabled
name: WithAllowAmbiguousFramingEnabled()
href: api/Titanium.Web.Proxy.Options.ProxyPolicyModes.html#Titanium_Web_Proxy_Options_ProxyPolicyModes_WithAllowAmbiguousFramingEnabled
@@ -8495,6 +8588,32 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.Default
nameWithType: ProxyResourceLimits.Default
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+ name: Http2WindowUpdateTimeoutSeconds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_Http2WindowUpdateTimeoutSeconds
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+ nameWithType: ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds*
+ name: Http2WindowUpdateTimeoutSeconds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_Http2WindowUpdateTimeoutSeconds_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+ nameWithType: ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds
+ name: MaxAuthChallengeRounds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxAuthChallengeRounds
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds
+ nameWithType: ProxyResourceLimits.MaxAuthChallengeRounds
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds*
+ name: MaxAuthChallengeRounds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxAuthChallengeRounds_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds
+ nameWithType: ProxyResourceLimits.MaxAuthChallengeRounds
- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxCachedConnectionsPerHost
name: MaxCachedConnectionsPerHost
href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxCachedConnectionsPerHost
@@ -8586,6 +8705,25 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDecompressionRatio
nameWithType: ProxyResourceLimits.MaxDecompressionRatio
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesCeiling
+ name: MaxDeferredOutboundBytesCeiling
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxDeferredOutboundBytesCeiling
+ commentId: F:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesCeiling
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesCeiling
+ nameWithType: ProxyResourceLimits.MaxDeferredOutboundBytesCeiling
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
+ name: MaxDeferredOutboundBytesPerStream
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxDeferredOutboundBytesPerStream
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
+ nameWithType: ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream*
+ name: MaxDeferredOutboundBytesPerStream
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxDeferredOutboundBytesPerStream_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
+ nameWithType: ProxyResourceLimits.MaxDeferredOutboundBytesPerStream
- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxEncodedBodyBytes
name: MaxEncodedBodyBytes
href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxEncodedBodyBytes
@@ -8638,6 +8776,51 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHeaderLineBytes
nameWithType: ProxyResourceLimits.MaxHeaderLineBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+ name: MaxHttp2CompressedHeaderBlockBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxHttp2CompressedHeaderBlockBytes
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+ nameWithType: ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes*
+ name: MaxHttp2CompressedHeaderBlockBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxHttp2CompressedHeaderBlockBytes_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+ nameWithType: ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes
+ name: MaxHttp3FramePayloadBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxHttp3FramePayloadBytes
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes
+ nameWithType: ProxyResourceLimits.MaxHttp3FramePayloadBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes*
+ name: MaxHttp3FramePayloadBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxHttp3FramePayloadBytes_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes
+ nameWithType: ProxyResourceLimits.MaxHttp3FramePayloadBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadCeiling
+ name: MaxHttp3FramePayloadCeiling
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxHttp3FramePayloadCeiling
+ commentId: F:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadCeiling
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadCeiling
+ nameWithType: ProxyResourceLimits.MaxHttp3FramePayloadCeiling
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses
+ name: MaxInterimResponses
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxInterimResponses
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses
+ nameWithType: ProxyResourceLimits.MaxInterimResponses
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses*
+ name: MaxInterimResponses
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxInterimResponses_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses
+ nameWithType: ProxyResourceLimits.MaxInterimResponses
- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxOpenHeaderBlockDuration
name: MaxOpenHeaderBlockDuration
href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxOpenHeaderBlockDuration
@@ -8690,6 +8873,74 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxPeerInitiatedIncompleteStreamResets
nameWithType: ProxyResourceLimits.MaxPeerInitiatedIncompleteStreamResets
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+ name: MaxTrailerHeaderBlockBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxTrailerHeaderBlockBytes
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+ nameWithType: ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes*
+ name: MaxTrailerHeaderBlockBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxTrailerHeaderBlockBytes_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+ nameWithType: ProxyResourceLimits.MaxTrailerHeaderBlockBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount
+ name: MaxTrailerHeaderCount
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxTrailerHeaderCount
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount
+ nameWithType: ProxyResourceLimits.MaxTrailerHeaderCount
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount*
+ name: MaxTrailerHeaderCount
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxTrailerHeaderCount_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount
+ nameWithType: ProxyResourceLimits.MaxTrailerHeaderCount
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+ name: MaxUpstreamProxyAuthenticationAttempts
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxUpstreamProxyAuthenticationAttempts
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+ nameWithType: ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts*
+ name: MaxUpstreamProxyAuthenticationAttempts
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxUpstreamProxyAuthenticationAttempts_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+ nameWithType: ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes
+ name: MaxWinAuthTokenBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxWinAuthTokenBytes
+ commentId: P:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes
+ nameWithType: ProxyResourceLimits.MaxWinAuthTokenBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes*
+ name: MaxWinAuthTokenBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_MaxWinAuthTokenBytes_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes
+ nameWithType: ProxyResourceLimits.MaxWinAuthTokenBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds(System.Int32,System.Int32,System.Int32)
+ name: WithAuthenticationBounds(int, int, int)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithAuthenticationBounds_System_Int32_System_Int32_System_Int32_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds(System.Int32,System.Int32,System.Int32)
+ name.vb: WithAuthenticationBounds(Integer, Integer, Integer)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds(int, int, int)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds(Integer, Integer, Integer)
+ nameWithType: ProxyResourceLimits.WithAuthenticationBounds(int, int, int)
+ nameWithType.vb: ProxyResourceLimits.WithAuthenticationBounds(Integer, Integer, Integer)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds*
+ name: WithAuthenticationBounds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithAuthenticationBounds_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds
+ nameWithType: ProxyResourceLimits.WithAuthenticationBounds
- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithCertificateCacheBounds(System.Nullable{System.Int32},System.Nullable{System.Int32})
name: WithCertificateCacheBounds(int?, int?)
href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithCertificateCacheBounds_System_Nullable_System_Int32__System_Nullable_System_Int32__
@@ -8706,6 +8957,22 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithCertificateCacheBounds
nameWithType: ProxyResourceLimits.WithCertificateCacheBounds
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(System.Int32)
+ name: WithHttp2WindowUpdateTimeoutSeconds(int)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithHttp2WindowUpdateTimeoutSeconds_System_Int32_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(System.Int32)
+ name.vb: WithHttp2WindowUpdateTimeoutSeconds(Integer)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(int)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(Integer)
+ nameWithType: ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(int)
+ nameWithType.vb: ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(Integer)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds*
+ name: WithHttp2WindowUpdateTimeoutSeconds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithHttp2WindowUpdateTimeoutSeconds_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds
+ nameWithType: ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds
- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxConcurrentStreamsPerConnection(System.Int32)
name: WithMaxConcurrentStreamsPerConnection(int)
href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxConcurrentStreamsPerConnection_System_Int32_
@@ -8722,6 +8989,70 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxConcurrentStreamsPerConnection
nameWithType: ProxyResourceLimits.WithMaxConcurrentStreamsPerConnection
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(System.Int32)
+ name: WithMaxDeferredOutboundBytesPerStream(int)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxDeferredOutboundBytesPerStream_System_Int32_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(System.Int32)
+ name.vb: WithMaxDeferredOutboundBytesPerStream(Integer)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(int)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(Integer)
+ nameWithType: ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(int)
+ nameWithType.vb: ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(Integer)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream*
+ name: WithMaxDeferredOutboundBytesPerStream
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxDeferredOutboundBytesPerStream_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream
+ nameWithType: ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(System.Int32)
+ name: WithMaxHttp2CompressedHeaderBlockBytes(int)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxHttp2CompressedHeaderBlockBytes_System_Int32_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(System.Int32)
+ name.vb: WithMaxHttp2CompressedHeaderBlockBytes(Integer)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(int)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(Integer)
+ nameWithType: ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(int)
+ nameWithType.vb: ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(Integer)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes*
+ name: WithMaxHttp2CompressedHeaderBlockBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxHttp2CompressedHeaderBlockBytes_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes
+ nameWithType: ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(System.Int64)
+ name: WithMaxHttp3FramePayloadBytes(long)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxHttp3FramePayloadBytes_System_Int64_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(System.Int64)
+ name.vb: WithMaxHttp3FramePayloadBytes(Long)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(long)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(Long)
+ nameWithType: ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(long)
+ nameWithType.vb: ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(Long)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes*
+ name: WithMaxHttp3FramePayloadBytes
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxHttp3FramePayloadBytes_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes
+ nameWithType: ProxyResourceLimits.WithMaxHttp3FramePayloadBytes
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses(System.Int32)
+ name: WithMaxInterimResponses(int)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxInterimResponses_System_Int32_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses(System.Int32)
+ name.vb: WithMaxInterimResponses(Integer)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses(int)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses(Integer)
+ nameWithType: ProxyResourceLimits.WithMaxInterimResponses(int)
+ nameWithType.vb: ProxyResourceLimits.WithMaxInterimResponses(Integer)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses*
+ name: WithMaxInterimResponses
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxInterimResponses_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses
+ nameWithType: ProxyResourceLimits.WithMaxInterimResponses
- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxOriginHttp2ConnectionsPerAuthority(System.Int32)
name: WithMaxOriginHttp2ConnectionsPerAuthority(int)
href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithMaxOriginHttp2ConnectionsPerAuthority_System_Int32_
@@ -8738,6 +9069,22 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxOriginHttp2ConnectionsPerAuthority
nameWithType: ProxyResourceLimits.WithMaxOriginHttp2ConnectionsPerAuthority
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds(System.Int32,System.Int32)
+ name: WithTrailerHeaderBounds(int, int)
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithTrailerHeaderBounds_System_Int32_System_Int32_
+ commentId: M:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds(System.Int32,System.Int32)
+ name.vb: WithTrailerHeaderBounds(Integer, Integer)
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds(int, int)
+ fullName.vb: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds(Integer, Integer)
+ nameWithType: ProxyResourceLimits.WithTrailerHeaderBounds(int, int)
+ nameWithType.vb: ProxyResourceLimits.WithTrailerHeaderBounds(Integer, Integer)
+- uid: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds*
+ name: WithTrailerHeaderBounds
+ href: api/Titanium.Web.Proxy.Options.ProxyResourceLimits.html#Titanium_Web_Proxy_Options_ProxyResourceLimits_WithTrailerHeaderBounds_
+ commentId: Overload:Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds
+ nameWithType: ProxyResourceLimits.WithTrailerHeaderBounds
- uid: Titanium.Web.Proxy.Options.ProxyTimeoutOptions
name: ProxyTimeoutOptions
href: api/Titanium.Web.Proxy.Options.ProxyTimeoutOptions.html
diff --git a/src/Titanium.Cli/Config/RunCommand.cs b/src/Titanium.Cli/Config/RunCommand.cs
index ad1ab5e20..57fa29ed1 100644
--- a/src/Titanium.Cli/Config/RunCommand.cs
+++ b/src/Titanium.Cli/Config/RunCommand.cs
@@ -74,6 +74,8 @@ internal static async Task ExecuteCoreAsync( // NOSONAR S3776 -- CLI run li
return 1;
}
+ WriteConfigWarnings(loaded);
+
// When launched as a service, resolve relative paths against the config directory
// (SCM / systemd / launchd cwd is typically System32 or /).
var configDir = Path.GetDirectoryName(Path.GetFullPath(configPath));
@@ -292,6 +294,8 @@ internal static async Task ReloadConfigAsync( // NOSONAR S107 -- Reload keeps es
throw new InvalidOperationException(string.Join("; ", errors));
}
+ WriteConfigWarnings(loaded);
+
ServerConfigApplier.Apply(proxy, loaded.Config.Server);
if (loaded.Config.Clusters.Count > 0)
{
@@ -1041,4 +1045,12 @@ config.Plus.Options is not null &&
return config.Routes.Any(r => r.Transforms is { Count: > 0 });
}
+
+ private static void WriteConfigWarnings(LoadedConfig loaded)
+ {
+ foreach (var warning in loaded.UnknownKeys)
+ AsyncConsole.WriteError("warning: " + warning);
+ foreach (var warning in TwpConfigValidator.CollectWarnings(loaded.Config))
+ AsyncConsole.WriteError("warning: " + warning);
+ }
}
diff --git a/src/Titanium.Cli/Config/ServerConfigApplier.cs b/src/Titanium.Cli/Config/ServerConfigApplier.cs
index 599840515..2e229a420 100644
--- a/src/Titanium.Cli/Config/ServerConfigApplier.cs
+++ b/src/Titanium.Cli/Config/ServerConfigApplier.cs
@@ -38,6 +38,21 @@ public static void Apply(ProxyServer proxy, ServerConfig? server)
ApplyUpstream(proxy, server.Upstream);
ApplyCertificateManager(proxy, server.CertificateManager);
ApplyDecryptExclusions(proxy, server);
+ ApplyDecryptFailureBypass(proxy, server);
+ if (!string.IsNullOrWhiteSpace(server.ProxyAuthenticationRealm))
+ proxy.ProxyAuthenticationRealm = server.ProxyAuthenticationRealm;
+ }
+
+ private static void ApplyDecryptFailureBypass(ProxyServer proxy, ServerConfig server)
+ {
+ if (server.EnableDecryptFailureBypass is bool enabled)
+ proxy.EnableDecryptFailureBypass = enabled;
+ if (server.DecryptFailureBypassTtlMinutes is int minutes && minutes > 0)
+ proxy.DecryptFailureBypassTtl = TimeSpan.FromMinutes(minutes);
+ if (server.DecryptFailureBypassMaxEntries is int entries && entries > 0)
+ proxy.DecryptFailureBypassMaxEntries = entries;
+ if (server.DecryptFailureBypassThreshold is int threshold && threshold > 0)
+ proxy.DecryptFailureBypassThreshold = threshold;
}
///
@@ -312,6 +327,22 @@ private static void ApplyLimits(ProxyServer proxy, LimitsConfig? limits)
built = built.WithCertificateCacheBounds(
built.MaxCertificateCacheEntries,
limits.MaxCertificateDiskCacheEntries ?? current.MaxCertificateDiskCacheEntries);
+ built = built.WithMaxHttp3FramePayloadBytes(
+ limits.MaxHttp3FramePayloadBytes ?? current.MaxHttp3FramePayloadBytes);
+ built = built.WithMaxDeferredOutboundBytesPerStream(
+ limits.MaxDeferredOutboundBytesPerStream ?? current.MaxDeferredOutboundBytesPerStream);
+ built = built.WithTrailerHeaderBounds(
+ limits.MaxTrailerHeaderCount ?? current.MaxTrailerHeaderCount,
+ limits.MaxTrailerHeaderBlockBytes ?? current.MaxTrailerHeaderBlockBytes);
+ built = built.WithMaxHttp2CompressedHeaderBlockBytes(
+ limits.MaxHttp2CompressedHeaderBlockBytes ?? current.MaxHttp2CompressedHeaderBlockBytes);
+ built = built.WithMaxInterimResponses(limits.MaxInterimResponses ?? current.MaxInterimResponses);
+ built = built.WithAuthenticationBounds(
+ limits.MaxAuthChallengeRounds ?? current.MaxAuthChallengeRounds,
+ limits.MaxUpstreamProxyAuthenticationAttempts ?? current.MaxUpstreamProxyAuthenticationAttempts,
+ limits.MaxWinAuthTokenBytes ?? current.MaxWinAuthTokenBytes);
+ built = built.WithHttp2WindowUpdateTimeoutSeconds(
+ limits.Http2WindowUpdateTimeoutSeconds ?? current.Http2WindowUpdateTimeoutSeconds);
proxy.ResourceLimits = built;
@@ -338,22 +369,25 @@ private static void ApplyPolicyModes(ProxyServer proxy, PolicyModesConfig? polic
return;
}
- var current = proxy.PolicyModes;
- var modes = ProxyPolicyModes.Create(
- ParsePolicyMode(policy.BodyBudget, current[PolicyFamily.BodyBudget]),
- ParsePolicyMode(policy.DecompressionRatio, current[PolicyFamily.DecompressionRatio]),
- ParsePolicyMode(policy.HeaderLimits, current[PolicyFamily.HeaderLimits]),
- ParsePolicyMode(policy.AdmissionControl, current[PolicyFamily.AdmissionControl]),
- ParsePolicyMode(policy.Http2AbuseBudget, current[PolicyFamily.Http2AbuseBudget]),
- ParsePolicyMode(policy.Http2RelayValidation, current[PolicyFamily.Http2RelayValidation]))
- .With(
- PolicyFamily.WebSocketFrameBudget,
- ParsePolicyMode(policy.WebSocketFrameBudget, current[PolicyFamily.WebSocketFrameBudget]));
-
- if (policy.AllowAmbiguousFraming == true)
- {
- modes = modes.WithAllowAmbiguousFramingEnabled();
- }
+ var modes = proxy.PolicyModes;
+ if (!string.IsNullOrWhiteSpace(policy.BodyBudget))
+ modes = modes.With(PolicyFamily.BodyBudget, ParsePolicyMode(policy.BodyBudget, modes[PolicyFamily.BodyBudget]));
+ if (!string.IsNullOrWhiteSpace(policy.DecompressionRatio))
+ modes = modes.With(PolicyFamily.DecompressionRatio, ParsePolicyMode(policy.DecompressionRatio, modes[PolicyFamily.DecompressionRatio]));
+ if (!string.IsNullOrWhiteSpace(policy.HeaderLimits))
+ modes = modes.With(PolicyFamily.HeaderLimits, ParsePolicyMode(policy.HeaderLimits, modes[PolicyFamily.HeaderLimits]));
+ if (!string.IsNullOrWhiteSpace(policy.AdmissionControl))
+ modes = modes.With(PolicyFamily.AdmissionControl, ParsePolicyMode(policy.AdmissionControl, modes[PolicyFamily.AdmissionControl]));
+ if (!string.IsNullOrWhiteSpace(policy.Http2AbuseBudget))
+ modes = modes.With(PolicyFamily.Http2AbuseBudget, ParsePolicyMode(policy.Http2AbuseBudget, modes[PolicyFamily.Http2AbuseBudget]));
+ if (!string.IsNullOrWhiteSpace(policy.Http2RelayValidation))
+ modes = modes.With(PolicyFamily.Http2RelayValidation, ParsePolicyMode(policy.Http2RelayValidation, modes[PolicyFamily.Http2RelayValidation]));
+ if (!string.IsNullOrWhiteSpace(policy.WebSocketFrameBudget))
+ modes = modes.With(PolicyFamily.WebSocketFrameBudget, ParsePolicyMode(policy.WebSocketFrameBudget, modes[PolicyFamily.WebSocketFrameBudget]));
+ if (!string.IsNullOrWhiteSpace(policy.Http1ReplaySafety))
+ modes = modes.With(PolicyFamily.Http1ReplaySafety, ParsePolicyMode(policy.Http1ReplaySafety, modes[PolicyFamily.Http1ReplaySafety]));
+ if (policy.AllowAmbiguousFraming is bool framing)
+ modes = modes.WithAllowAmbiguousFraming(framing);
proxy.PolicyModes = modes;
}
diff --git a/src/Titanium.Cli/Config/TestCommand.cs b/src/Titanium.Cli/Config/TestCommand.cs
index 4da47f511..460528556 100644
--- a/src/Titanium.Cli/Config/TestCommand.cs
+++ b/src/Titanium.Cli/Config/TestCommand.cs
@@ -13,6 +13,7 @@ public static Task ExecuteAsync(string[] args)
return Task.FromResult(PrintHelp());
}
+ var strict = args.Any(a => string.Equals(a, "--strict", StringComparison.OrdinalIgnoreCase));
var configPath = RunCommand.ParseConfigPath(args);
var loaded = ConfigLoader.Load(configPath);
var errors = TwpConfigValidator.Validate(loaded.Config);
@@ -26,6 +27,13 @@ public static Task ExecuteAsync(string[] args)
return Task.FromResult(1);
}
+ foreach (var warning in loaded.UnknownKeys)
+ AsyncConsole.WriteError("warning: " + warning);
+ foreach (var warning in TwpConfigValidator.CollectWarnings(loaded.Config))
+ AsyncConsole.WriteError("warning: " + warning);
+ if (strict && loaded.UnknownKeys.Count > 0)
+ return Task.FromResult(1);
+
var needsSession = RunCommand.ConfigNeedsSessionPath(loaded.Config);
AsyncConsole.WriteLine($"Config OK: {configPath}");
AsyncConsole.WriteLine($"Routes: {loaded.Config.Routes.Count}, Clusters: {loaded.Config.Clusters.Count}, Listeners: {loaded.Config.Listeners.Count}");
@@ -37,9 +45,10 @@ public static Task ExecuteAsync(string[] args)
internal static int PrintHelp()
{
AsyncConsole.WriteLine("""
- titanium test -c
+ titanium test -c [--strict]
-c, --config Path to twp.yaml / .json / .twp / .conf (required).
+ --strict Exit 1 when the file contains unknown keys.
Validates the config without opening listeners or serving traffic.
""");
diff --git a/src/Titanium.Cli/Parsers/ConfigLoader.cs b/src/Titanium.Cli/Parsers/ConfigLoader.cs
index 278f52f3d..926f1ee07 100644
--- a/src/Titanium.Cli/Parsers/ConfigLoader.cs
+++ b/src/Titanium.Cli/Parsers/ConfigLoader.cs
@@ -10,6 +10,7 @@ internal sealed class LoadedConfig
public required TwpConfig Config { get; init; }
public required string Path { get; init; }
public required string Dialect { get; init; }
+ public IReadOnlyList UnknownKeys { get; init; } = [];
}
internal static class ConfigLoader
@@ -68,11 +69,15 @@ public static LoadedConfig Load(string path)
}
}
+ var text = File.ReadAllText(path);
+ var yaml = ext.Equals(".yaml", StringComparison.OrdinalIgnoreCase) ||
+ ext.Equals(".yml", StringComparison.OrdinalIgnoreCase);
return new LoadedConfig
{
Path = path,
Dialect = "twp-native",
- Config = TwpConfigLoader.LoadFile(path),
+ Config = yaml ? TwpConfigLoader.LoadYaml(text) : TwpConfigLoader.LoadJson(text),
+ UnknownKeys = UnknownConfigKeyScanner.Scan(text, yaml),
};
}
diff --git a/src/Titanium.Cli/Titanium.Cli.csproj b/src/Titanium.Cli/Titanium.Cli.csproj
index 2b7d38d57..c271e6f1f 100644
--- a/src/Titanium.Cli/Titanium.Cli.csproj
+++ b/src/Titanium.Cli/Titanium.Cli.csproj
@@ -7,7 +7,7 @@
latestenablefalse
- 7.0.18
+ 7.0.19Jehonathan ThomasTitanium Web Proxy CLI (titanium / twp).MIT
diff --git a/src/Titanium.Inspector/Services/InterceptionService.cs b/src/Titanium.Inspector/Services/InterceptionService.cs
index 437c82b2a..e576bc5c3 100644
--- a/src/Titanium.Inspector/Services/InterceptionService.cs
+++ b/src/Titanium.Inspector/Services/InterceptionService.cs
@@ -603,6 +603,59 @@ public bool ReapplySystemProxyIfEnabled()
return SetSystemProxy(true, SystemProxySettings);
}
+ ///
+ /// Turns the system proxy off, then back on, so clients drop a cached connection and open a new one.
+ /// Rewriting the same ProxyEnable=1 value does not do that: packaged apps that already
+ /// failed to reach localhost keep their direct connection until ProxyEnable actually changes.
+ /// Must not run on the UI thread: the WinINET settings broadcast can deadlock Avalonia.
+ ///
+ ///
+ /// When this returns false after the proxy is turned off, it stays off (the user turned System proxy off).
+ ///
+ /// True only when the system proxy is enabled again.
+ public bool NudgeSystemProxy(Func? stillWanted = null)
+ {
+ lock (_systemProxyGate)
+ {
+ LastSystemProxyError = null;
+ if (!_systemProxyEnabled)
+ {
+ return false;
+ }
+
+ if (stillWanted is not null && !stillWanted())
+ {
+ return false;
+ }
+
+ if (_proxy is null || _endPoint is null || !_proxy.ProxyRunning)
+ {
+ LastSystemProxyError = "Proxy is not running";
+ return false;
+ }
+
+ try
+ {
+ if (!TryDisableSystemProxyLocked())
+ {
+ return false;
+ }
+
+ if (stillWanted is not null && !stillWanted())
+ {
+ return false;
+ }
+
+ return TryEnableSystemProxyLocked(SystemProxySettings);
+ }
+ catch (Exception ex)
+ {
+ LastSystemProxyError = ex.Message;
+ return false;
+ }
+ }
+ }
+
/// Install root CA and refresh from the store.
/// True when the cert is present in the target Root store after install (or Unix SSL trust succeeded / needs Keychain confirm).
///
diff --git a/src/Titanium.Inspector/Services/LoopbackExemptCopy.cs b/src/Titanium.Inspector/Services/LoopbackExemptCopy.cs
new file mode 100644
index 000000000..6cd56b5e9
--- /dev/null
+++ b/src/Titanium.Inspector/Services/LoopbackExemptCopy.cs
@@ -0,0 +1,114 @@
+namespace Titanium.Inspector.Services;
+
+/// Whether Inspector can receive Store-app traffic right now.
+public enum LoopbackCaptureReadiness
+{
+ Ready,
+ ProxyStopped,
+ SystemProxyOff,
+}
+
+/// Result returned when the Allow Store apps dialog closes.
+public sealed class LoopbackExemptResult
+{
+ public bool Changed { get; init; }
+
+ public string StatusText { get; init; } = LoopbackExemptCopy.ClosedUnchanged;
+}
+
+///
+/// Proxy state and the one-shot reconnect used by Allow Store apps.
+/// Checkbox edits do not call .
+///
+public sealed class LoopbackExemptSession
+{
+ public Func? Readiness { get; init; }
+
+ ///
+ /// Turns the system proxy off and back on so running apps open a new connection.
+ /// Returns true only when the proxy is enabled again afterward.
+ ///
+ public Func>? RefreshRunningAppsAsync { get; init; }
+
+ public LoopbackCaptureReadiness CurrentReadiness() =>
+ Readiness?.Invoke() ?? LoopbackCaptureReadiness.ProxyStopped;
+}
+
+/// User-facing copy for Allow Store apps. Shared by the dialog and tests.
+public static class LoopbackExemptCopy
+{
+ public const string Intro =
+ "Windows Store / UWP apps are blocked from localhost by default. Check apps that should use Inspector, then Apply. " +
+ "Restart each app (fully quit it) after Apply. Inspector must be running with System proxy on. " +
+ "Clear all removes every allow entry.";
+
+ public const string ClosedUnchanged = "Store apps unchanged.";
+
+ public const string ProxyStoppedWarning =
+ "Start the proxy and turn on System proxy. Store apps cannot be captured until both are on.";
+
+ public const string SystemProxyOffWarning =
+ "Turn on System proxy (Capture menu). The allow list only lets Store apps reach localhost; they still follow the system proxy.";
+
+ public const string RefreshingStatus = "Refreshing system proxy so running apps reconnect…";
+
+ public static string? ProxyWarning(LoopbackCaptureReadiness readiness) => readiness switch
+ {
+ LoopbackCaptureReadiness.ProxyStopped => ProxyStoppedWarning,
+ LoopbackCaptureReadiness.SystemProxyOff => SystemProxyOffWarning,
+ _ => null,
+ };
+
+ ///
+ /// Refresh only after a real allow-list change, and only while System proxy is already on.
+ /// Checkbox edits are not a change until Apply or Clear.
+ ///
+ public static bool ShouldRefreshRunningApps(bool setChanged, LoopbackCaptureReadiness readiness) =>
+ setChanged && readiness == LoopbackCaptureReadiness.Ready;
+
+ public static bool SameSet(IEnumerable left, IEnumerable right)
+ {
+ var a = ToSet(left);
+ var b = ToSet(right);
+ return a.SetEquals(b);
+ }
+
+ public static string UnchangedStatus(int count) =>
+ count == 1
+ ? "Allow list already matches this app."
+ : $"Allow list already matches these {count} apps.";
+
+ public static string AppliedStatus(int count, LoopbackCaptureReadiness readiness, bool refreshed)
+ {
+ var allowed = count == 1 ? "Allowed 1 app." : $"Allowed {count} apps.";
+ if (refreshed)
+ {
+ return allowed +
+ " System proxy was refreshed so running apps reconnect. If traffic still does not appear, fully quit and reopen the app.";
+ }
+
+ return readiness switch
+ {
+ LoopbackCaptureReadiness.ProxyStopped =>
+ allowed + " Start the proxy and turn on System proxy, then fully quit and reopen the app.",
+ LoopbackCaptureReadiness.SystemProxyOff =>
+ allowed + " Turn on System proxy, then fully quit and reopen the app.",
+ _ => allowed + " Fully quit and reopen the app to capture traffic.",
+ };
+ }
+
+ public static string ClearedStatus(LoopbackCaptureReadiness readiness, bool refreshed)
+ {
+ if (refreshed)
+ {
+ return "All loopback exemptions cleared. System proxy was refreshed so running apps reconnect.";
+ }
+
+ return readiness == LoopbackCaptureReadiness.Ready
+ ? "All loopback exemptions cleared. Fully quit and reopen any app that was using Inspector."
+ : "All loopback exemptions cleared.";
+ }
+
+ private static HashSet ToSet(IEnumerable values) =>
+ new(values.Where(static s => !string.IsNullOrWhiteSpace(s)), StringComparer.OrdinalIgnoreCase);
+}
diff --git a/src/Titanium.Inspector/Services/SessionSearch.cs b/src/Titanium.Inspector/Services/SessionSearch.cs
index 4d2dcfac1..bd82cbe21 100644
--- a/src/Titanium.Inspector/Services/SessionSearch.cs
+++ b/src/Titanium.Inspector/Services/SessionSearch.cs
@@ -3,8 +3,9 @@
namespace Titanium.Inspector.Services;
///
-/// Session search/filter syntax:
-/// method:, status: (exact or 2xx–5xx), host:, url:, body:, process:, content-type:,
+/// Session search/filter syntax. A bare word matches URL, host, process name, process ID (exact),
+/// method (exact), or status code (exact). Prefixes limit the match to one field:
+/// method:, status: (exact or 2xx–5xx), host:, url:, body:, process:, pid:, protocol:, content-type:,
/// is:ws|grpc|tunnel|multipart|error, hide:tunnel|image|static
///
public static class SessionSearch
@@ -194,31 +195,41 @@ public static string BuildSessionCountText(
DateTimeOffset? oldestStartedUtc)
{
var searching = !string.IsNullOrWhiteSpace(searchQuery);
+ var kept = FormatSessionCount(totalCount);
+ if (retentionEvictedTotal > 0)
+ kept += " most recent";
+
var text = searching
- ? $"Sessions: {visibleCount} / {totalCount}"
- : $"Sessions: {totalCount}";
+ ? $"Sessions: {FormatSessionCount(visibleCount)} of {kept} match filter"
+ : $"Sessions: {kept}";
+
+ var emptySearch = searching && visibleCount == 0 && totalCount > 0;
if (retentionEvictedTotal > 0 && oldestStartedUtc is { } oldest)
{
+ // Retention dropped older sessions: the list is the most recent kept window.
text += $" · since {oldest.ToLocalTime():HH:mm}";
}
- if (searching && visibleCount == 0 && totalCount > 0)
+ if (emptySearch)
text += FormatEmptySearchRetentionHint(retentionEvictedTotal);
return text;
}
+ /// Compact round thousands (10k) so a retention cap is readable in the status bar.
+ private static string FormatSessionCount(int count) =>
+ count >= 1_000 && count % 1_000 == 0
+ ? $"{count / 1_000}k"
+ : count.ToString("N0");
+
private static string FormatEmptySearchRetentionHint(int retentionEvictedTotal)
{
if (retentionEvictedTotal <= 0)
return "";
- var retention = retentionEvictedTotal == 1
- ? "1 removed by retention"
- : $"{retentionEvictedTotal} removed by retention";
-
- return $" · no matches in current list · {retention}";
+ // Removed sessions are no longer searchable, so a zero-match result may not be the whole story.
+ return $" · {retentionEvictedTotal:N0} older removed by retention (not searched)";
}
private static List<(string Key, string Value)> Tokenize(string query)
@@ -232,7 +243,8 @@ private static string FormatEmptySearchRetentionHint(int retentionEvictedTotal)
}
else
{
- list.Add(("url", groups[3].Value));
+ // Empty key is not a user prefix (those match [\w-]+), so text: stays an unknown key.
+ list.Add(("", groups[3].Value));
}
}
@@ -254,6 +266,11 @@ private static bool MatchToken(
((s.RequestBodyText?.Contains(token.Value, StringComparison.OrdinalIgnoreCase) == true) ||
(s.ResponseBodyText?.Contains(token.Value, StringComparison.OrdinalIgnoreCase) == true)),
"process" => MatchProcess(s, token.Value),
+ "pid" => s.ProcessId > 0 &&
+ s.ProcessId.ToString().Equals(token.Value, StringComparison.Ordinal),
+ "protocol" =>
+ s.Protocol?.Contains(token.Value, StringComparison.OrdinalIgnoreCase) == true,
+ "" => MatchBareText(s, token.Value),
"content-type" or "contenttype" =>
s.ContentType?.Contains(token.Value, StringComparison.OrdinalIgnoreCase) == true,
"is" => token.Value.ToLowerInvariant() switch
@@ -327,6 +344,43 @@ private static bool MatchProcess(SessionSnapshot s, string value) =>
s.ProcessDisplay.Contains(value, StringComparison.OrdinalIgnoreCase) ||
(s.ProcessId > 0 && s.ProcessId.ToString().Equals(value, StringComparison.Ordinal));
+ ///
+ /// Free-text match over identifying columns. Process matches by name, or by exact PID —
+ /// never by substring of (chrome:4430),
+ /// which would treat port-like numbers as hits.
+ ///
+ private static bool MatchBareText(SessionSnapshot s, string value)
+ {
+ if (s.Url.Contains(value, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (MatchHost(s, value))
+ {
+ return true;
+ }
+
+ if (!string.IsNullOrEmpty(s.ProcessName) &&
+ s.ProcessName.Contains(value, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ if (s.ProcessId > 0 &&
+ s.ProcessId.ToString().Equals(value, StringComparison.Ordinal))
+ {
+ return true;
+ }
+
+ if (s.Method.Equals(value, StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return s.StatusCode?.ToString() == value;
+ }
+
internal static bool IsImageOrStatic(SessionSnapshot s)
{
var ct = s.ContentType;
diff --git a/src/Titanium.Inspector/Titanium.Inspector.csproj b/src/Titanium.Inspector/Titanium.Inspector.csproj
index 1fda90cf1..95c3d84f2 100644
--- a/src/Titanium.Inspector/Titanium.Inspector.csproj
+++ b/src/Titanium.Inspector/Titanium.Inspector.csproj
@@ -8,7 +8,7 @@
enabletruefalse
- 7.0.18
+ 7.0.19Jehonathan ThomasTitanium Inspector desktop traffic debugger (PolyForm Noncommercial).LICENSE
diff --git a/src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs b/src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs
index 58fc28bce..1ccd10ea1 100644
--- a/src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs
+++ b/src/Titanium.Inspector/ViewModels/MainWindowViewModel.cs
@@ -932,7 +932,6 @@ private async Task TryToggleSystemProxyAsync()
SystemProxy = true;
}
- private Task AwaitCancellableAsync(Task task) => task.WaitAsync(StatusCancelToken);
private Task AwaitCancellableAsync(Task task) => task.WaitAsync(StatusCancelToken);
///
@@ -974,8 +973,52 @@ private async Task OpenLoopbackExemptAsync()
return;
}
- await AwaitCancellableAsync(LoopbackExemptWindow.ShowAsync(owner));
- StatusText = "Allow Store apps dialog closed";
+ var result = await AwaitDialogAsync(LoopbackExemptWindow.ShowAsync(owner, CreateLoopbackExemptSession()));
+ StatusText = result.StatusText;
+ }
+
+ private LoopbackExemptSession CreateLoopbackExemptSession() => new()
+ {
+ Readiness = ReadLoopbackCaptureReadiness,
+ RefreshRunningAppsAsync = RefreshStoreAppsAfterLoopbackChangeAsync,
+ };
+
+ private LoopbackCaptureReadiness ReadLoopbackCaptureReadiness()
+ {
+ if (!_interception.IsRunning)
+ return LoopbackCaptureReadiness.ProxyStopped;
+ if (!_interception.SystemProxyEnabled)
+ return LoopbackCaptureReadiness.SystemProxyOff;
+ return LoopbackCaptureReadiness.Ready;
+ }
+
+ ///
+ /// One off/on cycle of the system proxy after the Store-app allow list changes.
+ /// Runs off the UI thread. Does nothing when System proxy is already off.
+ ///
+ private async Task RefreshStoreAppsAfterLoopbackChangeAsync()
+ {
+ if (!_interception.SystemProxyEnabled || !_interception.IsRunning)
+ return false;
+
+ var ok = await RunOffUiAsync(
+ () => _interception.NudgeSystemProxy(
+ stillWanted: () => _systemProxy && _interception.IsRunning),
+ CancellationToken.None).ConfigureAwait(true);
+
+ if (!_interception.SystemProxyEnabled && _systemProxy)
+ {
+ await MarshalToUiAsync(() =>
+ {
+ SetSystemProxyCore(false);
+ SetOutcomeStatus(
+ FormatSystemProxyFailureStatus(true, _interception.LastSystemProxyError),
+ StatusSeverity.Error,
+ toastImportant: true);
+ }, CancellationToken.None).ConfigureAwait(true);
+ }
+
+ return ok && _interception.SystemProxyEnabled;
}
private async Task OpenSessionRetentionAsync()
diff --git a/src/Titanium.Inspector/Views/LoopbackExemptWindow.axaml b/src/Titanium.Inspector/Views/LoopbackExemptWindow.axaml
index f3b8b5359..a53a4e03f 100644
--- a/src/Titanium.Inspector/Views/LoopbackExemptWindow.axaml
+++ b/src/Titanium.Inspector/Views/LoopbackExemptWindow.axaml
@@ -3,11 +3,13 @@
xmlns:svc="using:Titanium.Inspector.Services"
x:Class="Titanium.Inspector.Views.LoopbackExemptWindow" AutomationProperties.AutomationId="LoopbackExemptWindow"
Title="Allow Store apps to use this proxy"
- Width="640" Height="520"
+ Width="640" Height="560"
WindowStartupLocation="CenterOwner">
-
+
+
internal static async Task CreateAsync(TcpServerConnection connection,
ILogger logger, CancellationToken cancellationToken,
- ProxyResourceLimits? resourceLimits = null)
+ ProxyResourceLimits? resourceLimits = null, int maxDecodedHeaderListBytes = 64 * 1024)
{
// Streaming bodies are bounded by HTTP/2 flow control (stream window × concurrent streams),
// not by MaxBufferedBodyBytes. That budget applies only where the library buffers a whole body.
var instance = new Http2OriginConnection(connection, logger,
- resourceLimits ?? ProxyResourceLimits.Default);
+ resourceLimits ?? ProxyResourceLimits.Default, maxDecodedHeaderListBytes);
var preface = Http2Helper.ConnectionPreface;
connection.Http2SessionStarted = true;
@@ -1125,10 +1131,12 @@ private void FlushOriginStreamCredit(int streamId, PendingStream pending, bool f
headerBlockStreamId = streamId;
headerBlockEndStream = (flags & Http2FrameFlag.EndStream) != 0;
var data = StripHeadersFraming(payloadSpan, flags);
- if (data.Length > MaxHeaderBlockBytes)
+ if (data.Length > maxCompressedHeaderBlockBytes)
{
+ ProxyLog.LimitExceeded(logger, LimitId.Http2CompressedHeaderBlock, PolicyMode.Enforce,
+ data.Length, maxCompressedHeaderBlockBytes, "connection closed");
Fail(new IOException(
- $"HTTP/2 protocol error: origin header block exceeded {MaxHeaderBlockBytes} bytes."));
+ $"HTTP/2 protocol error: origin header block exceeded {maxCompressedHeaderBlockBytes} bytes."));
return;
}
@@ -1164,10 +1172,12 @@ private void FlushOriginStreamCredit(int streamId, PendingStream pending, bool f
return;
}
- if (headerBlockBuffer.Length + length > MaxHeaderBlockBytes)
+ if (headerBlockBuffer.Length + length > maxCompressedHeaderBlockBytes)
{
+ ProxyLog.LimitExceeded(logger, LimitId.Http2CompressedHeaderBlock, PolicyMode.Enforce,
+ headerBlockBuffer.Length + length, maxCompressedHeaderBlockBytes, "connection closed");
Fail(new IOException(
- $"HTTP/2 protocol error: origin CONTINUATION block exceeded {MaxHeaderBlockBytes} bytes."));
+ $"HTTP/2 protocol error: origin CONTINUATION block exceeded {maxCompressedHeaderBlockBytes} bytes."));
return;
}
@@ -1607,9 +1617,18 @@ private static void ThrowIfDataPaddingTooLong(int padLength, int payloadLength)
try
{
- decoder ??= new Decoder(8192, 4096);
+ decoder ??= new Decoder(maxDecodedHeaderListBytes, 4096);
decoder.Decode(compressed, headerCollector);
- decoder.EndHeaderBlock();
+ if (decoder.EndHeaderBlock())
+ {
+ // Truncation keeps the dynamic table consistent (indexed entries are still inserted).
+ // Fail only this stream so siblings on the connection stay healthy.
+ ProxyLog.LimitExceeded(logger, LimitId.DecodedHeaderList, PolicyMode.Enforce,
+ maxDecodedHeaderListBytes + 1L, maxDecodedHeaderListBytes, "502");
+ FailStream(streamId, new ProxyHttpException(
+ "Origin HTTP/2 header list exceeded maxDecodedHeaderListBytes.", null, null));
+ return;
+ }
}
catch (Exception ex)
{
diff --git a/src/Titanium.Web.Proxy/Http2/Http2OriginRelayPool.cs b/src/Titanium.Web.Proxy/Http2/Http2OriginRelayPool.cs
index aa02879e2..3d393bb3f 100644
--- a/src/Titanium.Web.Proxy/Http2/Http2OriginRelayPool.cs
+++ b/src/Titanium.Web.Proxy/Http2/Http2OriginRelayPool.cs
@@ -267,9 +267,10 @@ await Http2Helper.SendHttp2ClientConnectionStartupAsync(connection.Stream, cance
}
}
- private static OriginLeg CreateLeg(TcpServerConnection connection, SemaphoreSlim writeLock, bool ownsWriteLock)
+ private OriginLeg CreateLeg(TcpServerConnection connection, SemaphoreSlim writeLock, bool ownsWriteLock)
{
- return new OriginLeg(connection, new Http2FrameWriter(connection.Stream, writeLock), writeLock, ownsWriteLock);
+ return new OriginLeg(connection, new Http2FrameWriter(connection.Stream, writeLock), writeLock, ownsWriteLock,
+ resourceLimits.MaxDeferredOutboundBytesPerStream, resourceLimits.Http2WindowUpdateTimeoutSeconds);
}
private static async Task DisposeLegAsync(OriginLeg leg)
@@ -304,7 +305,7 @@ public async ValueTask DisposeAsync()
internal sealed class OriginLeg
{
public OriginLeg(TcpServerConnection connection, Http2FrameWriter writer, SemaphoreSlim writeLock,
- bool ownsWriteLock)
+ bool ownsWriteLock, int maxDeferredBytesPerStream = 0, int windowUpdateTimeoutSeconds = 0)
{
Connection = connection;
Writer = writer;
@@ -312,8 +313,12 @@ public OriginLeg(TcpServerConnection connection, Http2FrameWriter writer, Semaph
WriteLock = writeLock;
OwnsWriteLock = ownsWriteLock;
NextStreamId = 1;
- SendFlow = new Http2FlowController();
- OutboundDeferred = new Http2DeferredOutboundData();
+ SendFlow = new Http2FlowController(windowUpdateTimeoutSeconds > 0
+ ? TimeSpan.FromSeconds(windowUpdateTimeoutSeconds)
+ : null);
+ OutboundDeferred = new Http2DeferredOutboundData(maxDeferredBytesPerStream > 0
+ ? maxDeferredBytesPerStream
+ : Http2DeferredOutboundData.HardMaxDeferredBytesPerStream);
}
public TcpServerConnection Connection { get; }
diff --git a/src/Titanium.Web.Proxy/Http3/Http3Frame.cs b/src/Titanium.Web.Proxy/Http3/Http3Frame.cs
index ef7f235b3..ccee60190 100644
--- a/src/Titanium.Web.Proxy/Http3/Http3Frame.cs
+++ b/src/Titanium.Web.Proxy/Http3/Http3Frame.cs
@@ -5,6 +5,8 @@
using System.Net.Quic;
using System.Threading;
using System.Threading.Tasks;
+using Titanium.Web.Proxy.Logging;
+using Titanium.Web.Proxy.Options;
namespace Titanium.Web.Proxy.Http3;
@@ -26,6 +28,15 @@ internal sealed class Http3Frame
///
internal const long DefaultMaxPayloadBytes = 4 * 1024 * 1024;
+ ///
+ /// Declared lengths above this are rented as data arrives, starting at this size, so a peer
+ /// that advertises a huge length and sends nothing cannot reserve the full cap up front.
+ ///
+ internal const int ProgressiveRentChunkBytes = 256 * 1024;
+
+ /// Last payload rent size on the progressive path. Tests read this; production does not branch on it.
+ internal static int TestLastRentBytes { get; set; }
+
public ulong Type { get; }
public ReadOnlyMemory Payload { get; }
@@ -47,6 +58,12 @@ public void ReturnPayload()
ArrayPool.Shared.Return(buffer);
}
+ internal readonly struct FrameHeader
+ {
+ public ulong Type { get; init; }
+ public long Length { get; init; }
+ }
+
///
/// Reads one HTTP/3 frame from .
/// Returns when the stream is cleanly closed (end-of-data).
@@ -57,37 +74,116 @@ public void ReturnPayload()
long maxPayloadBytes,
CancellationToken cancellationToken)
{
- var frameType = await Http3VarInt.ReadAsync(stream, cancellationToken);
+ var header = await ReadFrameHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
+ if (header is null) return null;
+ return await ReadPayloadAfterHeaderAsync(stream, header.Value, maxPayloadBytes, cancellationToken)
+ .ConfigureAwait(false);
+ }
+
+ /// Reads the type and length varints. Null on a clean end of stream before the type.
+ internal static async ValueTask ReadFrameHeaderAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ var frameType = await Http3VarInt.ReadAsync(stream, cancellationToken).ConfigureAwait(false);
if (frameType is null) return null;
- var payloadLength = await Http3VarInt.ReadAsync(stream, cancellationToken)
+ var payloadLength = await Http3VarInt.ReadAsync(stream, cancellationToken).ConfigureAwait(false)
?? throw new Http3ConnectionException(Http3ErrorCode.FrameError, "Unexpected end of stream reading frame length.");
+ if (payloadLength > long.MaxValue)
+ throw new Http3ConnectionException(Http3ErrorCode.FrameError, "HTTP/3 frame length does not fit.");
+ return new FrameHeader { Type = frameType.Value, Length = (long)payloadLength };
+ }
- // 0 means "use the dedicated frame limit", not unlimited. A hostile peer picks
- // payloadLength; checking before the int cast is what stops the rent.
+ /// Reads a payload whose header was already consumed. Enforces .
+ internal static ValueTask ReadPayloadAfterHeaderAsync(
+ Stream stream, FrameHeader header, long maxPayloadBytes, CancellationToken cancellationToken)
+ {
var limit = maxPayloadBytes > 0 ? maxPayloadBytes : DefaultMaxPayloadBytes;
- if (payloadLength > (ulong)limit || payloadLength > int.MaxValue)
+ if ((ulong)header.Length > (ulong)limit || header.Length > int.MaxValue)
+ {
+ ProxyLog.LimitExceeded(ProxyDiagnostics.Logger, LimitId.Http3FramePayload, PolicyMode.Enforce,
+ header.Length, limit, "HTTP/3 stream reset");
throw new Http3ConnectionException(Http3ErrorCode.ExcessiveLoad,
- $"HTTP/3 frame payload {payloadLength} bytes exceeds limit {limit}.");
+ $"HTTP/3 frame payload {header.Length} bytes exceeds limit {limit}.");
+ }
+ if (header.Length == 0)
+ return new ValueTask(new Http3Frame(header.Type, ReadOnlyMemory.Empty, null));
+ return ReadPayloadBytesAsync(stream, header.Type, (int)header.Length, cancellationToken);
+ }
- if (payloadLength == 0)
- return new Http3Frame(frameType.Value, ReadOnlyMemory.Empty, null);
+ ///
+ /// Copies a DATA payload in slices without renting the declared length. Used by the no-hook
+ /// relay so a frame larger than the whole-frame cap can still stream.
+ ///
+ internal static async ValueTask CopyPayloadAsync(
+ Stream stream, long length, int sliceBytes,
+ Func, CancellationToken, ValueTask> consume,
+ CancellationToken cancellationToken)
+ {
+ if (length < 0)
+ throw new Http3ConnectionException(Http3ErrorCode.FrameError, "HTTP/3 frame length is negative.");
+ if (length == 0) return;
+ var slice = sliceBytes > 0 ? sliceBytes : 16 * 1024;
+ var buffer = ArrayPool.Shared.Rent(slice);
+ try
+ {
+ var remaining = length;
+ while (remaining > 0)
+ {
+ var n = (int)Math.Min(slice, remaining);
+ var filled = 0;
+ while (filled < n)
+ {
+ var read = await stream.ReadAsync(buffer.AsMemory(filled, n - filled), cancellationToken)
+ .ConfigureAwait(false);
+ if (read == 0)
+ throw new Http3ConnectionException(Http3ErrorCode.FrameError,
+ $"Unexpected end of stream reading frame payload (expected {length}).");
+ filled += read;
+ }
+
+ await consume(buffer.AsMemory(0, n), cancellationToken).ConfigureAwait(false);
+ remaining -= n;
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
- var length = (int)payloadLength;
- var rented = ArrayPool.Shared.Rent(length);
+ private static async ValueTask ReadPayloadBytesAsync(
+ Stream stream, ulong frameType, int length, CancellationToken cancellationToken)
+ {
+ // Small frames keep the single rent. Larger declared lengths grow as bytes arrive.
+ var initial = length <= ProgressiveRentChunkBytes ? length : ProgressiveRentChunkBytes;
+ var rented = ArrayPool.Shared.Rent(initial);
+ if (length > ProgressiveRentChunkBytes)
+ TestLastRentBytes = initial;
+ var capacity = initial;
try
{
var offset = 0;
while (offset < length)
{
- var read = await stream.ReadAsync(rented.AsMemory(offset, length - offset), cancellationToken);
+ if (offset == capacity)
+ {
+ var next = Math.Min(length, capacity * 2);
+ var bigger = ArrayPool.Shared.Rent(next);
+ Buffer.BlockCopy(rented, 0, bigger, 0, offset);
+ ArrayPool.Shared.Return(rented);
+ rented = bigger;
+ capacity = next;
+ }
+
+ var read = await stream.ReadAsync(rented.AsMemory(offset, Math.Min(capacity, length) - offset),
+ cancellationToken).ConfigureAwait(false);
if (read == 0)
throw new Http3ConnectionException(Http3ErrorCode.FrameError,
- $"Unexpected end of stream reading frame payload (expected {payloadLength}, got {offset}).");
+ $"Unexpected end of stream reading frame payload (expected {length}, got {offset}).");
offset += read;
}
- return new Http3Frame(frameType.Value, rented.AsMemory(0, length), rented);
+ return new Http3Frame(frameType, rented.AsMemory(0, length), rented);
}
catch
{
diff --git a/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Http2.cs b/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Http2.cs
index c60f3284b..be5678572 100644
--- a/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Http2.cs
+++ b/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Http2.cs
@@ -218,7 +218,7 @@ private static async Task SendHttp2OriginFastWithGoAwayRetr
null, sessionArgs);
}
- return await Http2OriginConnection.CreateAsync(tcp, logger, ct, server.ResourceLimits);
+ return await Http2OriginConnection.CreateAsync(tcp, logger, ct, server.ResourceLimits, server.MaxDecodedHeaderListBytes);
}
finally
{
@@ -485,7 +485,7 @@ private readonly record struct Http2OriginTarget(
null, sessionArgs);
}
- return await Http2OriginConnection.CreateAsync(tcp, logger, ct, server.ResourceLimits);
+ return await Http2OriginConnection.CreateAsync(tcp, logger, ct, server.ResourceLimits, server.MaxDecodedHeaderListBytes);
}, cancellationToken);
}
diff --git a/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Quic.cs b/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Quic.cs
index a8b353e4d..f009c8963 100644
--- a/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Quic.cs
+++ b/src/Titanium.Web.Proxy/Http3/Http3OriginBridge.Quic.cs
@@ -17,6 +17,7 @@
using Titanium.Web.Proxy.Http.Responses;
using Titanium.Web.Proxy.Http2;
using Titanium.Web.Proxy.Http3.Qpack;
+using Titanium.Web.Proxy.Logging;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
using Titanium.Web.Proxy.Network.Quic;
@@ -173,7 +174,7 @@ await Http3Frame.WriteAsync(originStream, Http3FrameType.Data, body, cancellatio
originStream.CompleteWrites();
sessionArgs.Timing?.MarkRequestSent();
- const int maxInterimResponses = 20;
+ var maxInterimResponses = server.ResourceLimits.MaxInterimResponses;
int interimCount = 0;
Http3Frame? responseHeadersFrame;
@@ -212,8 +213,12 @@ await Http3Frame.WriteAsync(originStream, Http3FrameType.Data, body, cancellatio
if (finalStatus is >= 100 and < 200)
{
if (++interimCount > maxInterimResponses)
+ {
+ ProxyLog.LimitExceeded(logger, LimitId.InterimResponses, PolicyMode.Enforce,
+ interimCount, maxInterimResponses, "stream reset");
throw new Http3StreamException(Http3ErrorCode.InternalError,
$"Origin sent more than {maxInterimResponses} interim responses.");
+ }
if (onInterimResponse != null)
{
@@ -254,7 +259,7 @@ await Http3Frame.WriteAsync(originStream, Http3FrameType.Data, body, cancellatio
}
}
- var maxPayload = Http3Frame.DefaultMaxPayloadBytes;
+ var maxPayload = server.ResourceLimits.MaxHttp3FramePayloadBytes;
// Stream large / unknown-length bodies as DATA arrives (TTFB on big HTML). Tiny known-CL
// must materialize first: H1 WriteResponseAsync + StreamBodyWriter emits a header-only
@@ -367,8 +372,18 @@ await Http3Frame.WriteAsync(originStream, Http3FrameType.Data, body, cancellatio
{
while (true)
{
- var frame = await Http3Frame.ReadAsync(streamToClient, maxPayloadBytes: maxPayload, ct);
- if (frame == null) break;
+ var header = await Http3Frame.ReadFrameHeaderAsync(streamToClient, ct);
+ if (header is null) break;
+ if (header.Value.Type == Http3FrameType.Data)
+ {
+ if (header.Value.Length > 0)
+ await Http3Frame.CopyPayloadAsync(streamToClient, header.Value.Length, 16 * 1024,
+ (slice, token) => clientBodyStream.WriteAsync(slice, token), ct);
+ continue;
+ }
+
+ var frame = await Http3Frame.ReadPayloadAfterHeaderAsync(
+ streamToClient, header.Value, maxPayload, ct);
try
{
if (frame.Type == Http3FrameType.Headers)
@@ -381,10 +396,6 @@ await Http3Frame.WriteAsync(originStream, Http3FrameType.Data, body, cancellatio
break;
}
- if (frame.Type != Http3FrameType.Data || frame.Payload.Length == 0)
- continue;
-
- await clientBodyStream.WriteAsync(frame.Payload, ct);
}
finally
{
@@ -666,7 +677,7 @@ await Http3Frame.WriteAsync(originStream, Http3FrameType.Headers, encodedHeaders
// Verbatim origin→client frame copy, or MITM capture when clientStream is null.
// Tiny GET: coalesce HEADERS+DATA into one Quic write (origin probe sends both).
const int relayCoalesceMaxBytes = 16 * 1024;
- var maxPayload = Math.Max(Http3Frame.DefaultMaxPayloadBytes, server.MaxDecodedHeaderListBytes);
+ var maxPayload = Math.Max(server.ResourceLimits.MaxHttp3FramePayloadBytes, server.MaxDecodedHeaderListBytes);
var captureForMitm = clientStream is null;
var sawFinalHeaders = false;
while (true)
diff --git a/src/Titanium.Web.Proxy/Http3/Http3RequestStream.cs b/src/Titanium.Web.Proxy/Http3/Http3RequestStream.cs
index ec7f9e341..c006677c3 100644
--- a/src/Titanium.Web.Proxy/Http3/Http3RequestStream.cs
+++ b/src/Titanium.Web.Proxy/Http3/Http3RequestStream.cs
@@ -12,6 +12,7 @@
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Http3.Qpack;
+using Titanium.Web.Proxy.Logging;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network.Quic;
using Titanium.Web.Proxy.Network.Streams;
@@ -268,6 +269,8 @@ await HandleH3OriginFastPathAsync(
catch (BodySizeLimitExceededException)
{
// GetRequestBody() during BeforeRequest hit MaxBufferedBodyBytes.
+ ProxyLog.LimitExceeded(server.Logger, LimitId.BufferedBody, PolicyMode.Enforce,
+ server.MaxBufferedBodyBytes, server.MaxBufferedBodyBytes, "413");
await SendSimpleStatusResponseAsync(stream, 413, qpackContext, cancellationToken);
stream.Abort(QuicAbortDirection.Read, (long)Http3ErrorCode.ExcessiveLoad);
streamState.RequestClosed = true;
@@ -1006,12 +1009,13 @@ private static async Task BufferRequestBodyAsync(
{
var body = new MemoryStream();
var maxBufferedBodyBytes = sessionArgs.MaxBufferedBodyBytes ?? server.MaxBufferedBodyBytes;
+ var maxFrame = server.ResourceLimits.MaxHttp3FramePayloadBytes;
var boundedBody = new BoundedWriteStream(body, maxBufferedBodyBytes, server.PolicyModes[PolicyFamily.BodyBudget]);
try
{
while (true)
{
- var frame = await Http3Frame.ReadAsync(stream, maxPayloadBytes: 0, ct);
+ var frame = await Http3Frame.ReadAsync(stream, maxPayloadBytes: maxFrame, ct);
if (frame is null) break;
try
{
@@ -1087,25 +1091,28 @@ await StreamRequestBodyToWriteAsync(
if (!hasHook)
{
+ var maxFrame = server.ResourceLimits.MaxHttp3FramePayloadBytes;
while (true)
{
- var frame = await Http3Frame.ReadAsync(clientStream, maxPayloadBytes: 0, ct);
- if (frame is null) break;
+ var header = await Http3Frame.ReadFrameHeaderAsync(clientStream, ct);
+ if (header is null) break;
+ if (header.Value.Type == Http3FrameType.Data)
+ {
+ if (header.Value.Length > 0)
+ await Http3Frame.CopyPayloadAsync(clientStream, header.Value.Length, 16 * 1024, writeData, ct);
+ continue;
+ }
+
+ var frame = await Http3Frame.ReadPayloadAfterHeaderAsync(clientStream, header.Value, maxFrame, ct);
try
{
- switch (frame.Type)
+ if (frame.Type == Http3FrameType.Headers)
{
- case Http3FrameType.Data:
- if (frame.Payload.Length > 0)
- await writeData(frame.Payload, ct);
- break;
- case Http3FrameType.Headers:
- if (onTrailerFrame != null)
- await onTrailerFrame(frame.Payload, ct);
- var trailers = QpackDecoder.Decode(frame.Payload.Span);
- foreach (var (name, value) in trailers)
- request.TrailingHeaders.AddHeader(new HttpHeader(name, value));
- break;
+ if (onTrailerFrame != null)
+ await onTrailerFrame(frame.Payload, ct);
+ var trailers = QpackDecoder.Decode(frame.Payload.Span);
+ foreach (var (name, value) in trailers)
+ request.TrailingHeaders.AddHeader(new HttpHeader(name, value));
}
}
finally
@@ -1116,10 +1123,11 @@ await StreamRequestBodyToWriteAsync(
}
else
{
- var current = await Http3Frame.ReadAsync(clientStream, maxPayloadBytes: 0, ct);
+ var maxFrame = server.ResourceLimits.MaxHttp3FramePayloadBytes;
+ var current = await Http3Frame.ReadAsync(clientStream, maxPayloadBytes: maxFrame, ct);
while (current != null)
{
- var next = await Http3Frame.ReadAsync(clientStream, maxPayloadBytes: 0, ct);
+ var next = await Http3Frame.ReadAsync(clientStream, maxPayloadBytes: maxFrame, ct);
var isLast = next == null || next.Type == Http3FrameType.Headers;
try
diff --git a/src/Titanium.Web.Proxy/Logging/LimitCatalog.cs b/src/Titanium.Web.Proxy/Logging/LimitCatalog.cs
new file mode 100644
index 000000000..d6e6bd3df
--- /dev/null
+++ b/src/Titanium.Web.Proxy/Logging/LimitCatalog.cs
@@ -0,0 +1,195 @@
+using System;
+using System.Threading;
+using Titanium.Web.Proxy.Options;
+
+namespace Titanium.Web.Proxy.Logging;
+
+///
+/// Stable id for one limit-driven failure. The rate limiter indexes this; do not renumber.
+///
+internal enum LimitId
+{
+ BufferedBody = 0,
+ HeaderCount = 1,
+ HeaderAggregate = 2,
+ HeaderLine = 3,
+ Http3FramePayload = 4,
+ DeferredOutboundBytes = 5,
+ DecodedHeaderList = 6,
+ TrailerHeaderCount = 7,
+ TrailerHeaderBlock = 8,
+ Http2CompressedHeaderBlock = 9,
+ InterimResponses = 10,
+ AuthChallengeRounds = 11,
+ UpstreamProxyAuthAttempts = 12,
+ WinAuthToken = 13,
+ Http2WindowUpdate = 14,
+ UpgradeHandshakeHeaders = 15,
+ AdmissionGlobal = 16,
+ AdmissionEndpoint = 17,
+ TimeoutConnect = 18,
+ TimeoutResponseHeader = 19,
+ TimeoutIdleRead = 20,
+ TimeoutIdleWrite = 21,
+ TimeoutRequest = 22,
+ TimeoutClientHeader = 23,
+ WebSocketFrame = 24,
+ Count = 25
+}
+
+///
+/// One row of the operator-facing limit reference: property, CLI key, default, and the hint
+/// printed when the limit rejects a request. Docs and the config applier are checked against this.
+///
+internal readonly struct LimitEntry
+{
+ public LimitEntry(LimitId id, string property, string cliKey, string what, string familyKey,
+ string hint, PolicyFamily? family)
+ {
+ Id = id;
+ Property = property;
+ CliKey = cliKey;
+ What = what;
+ FamilyKey = familyKey;
+ Hint = hint;
+ Family = family;
+ }
+
+ public LimitId Id { get; }
+ public string Property { get; }
+ public string CliKey { get; }
+ public string What { get; }
+ public string FamilyKey { get; }
+ public string Hint { get; }
+ public PolicyFamily? Family { get; }
+}
+
+/// Single source of truth for limit names, CLI keys, and log hints.
+internal static class LimitCatalog
+{
+ private const string HeaderLimitsKey = "headerLimits";
+ private const string AdmissionControlKey = "admissionControl";
+
+ private static readonly LimitEntry[] Entries = Build();
+
+ internal static LimitEntry Get(LimitId id) => Entries[(int)id];
+
+ internal static ReadOnlySpan All => Entries;
+
+ internal static string Reason(LimitId id) => id.ToString();
+
+ private static LimitEntry[] Build()
+ {
+ var all = new LimitEntry[(int)LimitId.Count];
+ void Add(LimitEntry entry) => all[(int)entry.Id] = entry;
+
+ Add(Row(LimitId.BufferedBody, "MaxBufferedBodyBytes", "server.limits.maxBufferedBodyBytes",
+ "buffered body", "bodyBudget", PolicyFamily.BodyBudget));
+ Add(Row(LimitId.HeaderCount, "MaxHeaderCount", "server.limits.maxHeaderCount",
+ "header count", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.HeaderAggregate, "MaxHeaderAggregateBytes", "server.limits.maxHeaderAggregateBytes",
+ "header block size", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.HeaderLine, "MaxHeaderLineBytes", "server.limits.maxHeaderLineBytes",
+ "header line", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.Http3FramePayload, "MaxHttp3FramePayloadBytes", "server.limits.maxHttp3FramePayloadBytes",
+ "HTTP/3 frame payload", "bodyBudget", PolicyFamily.BodyBudget));
+ Add(Row(LimitId.DeferredOutboundBytes, "MaxDeferredOutboundBytesPerStream",
+ "server.limits.maxDeferredOutboundBytesPerStream",
+ "HTTP/2 deferred DATA", "bodyBudget", PolicyFamily.BodyBudget));
+ Add(Row(LimitId.DecodedHeaderList, "MaxDecodedHeaderListBytes", "server.limits.maxDecodedHeaderListBytes",
+ "decoded header list", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.TrailerHeaderCount, "MaxTrailerHeaderCount", "server.limits.maxTrailerHeaderCount",
+ "trailer header count", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.TrailerHeaderBlock, "MaxTrailerHeaderBlockBytes", "server.limits.maxTrailerHeaderBlockBytes",
+ "trailer header block", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.Http2CompressedHeaderBlock, "MaxHttp2CompressedHeaderBlockBytes",
+ "server.limits.maxHttp2CompressedHeaderBlockBytes",
+ "compressed header block", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.InterimResponses, "MaxInterimResponses", "server.limits.maxInterimResponses",
+ "interim responses", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.AuthChallengeRounds, "MaxAuthChallengeRounds", "server.limits.maxAuthChallengeRounds",
+ "authentication challenge rounds", AdmissionControlKey, PolicyFamily.AdmissionControl));
+ Add(Row(LimitId.UpstreamProxyAuthAttempts, "MaxUpstreamProxyAuthenticationAttempts",
+ "server.limits.maxUpstreamProxyAuthenticationAttempts",
+ "upstream proxy authentication attempts", AdmissionControlKey, PolicyFamily.AdmissionControl));
+ Add(Row(LimitId.WinAuthToken, "MaxWinAuthTokenBytes", "server.limits.maxWinAuthTokenBytes",
+ "Windows authentication token", AdmissionControlKey, PolicyFamily.AdmissionControl));
+ Add(Row(LimitId.Http2WindowUpdate, "Http2WindowUpdateTimeoutSeconds",
+ "server.limits.http2WindowUpdateTimeoutSeconds",
+ "HTTP/2 flow-control wait", "http2AbuseBudget", PolicyFamily.Http2AbuseBudget));
+ Add(Row(LimitId.UpgradeHandshakeHeaders, "MaxHeaderCount", "server.limits.maxHeaderCount",
+ "upgrade handshake headers", HeaderLimitsKey, PolicyFamily.HeaderLimits));
+ Add(Row(LimitId.AdmissionGlobal, "MaxConcurrentClientConnections",
+ "server.pooling.maxConcurrentClientConnections",
+ "client connections", AdmissionControlKey, PolicyFamily.AdmissionControl));
+ Add(Row(LimitId.AdmissionEndpoint, "MaxConcurrentClients", "server.limits.maxConcurrentClients",
+ "endpoint client connections", AdmissionControlKey, PolicyFamily.AdmissionControl));
+ Add(Timeout(LimitId.TimeoutConnect, "ConnectTimeOutSeconds", "server.timeouts.connectTimeOutSeconds",
+ "connect"));
+ Add(Timeout(LimitId.TimeoutResponseHeader, "ResponseHeaderTimeoutSeconds",
+ "server.timeouts.responseHeaderTimeoutSeconds", "response header"));
+ Add(Timeout(LimitId.TimeoutIdleRead, "IdleReadTimeoutSeconds", "server.timeouts.idleReadTimeoutSeconds",
+ "idle read"));
+ Add(Timeout(LimitId.TimeoutIdleWrite, "IdleWriteTimeoutSeconds", "server.timeouts.idleWriteTimeoutSeconds",
+ "idle write"));
+ Add(Timeout(LimitId.TimeoutRequest, "RequestTimeoutSeconds", "server.timeouts.requestTimeoutSeconds",
+ "request"));
+ Add(Timeout(LimitId.TimeoutClientHeader, "ClientHeaderTimeoutSeconds",
+ "server.timeouts.clientHeaderTimeoutSeconds", "client header"));
+ Add(Row(LimitId.WebSocketFrame, "MaxWebSocketFramePayloadBytes",
+ "server.limits.maxWebSocketFramePayloadBytes",
+ "WebSocket frame", "webSocketFrameBudget", PolicyFamily.WebSocketFrameBudget));
+ return all;
+ }
+
+ private static LimitEntry Row(LimitId id, string property, string cliKey, string what, string familyKey,
+ PolicyFamily family) =>
+ new(id, property, cliKey, what, familyKey,
+ $"Raise {cliKey} or set server.policyModes.{familyKey} to observe or disabled.", family);
+
+ private static LimitEntry Timeout(LimitId id, string property, string cliKey, string what) =>
+ new(id, property, cliKey, what + " timeout", "timeouts",
+ $"Raise {cliKey}.", null);
+}
+
+///
+/// Per-limit log throttle. First event is logged immediately; later events within 10 seconds
+/// increment a suppressed count and are dropped. No locks. A suppressed call allocates nothing.
+///
+internal static class LimitLogThrottle
+{
+ internal const long WindowTicks = 10L * TimeSpan.TicksPerSecond;
+
+ private static readonly long[] LastTicks = new long[(int)LimitId.Count];
+ private static readonly int[] Suppressed = new int[(int)LimitId.Count];
+
+ ///
+ /// Returns the number of events suppressed since the previous logged line (0 on the first),
+ /// or -1 when this call must not log.
+ ///
+ internal static int TryAcquire(LimitId id)
+ {
+ var index = (int)id;
+ var now = DateTime.UtcNow.Ticks;
+ var last = Volatile.Read(ref LastTicks[index]);
+ if (last != 0 && now - last < WindowTicks)
+ {
+ Interlocked.Increment(ref Suppressed[index]);
+ return -1;
+ }
+
+ if (Interlocked.CompareExchange(ref LastTicks[index], now, last) != last)
+ {
+ Interlocked.Increment(ref Suppressed[index]);
+ return -1;
+ }
+
+ return Interlocked.Exchange(ref Suppressed[index], 0);
+ }
+
+ internal static void Reset()
+ {
+ Array.Clear(LastTicks);
+ Array.Clear(Suppressed);
+ }
+}
diff --git a/src/Titanium.Web.Proxy/Logging/ProxyDiagnostics.cs b/src/Titanium.Web.Proxy/Logging/ProxyDiagnostics.cs
index 12525eb8f..d1199da55 100644
--- a/src/Titanium.Web.Proxy/Logging/ProxyDiagnostics.cs
+++ b/src/Titanium.Web.Proxy/Logging/ProxyDiagnostics.cs
@@ -158,6 +158,10 @@ internal static bool IsExpected(Exception exception)
{
OperationCanceledException => true,
ObjectDisposedException => true,
+ // Policy rejections inherit IOException. They must stay visible at Error; a client
+ // disconnect is still the plain IOException arm below.
+ Titanium.Web.Proxy.Network.Streams.BodySizeLimitExceededException => false,
+ Titanium.Web.Proxy.Http2.Http2HeaderListTooLargeException => false,
System.IO.IOException => true,
System.Net.Sockets.SocketException => true,
// Browser/OS clients routinely abort MITM handshakes (speculative CONNECT, idle tab
diff --git a/src/Titanium.Web.Proxy/Logging/ProxyLog.cs b/src/Titanium.Web.Proxy/Logging/ProxyLog.cs
index ee1469c22..6e9c1e783 100644
--- a/src/Titanium.Web.Proxy/Logging/ProxyLog.cs
+++ b/src/Titanium.Web.Proxy/Logging/ProxyLog.cs
@@ -111,9 +111,11 @@ internal static void OriginConnectionFailed(ILogger logger, string host, int por
internal static void ClientConnectionAdmissionRejected(ILogger logger, Models.ProxyEndPoint endPoint,
string reason)
{
- if (!logger.IsEnabled(LogLevel.Warning)) return;
- logger.LogWarning("Rejected a client connection on {Endpoint} ({Reason}).",
- $"{endPoint.IpAddress}:{endPoint.Port}", reason);
+ var id = string.Equals(reason, "endpoint limit", StringComparison.Ordinal)
+ ? LimitId.AdmissionEndpoint
+ : LimitId.AdmissionGlobal;
+ LimitExceeded(logger, id, Options.PolicyMode.Enforce, 0, reason, "connection rejected",
+ $"{endPoint.IpAddress}:{endPoint.Port}");
}
///
@@ -146,11 +148,73 @@ internal static void EffectiveProfileAtStartup(ILogger logger, Options.ProxyProf
internal static void PolicyBreach(ILogger logger, Options.PolicyFamily family, Options.PolicyMode mode,
string detail)
{
- var level = mode == Options.PolicyMode.Enforce ? LogLevel.Warning : LogLevel.Debug;
+ // Enforce rejects a request, so it must be visible at the default Error minimum level.
+ var level = mode == Options.PolicyMode.Enforce ? LogLevel.Error : LogLevel.Debug;
if (!logger.IsEnabled(level)) return;
logger.Log(level, "Policy family {Family} breached under {Mode}: {Detail}", family, mode, detail);
}
+ ///
+ /// A configured limit rejected or observed a request. Enforce logs at Error, Observe at Debug.
+ /// The line names the property, the limit, the client-visible action, and the CLI key.
+ /// Logged at most once per 10 seconds per ; metrics count every call.
+ /// Never pass URLs, header values, cookies, or bodies. is a host name only.
+ ///
+ internal static void LimitExceeded(ILogger logger, LimitId id, Options.PolicyMode mode,
+ long observed, object limit, string action, string? host = null)
+ {
+ var entry = LimitCatalog.Get(id);
+ if (entry.Family is { } family)
+ global::Titanium.Web.Proxy.Diagnostics.ProxyMetrics.PolicyBreach(family, mode);
+ global::Titanium.Web.Proxy.Diagnostics.ProxyMetrics.StreamRejected(entry.FamilyKey, LimitCatalog.Reason(id));
+
+ var suppressed = LimitLogThrottle.TryAcquire(id);
+ if (suppressed < 0) return;
+
+ var level = mode == Options.PolicyMode.Enforce ? LogLevel.Error : LogLevel.Debug;
+ if (!logger.IsEnabled(level)) return;
+
+ if (suppressed > 0)
+ {
+ logger.Log(level,
+ "{Family} ({Mode}): {What} exceeded {Property}={Limit}; observed {Observed}; client got {Action}. {Hint} ({Suppressed} similar events suppressed){HostSuffix}",
+ entry.FamilyKey, mode, entry.What, entry.Property, limit, observed, action, entry.Hint,
+ suppressed, FormatHostSuffix(host));
+ return;
+ }
+
+ logger.Log(level,
+ "{Family} ({Mode}): {What} exceeded {Property}={Limit}; observed {Observed}; client got {Action}. {Hint}{HostSuffix}",
+ entry.FamilyKey, mode, entry.What, entry.Property, limit, observed, action, entry.Hint,
+ FormatHostSuffix(host));
+ }
+
+ ///
+ /// Decrypt-bypass learning changed later CONNECTs for this host to opaque tunnels.
+ /// Warning, not Error: the current request was not failed. Host name only.
+ ///
+ internal static void DecryptFailureBypassLearned(ILogger logger, string host)
+ {
+ if (!logger.IsEnabled(LogLevel.Warning)) return;
+ logger.LogWarning(
+ "EnableDecryptFailureBypass learned {Host}; later CONNECTs tunnel opaque. Set server.enableDecryptFailureBypass to false to stop learning.",
+ host);
+ }
+
+ ///
+ /// Warns when an operator set a limit the runtime still treats as reserved.
+ ///
+ internal static void ReservedLimit(ILogger logger, string cliKey, object value, string instead)
+ {
+ if (!logger.IsEnabled(LogLevel.Warning)) return;
+ logger.LogWarning(
+ "{CliKey}={Value} is reserved and not enforced. {Instead}",
+ cliKey, value, instead);
+ }
+
+ private static string FormatHostSuffix(string? host) =>
+ string.IsNullOrEmpty(host) ? string.Empty : " host " + host;
+
internal static void Http2ProbeResult(ILogger logger, string connectTarget, bool fromCache, bool supported,
Exception? failure)
{
diff --git a/src/Titanium.Web.Proxy/Network/HttpWebClient.cs b/src/Titanium.Web.Proxy/Network/HttpWebClient.cs
index 26429223c..60cec297d 100644
--- a/src/Titanium.Web.Proxy/Network/HttpWebClient.cs
+++ b/src/Titanium.Web.Proxy/Network/HttpWebClient.cs
@@ -403,7 +403,8 @@ private ValueTask ReceiveResponseAfterStatus(ResponseStatusInfo? httpStatus,
Response.StatusCode = httpStatus.Value.StatusCode;
Response.StatusDescription = httpStatus.Value.Description;
- var headersVt = HeaderParser.TryReadHeadersAsync(Connection.Stream, Response.Headers, cancellationToken);
+ var headersVt = HeaderParser.TryReadHeadersAsync(Connection.Stream, Response.Headers, cancellationToken,
+ Connection.Stream.HeaderBudget(isRequest: false));
if (headersVt.IsCompletedSuccessfully)
{
if (!headersVt.Result)
diff --git a/src/Titanium.Web.Proxy/Network/Streams/HttpStream.Body.cs b/src/Titanium.Web.Proxy/Network/Streams/HttpStream.Body.cs
index 127150807..aeed112ac 100644
--- a/src/Titanium.Web.Proxy/Network/Streams/HttpStream.Body.cs
+++ b/src/Titanium.Web.Proxy/Network/Streams/HttpStream.Body.cs
@@ -67,7 +67,8 @@ public async ValueTask CopyBodyAsync(RequestResponseBase requestResponse, bool u
: requestResponse.ContentEncoding;
Stream s = limitedStream = new LimitedStream(this, bufferPool, isChunked, contentLength,
- requestResponse.TrailingHeaders);
+ requestResponse.TrailingHeaders, server.ResourceLimits.MaxTrailerHeaderCount,
+ server.ResourceLimits.MaxTrailerHeaderBlockBytes);
if (transformation == TransformationMode.Uncompress && contentEncoding != null)
{
@@ -227,7 +228,8 @@ async Task drainRemainingChunkedBody(long remainingInCurrentChunk)
{
// discard the trailer block too - it belongs to a message we chose not to forward in full
await ChunkedTrailerHelper.ReadTrailingHeaders(this, new HeaderCollection(), null,
- cancellationToken);
+ cancellationToken, server.ResourceLimits.MaxTrailerHeaderCount,
+ server.ResourceLimits.MaxTrailerHeaderBlockBytes);
return;
}
@@ -263,7 +265,8 @@ async Task drainRemainingChunkedBody(long remainingInCurrentChunk)
// line, populating requestResponse.TrailingHeaders (writeTerminator() below
// re-emits them for `writer`). See ChunkedTrailerHelper for why this is bounded.
await ChunkedTrailerHelper.ReadTrailingHeaders(this, requestResponse.TrailingHeaders,
- null, cancellationToken);
+ null, cancellationToken, server.ResourceLimits.MaxTrailerHeaderCount,
+ server.ResourceLimits.MaxTrailerHeaderBlockBytes);
await emit(Array.Empty(), true);
break;
}
@@ -388,7 +391,8 @@ private async ValueTask CopyBodyChunkedAsync(IHttpStreamWriter writer, bool isRe
// to `writer` byte-for-byte below, rather than re-serializing the parsed HeaderCollection.
var rawTrailerLines = new List();
await ChunkedTrailerHelper.ReadTrailingHeaders(this, requestResponse.TrailingHeaders,
- rawTrailerLines, cancellationToken);
+ rawTrailerLines, cancellationToken, server.ResourceLimits.MaxTrailerHeaderCount,
+ server.ResourceLimits.MaxTrailerHeaderBlockBytes);
await ChunkedTrailerHelper.WriteRawTrailingLinesAsync(writer, rawTrailerLines, cancellationToken);
diff --git a/src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs b/src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs
index fa4dae810..e21b34dc9 100644
--- a/src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs
+++ b/src/Titanium.Web.Proxy/Network/Streams/HttpStream.cs
@@ -87,6 +87,10 @@ internal partial class HttpStream : Stream, IHttpStreamWriter, IHttpStreamReader
/// When , skips the 8 KiB rent. Use for
/// HTTP/3 session placeholders backed by that never read the client stream.
///
+ internal HeaderBlockBudget HeaderBudget(bool isRequest) =>
+ new(server.ResourceLimits.MaxHeaderCount, server.ResourceLimits.MaxHeaderAggregateBytes,
+ server.PolicyModes[PolicyFamily.HeaderLimits], isRequest);
+
internal HttpStream(ProxyServer server, Stream baseStream, IBufferPool bufferPool,
CancellationToken cancellationToken, bool leaveOpen = false, bool rentReadBuffer = true)
{
diff --git a/src/Titanium.Web.Proxy/Network/Streams/LimitedStream.cs b/src/Titanium.Web.Proxy/Network/Streams/LimitedStream.cs
index 08a641b59..2e24fe327 100644
--- a/src/Titanium.Web.Proxy/Network/Streams/LimitedStream.cs
+++ b/src/Titanium.Web.Proxy/Network/Streams/LimitedStream.cs
@@ -15,6 +15,8 @@ internal class LimitedStream : Stream
private readonly IBufferPool bufferPool;
private readonly bool isChunked;
private readonly HeaderCollection? trailingHeaders;
+ private readonly int maxTrailerHeaderCount;
+ private readonly int maxTrailerHeaderBlockBytes;
private long bytesRemaining;
private bool readChunkTrail;
@@ -29,12 +31,15 @@ internal class LimitedStream : Stream
/// populate a request/response's trailing headers the same way the pass-through relay path does.
///
internal LimitedStream(IHttpStreamReader baseStream, IBufferPool bufferPool, bool isChunked,
- long contentLength, HeaderCollection? trailingHeaders = null)
+ long contentLength, HeaderCollection? trailingHeaders = null,
+ int maxTrailerHeaderCount = 100, int maxTrailerHeaderBlockBytes = 16 * 1024)
{
baseReader = baseStream;
this.bufferPool = bufferPool;
this.isChunked = isChunked;
this.trailingHeaders = trailingHeaders;
+ this.maxTrailerHeaderCount = maxTrailerHeaderCount;
+ this.maxTrailerHeaderBlockBytes = maxTrailerHeaderBlockBytes;
if (isChunked)
bytesRemaining = 0;
else
@@ -92,7 +97,7 @@ private async Task GetNextChunkAsync()
// reading only a single line here (as before) left any additional trailer lines unread on the
// source, corrupting a pooled keep-alive connection's next message.
await ChunkedTrailerHelper.ReadTrailingHeaders(baseReader, trailingHeaders ?? new HeaderCollection(),
- null);
+ null, default, maxTrailerHeaderCount, maxTrailerHeaderBlockBytes);
}
}
diff --git a/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs
index 2f4387018..936292d10 100644
--- a/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs
+++ b/src/Titanium.Web.Proxy/Network/TcpConnection/TcpConnectionFactory.cs
@@ -22,6 +22,7 @@
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Logging;
using Titanium.Web.Proxy.Models;
+using Titanium.Web.Proxy.Options;
using Titanium.Web.Proxy.ProxySocket;
namespace Titanium.Web.Proxy.Network.Tcp;
@@ -31,7 +32,6 @@ namespace Titanium.Web.Proxy.Network.Tcp;
///
internal class TcpConnectionFactory : IDisposable
{
- private const int MaximumUpstreamProxyAuthenticationAttempts = 5;
///
/// Maximum number of upstream CONNECT rejection body bytes retained for diagnostics.
@@ -1315,6 +1315,7 @@ private static async Task EstablishHttpUpstreamConnectAsync(ProxyServer pr
var authenticationData = new InternalDataStore();
var authenticationAttempts = 0;
+ var maxAuthAttempts = proxyServer.ResourceLimits.MaxUpstreamProxyAuthenticationAttempts;
while (true)
{
@@ -1325,7 +1326,7 @@ private static async Task EstablishHttpUpstreamConnectAsync(ProxyServer pr
?? throw new IOException(
"Upstream proxy closed the connection before sending a CONNECT response.");
var headers = new HeaderCollection();
- await HeaderParser.ReadHeaders(stream, headers, cancellationToken);
+ await HeaderParser.ReadHeaders(stream, headers, cancellationToken, stream.HeaderBudget(isRequest: false));
if (httpStatus.StatusCode == (int)HttpStatusCode.OK ||
httpStatus.Description.EqualsIgnoreCase("Connection Established"))
@@ -1333,9 +1334,11 @@ private static async Task EstablishHttpUpstreamConnectAsync(ProxyServer pr
var bodyPreview = await DrainUpstreamProxyResponseBody(stream, headers, cancellationToken);
+ NoteUpstreamAuthCap(proxyServer, authenticationAttempts, maxAuthAttempts);
+
if (httpStatus.StatusCode != (int)HttpStatusCode.ProxyAuthenticationRequired ||
!proxy.UseDefaultCredentials ||
- authenticationAttempts >= MaximumUpstreamProxyAuthenticationAttempts ||
+ authenticationAttempts >= maxAuthAttempts ||
!TryGetUpstreamProxyAuthenticationChallenge(headers, out var scheme, out var challenge))
{
throw CreateUpstreamProxyConnectException(httpStatus, headers, bodyPreview);
@@ -1363,6 +1366,13 @@ private static async Task EstablishHttpUpstreamConnectAsync(ProxyServer pr
}
}
+ private static void NoteUpstreamAuthCap(ProxyServer proxyServer, int authenticationAttempts, int maxAuthAttempts)
+ {
+ if (authenticationAttempts >= maxAuthAttempts)
+ ProxyLog.LimitExceeded(proxyServer.Logger, LimitId.UpstreamProxyAuthAttempts, PolicyMode.Enforce,
+ authenticationAttempts, maxAuthAttempts, "407");
+ }
+
private static bool TryGetUpstreamProxyAuthenticationChallenge(HeaderCollection headers, out string? scheme,
out string? challenge)
{
diff --git a/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs b/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs
index 905063f58..db29f0cae 100644
--- a/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs
+++ b/src/Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs
@@ -24,7 +24,7 @@ internal class WinAuthEndPoint
///
///
internal static byte[]? AcquireInitialSecurityToken(string hostname, string authScheme, InternalDataStore data,
- int attributes, WinAuthCredentials? credentials = null)
+ int attributes, WinAuthCredentials? credentials = null, int maxTokenBytes = 0)
{
if (!RunTime.IsWindows) return null;
@@ -33,7 +33,7 @@ internal class WinAuthEndPoint
// null for initial call
var serverToken = new SecurityBufferDescription();
- var clientToken = new SecurityBufferDescription(MaximumTokenSize);
+ var clientToken = new SecurityBufferDescription(maxTokenBytes > 0 ? maxTokenBytes : MaximumTokenSize);
MarshaledAuthIdentity? authIdentity = null;
var state = new State();
var stateStored = false;
@@ -203,7 +203,7 @@ private static void ZeroAndFreeUniString(ref IntPtr ptr, int knownCharLength)
///
///
internal static byte[]? AcquireFinalSecurityToken(string hostname, byte[] serverChallenge, InternalDataStore data,
- int attributes)
+ int attributes, int maxTokenBytes = 0)
{
if (!RunTime.IsWindows) return null;
@@ -212,7 +212,7 @@ private static void ZeroAndFreeUniString(ref IntPtr ptr, int knownCharLength)
// user server challenge
var serverToken = new SecurityBufferDescription(serverChallenge);
- var clientToken = new SecurityBufferDescription(MaximumTokenSize);
+ var clientToken = new SecurityBufferDescription(maxTokenBytes > 0 ? maxTokenBytes : MaximumTokenSize);
var state = data.GetAs(AuthStateKey);
var shouldDisposeState = false;
diff --git a/src/Titanium.Web.Proxy/Network/WinAuth/WinAuthHandler.cs b/src/Titanium.Web.Proxy/Network/WinAuth/WinAuthHandler.cs
index eaad65952..abc11320a 100644
--- a/src/Titanium.Web.Proxy/Network/WinAuth/WinAuthHandler.cs
+++ b/src/Titanium.Web.Proxy/Network/WinAuth/WinAuthHandler.cs
@@ -25,10 +25,11 @@ internal static class WinAuthHandler
/// Optional alternate credentials from WinAuthCredentialsProvider.
///
internal static string GetInitialAuthToken(string serverHostname, string authScheme, InternalDataStore data,
- WinAuthCredentials? credentials = null)
+ WinAuthCredentials? credentials = null, int maxTokenBytes = 0)
{
var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(serverHostname, authScheme, data,
- IscReqConfidentiality | IscReqReplayDetect | IscReqSequenceDetect | IscReqConnection, credentials);
+ IscReqConfidentiality | IscReqReplayDetect | IscReqSequenceDetect | IscReqConnection, credentials,
+ maxTokenBytes);
if (tokenBytes == null) throw new InvalidOperationException("Failed to acquire the initial authentication token.");
return string.Concat(" ", Convert.ToBase64String(tokenBytes));
@@ -41,11 +42,13 @@ internal static string GetInitialAuthToken(string serverHostname, string authSch
///
///
///
- internal static string GetFinalAuthToken(string serverHostname, string serverToken, InternalDataStore data)
+ internal static string GetFinalAuthToken(string serverHostname, string serverToken, InternalDataStore data,
+ int maxTokenBytes = 0)
{
var tokenBytes =
WinAuthEndPoint.AcquireFinalSecurityToken(serverHostname, Convert.FromBase64String(serverToken),
- data, IscReqConfidentiality | IscReqReplayDetect | IscReqSequenceDetect | IscReqConnection);
+ data, IscReqConfidentiality | IscReqReplayDetect | IscReqSequenceDetect | IscReqConnection,
+ maxTokenBytes);
if (tokenBytes == null) throw new InvalidOperationException("Failed to acquire the final authentication token.");
return string.Concat(" ", Convert.ToBase64String(tokenBytes));
@@ -54,9 +57,11 @@ internal static string GetFinalAuthToken(string serverHostname, string serverTok
///
/// Get the initial authentication token for an upstream proxy using the current process identity.
///
- internal static string GetInitialProxyAuthToken(string proxyHostname, string authScheme, InternalDataStore data)
+ internal static string GetInitialProxyAuthToken(string proxyHostname, string authScheme, InternalDataStore data,
+ int maxTokenBytes = 0)
{
- var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(proxyHostname, authScheme, data, 0);
+ var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(proxyHostname, authScheme, data, 0,
+ maxTokenBytes: maxTokenBytes);
if (tokenBytes == null) throw new InvalidOperationException("Failed to acquire the initial proxy authentication token.");
return string.Concat(" ", Convert.ToBase64String(tokenBytes));
@@ -65,10 +70,11 @@ internal static string GetInitialProxyAuthToken(string proxyHostname, string aut
///
/// Get the response token for an upstream proxy challenge.
///
- internal static string GetFinalProxyAuthToken(string proxyHostname, string serverToken, InternalDataStore data)
+ internal static string GetFinalProxyAuthToken(string proxyHostname, string serverToken, InternalDataStore data,
+ int maxTokenBytes = 0)
{
var tokenBytes = WinAuthEndPoint.AcquireFinalSecurityToken(proxyHostname,
- Convert.FromBase64String(serverToken), data, 0);
+ Convert.FromBase64String(serverToken), data, 0, maxTokenBytes);
if (tokenBytes == null) throw new InvalidOperationException("Failed to acquire the final proxy authentication token.");
return string.Concat(" ", Convert.ToBase64String(tokenBytes));
diff --git a/src/Titanium.Web.Proxy/Options/ProxyPolicyModes.cs b/src/Titanium.Web.Proxy/Options/ProxyPolicyModes.cs
index b3e6c61c7..792e99be0 100644
--- a/src/Titanium.Web.Proxy/Options/ProxyPolicyModes.cs
+++ b/src/Titanium.Web.Proxy/Options/ProxyPolicyModes.cs
@@ -118,5 +118,12 @@ public ProxyPolicyModes WithAllowAmbiguousFramingEnabled()
return new ProxyPolicyModes(new Dictionary(modes), true);
}
+ ///
+ /// Returns a snapshot identical to this one with set explicitly.
+ /// A partial config overlay uses this so an omitted flag does not clear a value set by the profile.
+ ///
+ public ProxyPolicyModes WithAllowAmbiguousFraming(bool enabled) =>
+ new(new Dictionary(modes), enabled);
+
internal IReadOnlyDictionary AsReadOnlyDictionary() => modes;
}
diff --git a/src/Titanium.Web.Proxy/Options/ProxyResourceLimits.cs b/src/Titanium.Web.Proxy/Options/ProxyResourceLimits.cs
index 5716943e7..cb7fbb29b 100644
--- a/src/Titanium.Web.Proxy/Options/ProxyResourceLimits.cs
+++ b/src/Titanium.Web.Proxy/Options/ProxyResourceLimits.cs
@@ -141,6 +141,45 @@ private ProxyResourceLimits()
///
public int? MaxCertificateDiskCacheEntries { get; private init; }
+ /// Maximum HTTP/3 frame payload accepted before H3_EXCESSIVE_LOAD. Default 4 MiB. Ceiling 64 MiB.
+ public long MaxHttp3FramePayloadBytes { get; private init; }
+
+ ///
+ /// Bytes of HTTP/2 DATA that may be parked per stream waiting for send-window credit.
+ /// Worst-case memory is concurrent streams times this value. Default 4 MiB + 16 KiB. Ceiling 64 MiB.
+ ///
+ public int MaxDeferredOutboundBytesPerStream { get; private init; }
+
+ /// Maximum trailer header fields on one chunked message. Default 100.
+ public int MaxTrailerHeaderCount { get; private init; }
+
+ /// Maximum trailer header block size in bytes. Default 16 KiB.
+ public int MaxTrailerHeaderBlockBytes { get; private init; }
+
+ /// Maximum compressed HTTP/2 header block (HEADERS + CONTINUATION) in bytes. Default 256 KiB.
+ public int MaxHttp2CompressedHeaderBlockBytes { get; private init; }
+
+ /// Maximum interim (1xx) responses accepted from one origin before the stream fails. Default 20.
+ public int MaxInterimResponses { get; private init; }
+
+ /// Maximum Windows/proxy authentication challenge rounds per request. Default 3.
+ public int MaxAuthChallengeRounds { get; private init; }
+
+ /// Maximum CONNECT authentication attempts against an upstream proxy. Default 5.
+ public int MaxUpstreamProxyAuthenticationAttempts { get; private init; }
+
+ /// Maximum SSPI token buffer in bytes. Default 12288.
+ public int MaxWinAuthTokenBytes { get; private init; }
+
+ /// Seconds to wait for an HTTP/2 WINDOW_UPDATE before failing the stream. Default 60.
+ public int Http2WindowUpdateTimeoutSeconds { get; private init; }
+
+ /// Upper bound for .
+ public const long MaxHttp3FramePayloadCeiling = 64L * 1024 * 1024;
+
+ /// Upper bound for .
+ public const int MaxDeferredOutboundBytesCeiling = 64 * 1024 * 1024;
+
///
/// Today's shipped values, carried forward as the Balanced profile's starting point.
/// Header, decompression, CONTINUATION, and reset budgets stay high enough that ordinary
@@ -234,7 +273,17 @@ private ProxyResourceLimits()
// H1 ALPN Offer flood ~0.89×; MaxOrigin=1 SoftPick long ~0.95× H1 TLS. SoftGrow=8
// cleartext MaxOrigin=8 Offer-once regresses local H3→h2c. SoftGrow=16 Offer-once rejected.
MaxOriginHttp2ConnectionsPerAuthority = 1,
- MaxCertificateCacheEntries = maxCertificateCacheEntries
+ MaxCertificateCacheEntries = maxCertificateCacheEntries,
+ MaxHttp3FramePayloadBytes = 4 * 1024 * 1024,
+ MaxDeferredOutboundBytesPerStream = (4 * 1024 * 1024) + (16 * 1024),
+ MaxTrailerHeaderCount = 100,
+ MaxTrailerHeaderBlockBytes = 16 * 1024,
+ MaxHttp2CompressedHeaderBlockBytes = 256 * 1024,
+ MaxInterimResponses = 20,
+ MaxAuthChallengeRounds = 3,
+ MaxUpstreamProxyAuthenticationAttempts = 5,
+ MaxWinAuthTokenBytes = 12288,
+ Http2WindowUpdateTimeoutSeconds = 60
};
}
@@ -261,7 +310,17 @@ public ProxyResourceLimits WithMaxConcurrentStreamsPerConnection(int maxConcurre
MaxCachedConnectionsPerHost = MaxCachedConnectionsPerHost,
MaxOriginHttp2ConnectionsPerAuthority = MaxOriginHttp2ConnectionsPerAuthority,
MaxCertificateCacheEntries = MaxCertificateCacheEntries,
- MaxCertificateDiskCacheEntries = MaxCertificateDiskCacheEntries
+ MaxCertificateDiskCacheEntries = MaxCertificateDiskCacheEntries,
+ MaxHttp3FramePayloadBytes = MaxHttp3FramePayloadBytes,
+ MaxDeferredOutboundBytesPerStream = MaxDeferredOutboundBytesPerStream,
+ MaxTrailerHeaderCount = MaxTrailerHeaderCount,
+ MaxTrailerHeaderBlockBytes = MaxTrailerHeaderBlockBytes,
+ MaxHttp2CompressedHeaderBlockBytes = MaxHttp2CompressedHeaderBlockBytes,
+ MaxInterimResponses = MaxInterimResponses,
+ MaxAuthChallengeRounds = MaxAuthChallengeRounds,
+ MaxUpstreamProxyAuthenticationAttempts = MaxUpstreamProxyAuthenticationAttempts,
+ MaxWinAuthTokenBytes = MaxWinAuthTokenBytes,
+ Http2WindowUpdateTimeoutSeconds = Http2WindowUpdateTimeoutSeconds
};
}
@@ -288,7 +347,17 @@ public ProxyResourceLimits WithMaxOriginHttp2ConnectionsPerAuthority(int maxOrig
MaxCachedConnectionsPerHost = MaxCachedConnectionsPerHost,
MaxOriginHttp2ConnectionsPerAuthority = maxOriginHttp2ConnectionsPerAuthority,
MaxCertificateCacheEntries = MaxCertificateCacheEntries,
- MaxCertificateDiskCacheEntries = MaxCertificateDiskCacheEntries
+ MaxCertificateDiskCacheEntries = MaxCertificateDiskCacheEntries,
+ MaxHttp3FramePayloadBytes = MaxHttp3FramePayloadBytes,
+ MaxDeferredOutboundBytesPerStream = MaxDeferredOutboundBytesPerStream,
+ MaxTrailerHeaderCount = MaxTrailerHeaderCount,
+ MaxTrailerHeaderBlockBytes = MaxTrailerHeaderBlockBytes,
+ MaxHttp2CompressedHeaderBlockBytes = MaxHttp2CompressedHeaderBlockBytes,
+ MaxInterimResponses = MaxInterimResponses,
+ MaxAuthChallengeRounds = MaxAuthChallengeRounds,
+ MaxUpstreamProxyAuthenticationAttempts = MaxUpstreamProxyAuthenticationAttempts,
+ MaxWinAuthTokenBytes = MaxWinAuthTokenBytes,
+ Http2WindowUpdateTimeoutSeconds = Http2WindowUpdateTimeoutSeconds
};
}
@@ -329,8 +398,132 @@ public ProxyResourceLimits WithCertificateCacheBounds(
MaxCachedConnectionsPerHost = MaxCachedConnectionsPerHost,
MaxOriginHttp2ConnectionsPerAuthority = MaxOriginHttp2ConnectionsPerAuthority,
MaxCertificateCacheEntries = maxCertificateCacheEntries,
- MaxCertificateDiskCacheEntries = maxCertificateDiskCacheEntries
+ MaxCertificateDiskCacheEntries = maxCertificateDiskCacheEntries,
+ MaxHttp3FramePayloadBytes = MaxHttp3FramePayloadBytes,
+ MaxDeferredOutboundBytesPerStream = MaxDeferredOutboundBytesPerStream,
+ MaxTrailerHeaderCount = MaxTrailerHeaderCount,
+ MaxTrailerHeaderBlockBytes = MaxTrailerHeaderBlockBytes,
+ MaxHttp2CompressedHeaderBlockBytes = MaxHttp2CompressedHeaderBlockBytes,
+ MaxInterimResponses = MaxInterimResponses,
+ MaxAuthChallengeRounds = MaxAuthChallengeRounds,
+ MaxUpstreamProxyAuthenticationAttempts = MaxUpstreamProxyAuthenticationAttempts,
+ MaxWinAuthTokenBytes = MaxWinAuthTokenBytes,
+ Http2WindowUpdateTimeoutSeconds = Http2WindowUpdateTimeoutSeconds
+ };
+ }
+
+ /// Returns a copy with replaced.
+ public ProxyResourceLimits WithMaxHttp3FramePayloadBytes(long maxHttp3FramePayloadBytes)
+ {
+ RequireInRange(maxHttp3FramePayloadBytes, MaxHttp3FramePayloadCeiling, nameof(maxHttp3FramePayloadBytes));
+ return Copy(new HatchOverrides { MaxHttp3FramePayloadBytes = maxHttp3FramePayloadBytes });
+ }
+
+ /// Returns a copy with replaced.
+ public ProxyResourceLimits WithMaxDeferredOutboundBytesPerStream(int maxDeferredOutboundBytesPerStream)
+ {
+ RequireInRange(maxDeferredOutboundBytesPerStream, MaxDeferredOutboundBytesCeiling, nameof(maxDeferredOutboundBytesPerStream));
+ return Copy(new HatchOverrides { MaxDeferredOutboundBytesPerStream = maxDeferredOutboundBytesPerStream });
+ }
+
+ /// Returns a copy with trailer header bounds replaced.
+ public ProxyResourceLimits WithTrailerHeaderBounds(int maxTrailerHeaderCount, int maxTrailerHeaderBlockBytes)
+ {
+ RequireInRange(maxTrailerHeaderCount, 10_000, nameof(maxTrailerHeaderCount));
+ RequireInRange(maxTrailerHeaderBlockBytes, 1024 * 1024, nameof(maxTrailerHeaderBlockBytes));
+ return Copy(new HatchOverrides
+ {
+ MaxTrailerHeaderCount = maxTrailerHeaderCount,
+ MaxTrailerHeaderBlockBytes = maxTrailerHeaderBlockBytes
+ });
+ }
+
+ /// Returns a copy with replaced.
+ public ProxyResourceLimits WithMaxHttp2CompressedHeaderBlockBytes(int maxHttp2CompressedHeaderBlockBytes)
+ {
+ RequireInRange(maxHttp2CompressedHeaderBlockBytes, 16 * 1024 * 1024, nameof(maxHttp2CompressedHeaderBlockBytes));
+ return Copy(new HatchOverrides { MaxHttp2CompressedHeaderBlockBytes = maxHttp2CompressedHeaderBlockBytes });
+ }
+
+ /// Returns a copy with replaced.
+ public ProxyResourceLimits WithMaxInterimResponses(int maxInterimResponses)
+ {
+ RequireInRange(maxInterimResponses, 100, nameof(maxInterimResponses));
+ return Copy(new HatchOverrides { MaxInterimResponses = maxInterimResponses });
+ }
+
+ /// Returns a copy with authentication bounds replaced.
+ public ProxyResourceLimits WithAuthenticationBounds(int maxAuthChallengeRounds,
+ int maxUpstreamProxyAuthenticationAttempts, int maxWinAuthTokenBytes)
+ {
+ RequireInRange(maxAuthChallengeRounds, 20, nameof(maxAuthChallengeRounds));
+ RequireInRange(maxUpstreamProxyAuthenticationAttempts, 20, nameof(maxUpstreamProxyAuthenticationAttempts));
+ RequireInRange(maxWinAuthTokenBytes, 1024 * 1024, nameof(maxWinAuthTokenBytes));
+ return Copy(new HatchOverrides
+ {
+ MaxAuthChallengeRounds = maxAuthChallengeRounds,
+ MaxUpstreamProxyAuthenticationAttempts = maxUpstreamProxyAuthenticationAttempts,
+ MaxWinAuthTokenBytes = maxWinAuthTokenBytes
+ });
+ }
+
+ /// Returns a copy with replaced.
+ public ProxyResourceLimits WithHttp2WindowUpdateTimeoutSeconds(int http2WindowUpdateTimeoutSeconds)
+ {
+ RequireInRange(http2WindowUpdateTimeoutSeconds, 3600, nameof(http2WindowUpdateTimeoutSeconds));
+ return Copy(new HatchOverrides { Http2WindowUpdateTimeoutSeconds = http2WindowUpdateTimeoutSeconds });
+ }
+
+ private readonly struct HatchOverrides
+ {
+ internal long? MaxHttp3FramePayloadBytes { get; init; }
+ internal int? MaxDeferredOutboundBytesPerStream { get; init; }
+ internal int? MaxTrailerHeaderCount { get; init; }
+ internal int? MaxTrailerHeaderBlockBytes { get; init; }
+ internal int? MaxHttp2CompressedHeaderBlockBytes { get; init; }
+ internal int? MaxInterimResponses { get; init; }
+ internal int? MaxAuthChallengeRounds { get; init; }
+ internal int? MaxUpstreamProxyAuthenticationAttempts { get; init; }
+ internal int? MaxWinAuthTokenBytes { get; init; }
+ internal int? Http2WindowUpdateTimeoutSeconds { get; init; }
+ }
+
+ private ProxyResourceLimits Copy(HatchOverrides over = default) =>
+ new()
+ {
+ MaxHeaderLineBytes = MaxHeaderLineBytes,
+ MaxHeaderCount = MaxHeaderCount,
+ MaxHeaderAggregateBytes = MaxHeaderAggregateBytes,
+ MaxEncodedBodyBytes = MaxEncodedBodyBytes,
+ MaxDecodedBodyBytes = MaxDecodedBodyBytes,
+ MaxDecompressionRatio = MaxDecompressionRatio,
+ MaxConcurrentClients = MaxConcurrentClients,
+ MaxConcurrentStreamsPerConnection = MaxConcurrentStreamsPerConnection,
+ MaxPeerInitiatedIncompleteStreamResets = MaxPeerInitiatedIncompleteStreamResets,
+ MaxOpenHeaderBlockFrames = MaxOpenHeaderBlockFrames,
+ MaxOpenHeaderBlockDuration = MaxOpenHeaderBlockDuration,
+ ConnectionPoolingEnabled = ConnectionPoolingEnabled,
+ MaxCachedConnectionsPerHost = MaxCachedConnectionsPerHost,
+ MaxOriginHttp2ConnectionsPerAuthority = MaxOriginHttp2ConnectionsPerAuthority,
+ MaxCertificateCacheEntries = MaxCertificateCacheEntries,
+ MaxCertificateDiskCacheEntries = MaxCertificateDiskCacheEntries,
+ MaxHttp3FramePayloadBytes = over.MaxHttp3FramePayloadBytes ?? MaxHttp3FramePayloadBytes,
+ MaxDeferredOutboundBytesPerStream = over.MaxDeferredOutboundBytesPerStream ?? MaxDeferredOutboundBytesPerStream,
+ MaxTrailerHeaderCount = over.MaxTrailerHeaderCount ?? MaxTrailerHeaderCount,
+ MaxTrailerHeaderBlockBytes = over.MaxTrailerHeaderBlockBytes ?? MaxTrailerHeaderBlockBytes,
+ MaxHttp2CompressedHeaderBlockBytes = over.MaxHttp2CompressedHeaderBlockBytes ?? MaxHttp2CompressedHeaderBlockBytes,
+ MaxInterimResponses = over.MaxInterimResponses ?? MaxInterimResponses,
+ MaxAuthChallengeRounds = over.MaxAuthChallengeRounds ?? MaxAuthChallengeRounds,
+ MaxUpstreamProxyAuthenticationAttempts = over.MaxUpstreamProxyAuthenticationAttempts ?? MaxUpstreamProxyAuthenticationAttempts,
+ MaxWinAuthTokenBytes = over.MaxWinAuthTokenBytes ?? MaxWinAuthTokenBytes,
+ Http2WindowUpdateTimeoutSeconds = over.Http2WindowUpdateTimeoutSeconds ?? Http2WindowUpdateTimeoutSeconds
};
+
+ private static void RequireInRange(long value, long ceiling, string paramName)
+ {
+ RequirePositive(value, paramName);
+ if (value > ceiling)
+ throw new ArgumentOutOfRangeException(paramName, value, $"Value exceeds the ceiling of {ceiling}.");
}
private static void RequirePositive(long value, string paramName)
diff --git a/src/Titanium.Web.Proxy/Properties/AssemblyInfo.cs b/src/Titanium.Web.Proxy/Properties/AssemblyInfo.cs
index b62d50043..010078847 100644
--- a/src/Titanium.Web.Proxy/Properties/AssemblyInfo.cs
+++ b/src/Titanium.Web.Proxy/Properties/AssemblyInfo.cs
@@ -77,5 +77,5 @@
// file-properties version disagreed with the package it was published in. Keep both of the values
// below equal to (as Major.Minor.Build.0) whenever that property changes.
-[assembly: AssemblyVersion("7.0.18.0")]
-[assembly: AssemblyFileVersion("7.0.18.0")]
+[assembly: AssemblyVersion("7.0.19.0")]
+[assembly: AssemblyFileVersion("7.0.19.0")]
diff --git a/src/Titanium.Web.Proxy/ProxyServer.Lifecycle.cs b/src/Titanium.Web.Proxy/ProxyServer.Lifecycle.cs
index ccefc8ab3..b7841cf2a 100644
--- a/src/Titanium.Web.Proxy/ProxyServer.Lifecycle.cs
+++ b/src/Titanium.Web.Proxy/ProxyServer.Lifecycle.cs
@@ -103,6 +103,24 @@ public partial class ProxyServer : IDisposable
// Name only, per the plan's rollout section - never hosts, URLs or secrets.
ProxyLog.EffectiveProfileAtStartup(logger, profile, policyModes);
+ if (ResourceLimits.MaxDecompressionRatio is { } ratio &&
+ DecompressionRatioDiffersFromDefault(ratio))
+ {
+ ProxyLog.ReservedLimit(logger, "server.limits.maxDecompressionRatio", ratio,
+ "Body expansion is bounded by maxBufferedBodyBytes.");
+ }
+
+ if (ResourceLimits.MaxEncodedBodyBytes is { } encoded)
+ {
+ ProxyLog.ReservedLimit(logger, "server.limits.maxEncodedBodyBytes", encoded,
+ "The enforced whole-body budget is maxBufferedBodyBytes.");
+ }
+
+ if (ResourceLimits.MaxDecodedBodyBytes is { } decoded)
+ {
+ ProxyLog.ReservedLimit(logger, "server.limits.maxDecodedBodyBytes", decoded,
+ "The enforced whole-body budget is maxBufferedBodyBytes.");
+ }
_ = CertificateManager.ClearIdleCertificates();
@@ -452,4 +470,12 @@ private void DisposeOwnedLoggerFactory()
// A misbehaving sink must never prevent proxy disposal from completing.
}
}
+
+ private static bool DecompressionRatioDiffersFromDefault(double configured)
+ {
+ if (ProxyResourceLimits.Default.MaxDecompressionRatio is not double expected)
+ return true;
+
+ return Math.Abs(configured - expected) > 1e-6;
+ }
}
diff --git a/src/Titanium.Web.Proxy/ProxyServer.cs b/src/Titanium.Web.Proxy/ProxyServer.cs
index 4e56afb8d..aa0434e90 100644
--- a/src/Titanium.Web.Proxy/ProxyServer.cs
+++ b/src/Titanium.Web.Proxy/ProxyServer.cs
@@ -440,7 +440,10 @@ internal bool TryRecordDecryptFailure(string? host, Exception? error, bool force
}
if (isActive && !wasActive)
+ {
+ ProxyLog.DecryptFailureBypassLearned(logger, host);
RaiseDecryptFailureBypassChanged(host);
+ }
return isActive;
}
@@ -474,7 +477,10 @@ internal bool TryRecordDecryptFailureFromHttpStatus(string? host, int statusCode
}
if (isActive && !wasActive)
+ {
+ ProxyLog.DecryptFailureBypassLearned(logger, host);
RaiseDecryptFailureBypassChanged(host);
+ }
return isActive;
}
diff --git a/src/Titanium.Web.Proxy/PublicAPI.Unshipped.txt b/src/Titanium.Web.Proxy/PublicAPI.Unshipped.txt
index c8c3f66e3..efce5c80e 100644
--- a/src/Titanium.Web.Proxy/PublicAPI.Unshipped.txt
+++ b/src/Titanium.Web.Proxy/PublicAPI.Unshipped.txt
@@ -281,3 +281,29 @@ Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.IsFinal.get -> b
Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.OpCode.get -> Titanium.Web.Proxy.WebsocketOpCode
Titanium.Web.Proxy.EventArguments.WebSocketFrameElidedEventArgs.Session.get -> Titanium.Web.Proxy.EventArguments.SessionEventArgs!
Titanium.Web.Proxy.Options.PolicyFamily.WebSocketFrameBudget = 7 -> Titanium.Web.Proxy.Options.PolicyFamily
+const Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesCeiling = 67108864 -> int
+const Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadCeiling = 67108864 -> long
+Titanium.Web.Proxy.Options.ProxyResourceLimits.Http2WindowUpdateTimeoutSeconds.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxAuthChallengeRounds.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxDeferredOutboundBytesPerStream.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp2CompressedHeaderBlockBytes.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxHttp3FramePayloadBytes.get -> long
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxInterimResponses.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderBlockBytes.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxTrailerHeaderCount.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxUpstreamProxyAuthenticationAttempts.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.MaxWinAuthTokenBytes.get -> int
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithAuthenticationBounds(int maxAuthChallengeRounds, int maxUpstreamProxyAuthenticationAttempts, int maxWinAuthTokenBytes) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithHttp2WindowUpdateTimeoutSeconds(int http2WindowUpdateTimeoutSeconds) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxDeferredOutboundBytesPerStream(int maxDeferredOutboundBytesPerStream) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp2CompressedHeaderBlockBytes(int maxHttp2CompressedHeaderBlockBytes) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxHttp3FramePayloadBytes(long maxHttp3FramePayloadBytes) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithMaxInterimResponses(int maxInterimResponses) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyResourceLimits.WithTrailerHeaderBounds(int maxTrailerHeaderCount, int maxTrailerHeaderBlockBytes) -> Titanium.Web.Proxy.Options.ProxyResourceLimits!
+Titanium.Web.Proxy.Options.ProxyPolicyModes.WithAllowAmbiguousFraming(bool enabled) -> Titanium.Web.Proxy.Options.ProxyPolicyModes!
+Titanium.Web.Proxy.Http.HeaderLimitsExceededException
+Titanium.Web.Proxy.Http.HeaderLimitsExceededException.HeaderLimitsExceededException(bool isCount, long observed, long limit, bool isRequest) -> void
+Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsCount.get -> bool
+Titanium.Web.Proxy.Http.HeaderLimitsExceededException.IsRequest.get -> bool
+Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Limit.get -> long
+Titanium.Web.Proxy.Http.HeaderLimitsExceededException.Observed.get -> long
diff --git a/src/Titanium.Web.Proxy/Routing/ReverseProxySessionDispatch.cs b/src/Titanium.Web.Proxy/Routing/ReverseProxySessionDispatch.cs
index 0285d51e0..4a0a8f5b9 100644
--- a/src/Titanium.Web.Proxy/Routing/ReverseProxySessionDispatch.cs
+++ b/src/Titanium.Web.Proxy/Routing/ReverseProxySessionDispatch.cs
@@ -58,6 +58,7 @@ destination is null ||
session.UpstreamConnectHost = destination.Address;
session.UpstreamConnectPort = port;
session.UpstreamDestinationId = destination.Id;
+ StreamDestinationDispatch.ApplyRouteLimits(route, session);
if (options.LoadBalancer is LoadBalancer lb)
{
diff --git a/src/Titanium.Web.Proxy/Routing/StreamDestinationDispatch.cs b/src/Titanium.Web.Proxy/Routing/StreamDestinationDispatch.cs
index 01af38e56..93fbefb43 100644
--- a/src/Titanium.Web.Proxy/Routing/StreamDestinationDispatch.cs
+++ b/src/Titanium.Web.Proxy/Routing/StreamDestinationDispatch.cs
@@ -2,6 +2,7 @@
using Titanium.Web.Proxy.Abstractions.Clusters;
using Titanium.Web.Proxy.Abstractions.Routing;
using Titanium.Web.Proxy.Clusters;
+using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
@@ -63,6 +64,25 @@ public static bool TrySelect(
return true;
}
+ ///
+ /// Applies when the route declares them. A null block is one reference
+ /// compare and does not touch the session.
+ ///
+ internal static void ApplyRouteLimits(RouteConfig route, SessionEventArgsBase session)
+ {
+ var limits = route.Limits;
+ if (limits is null || session is not SessionEventArgs args)
+ return;
+ if (limits.RequestTimeoutSeconds is int requestTimeout)
+ args.RequestTimeout = TimeSpan.FromSeconds(requestTimeout);
+ if (limits.IdleTimeoutSeconds is int idleTimeout)
+ args.IdleReadTimeout = TimeSpan.FromSeconds(idleTimeout);
+ if (limits.MaxBufferedBodyBytes is int body)
+ args.MaxBufferedBodyBytes = body;
+ if (limits.MaxWebSocketFramePayloadBytes is int frame)
+ args.MaxWebSocketFramePayloadBytes = frame;
+ }
+
///
/// Idempotent GET/HEAD connect retry to the next eligible destination after a failure.
///
diff --git a/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj b/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
index 9a2cce5e1..1a55fe166 100644
--- a/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+++ b/src/Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
@@ -13,7 +13,7 @@
- 7.0.18
+ 7.0.19