What is the HttpTransportElement's RequestInitializationTimeout about? MSDN says:
Gets or sets the timespan that specifies how long the request initialization has to complete before timing out.
What exactly is "request initialization"? Why might I want to set this property?
There is no clear documentation regarding RequestInitializationTimeout.
There is a comment inside the source code of SharedHttpTransportManager.IsCompatible regarding RequestInitializationTimeout checking:
// We are NOT checking the RequestInitializationTimeout here since the HttpChannelListener should be handle them // individually. However, some of the scenarios might be impacted, e.g., if we have one endpoint with high RequestInitializationTimeout // and the other is just normal, the first endpoint might be occupying all the receiving loops, then the requests to the normal endpoint // will experience timeout issues. The mitigation for this issue is that customers should be able to increase the MaxPendingAccepts number.
If we check the TimeoutException that it assigns to the requestException that is passed along with the HttpRequestContext.SetMessage(message, requestException) inside the OnParseComplete, we can see:
new TimeoutException(SR.GetString(
SR.RequestInitializationTimeoutReached,
this.HttpRequestContext.Listener.RequestInitializationTimeout,
"RequestInitializationTimeout",
typeof(HttpTransportBindingElement).Name))
and the regarding SR.RequestInitializationTimeoutReached System.Private.ServiceModel exception message is:
The initialization process of the request message timed out after {0}. To increase this quota, use the '{1}' property on the '{2}'.
that will be formatted to something like this:
The initialization process of the request message timed out after 00:00:10. To increase this quota, use the 'RequestInitializationTimeout' property on the 'HttpTransportBindingElement'.
If we follow the source code, we can find it's usage inside EmptyHttpPipeline.
EmptyHttpPipeline.EmptyHttpPipeline
If there is a RequestInitializationTimeout TimeSpan (and not the default 00:00:00 that disables it), an IOThreadTimer is set with the dueTime set to the TimeSpan ticks, the OnRequestInitializationTimeout Action callback and this (the actual EmptyHttpPipeline object) as the callbackState.
if (this.httpRequestContext.Listener.RequestInitializationTimeout != HttpTransportDefaults.RequestInitializationTimeout)
{
this.requestInitializationTimer = new IOThreadTimer(onRequestInitializationTimeout, this, false);
this.requestInitializationTimer.Set(this.httpRequestContext.Listener.RequestInitializationTimeout);
}
It's checked when the EmptyHttpPipeline.OnParseComplete method is being called by trying to cancel the requestInitializationTimer. If there is a timer and it's being previously canceled or overdue, it creates a TimeoutException that it assigns to the requestException that it's passed to the HttpPipeline.HttpRequestContext.SetMessage along with the message:
protected override void OnParseComplete(Message message, Exception requestException)
{
if (!this.CancelRequestInitializationTimer() && requestException == null)
{
requestException = FxTrace.Exception.AsError(new TimeoutException(SR.GetString(
SR.RequestInitializationTimeoutReached,
this.HttpRequestContext.Listener.RequestInitializationTimeout,
"RequestInitializationTimeout",
typeof(HttpTransportBindingElement).Name)));
}
this.HttpRequestContext.SetMessage(message, requestException);
}
The OnParseComplete method is being called inside EnqueueMessageAsyncResult.CompleteParseAndEnqueue using the HandleParseIncomingMessage AsyncCallback inside the EnqueueMessageAsyncResult.End method. The EnqueueMessageAsyncResult object is created when invoking EmptyHttpPipeline.BeginProcessInboundRequest and it terminates inside EmptyHttpPipeline.EndProcessInboundRequest.
BeginProcessInboundRequest and EndProcessInboundRequest
internal override IAsyncResult BeginProcessInboundRequest(
ReplyChannelAcceptor replyChannelAcceptor,
Action dequeuedCallback,
AsyncCallback callback,
object state)
{
this.TraceBeginProcessInboundRequestStart();
return new EnqueueMessageAsyncResult(replyChannelAcceptor, dequeuedCallback, this, callback, state);
}
internal override void EndProcessInboundRequest(IAsyncResult result)
{
// It will trigger HandleParseIncomingMessage AsyncCallback,
// that will call CompleteParseAndEnqueue
EnqueueMessageAsyncResult.End(result);
this.TraceProcessInboundRequestStop();
}
EnqueueMessageAsyncResult.CompleteParseAndEnqueuevoid CompleteParseAndEnqueue(IAsyncResult result)
{
using (DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.BoundOperation(this.CallbackActivity) : null)
{
Exception requestException;
Message message = this.pipeline.EndParseIncomingMesssage(result, out requestException);
if ((message == null) && (requestException == null))
{
throw FxTrace.Exception.AsError(
new ProtocolException(
SR.GetString(SR.MessageXmlProtocolError),
new XmlException(SR.GetString(SR.MessageIsEmpty))));
}
// Here the EmptyHttpPipeline.OnParseComplete is being invoked
this.pipeline.OnParseComplete(message, requestException);
this.acceptor.Enqueue(this.pipeline.HttpRequestContext, this.dequeuedCallback, true);
}
}
It's being checked using the CancelRequestInitializationTimer method and if there is no timer set, it just returns true; if it's previously canceled it returns false, else it calls IOThreadTimer.Cancel method to check and return true if it's canceled or false if the timer is overdue.
EmptyHttpPipeline.CancelRequestInitializationTimerprotected bool CancelRequestInitializationTimer()
{
if (this.requestInitializationTimer == null)
{
return true;
}
if (this.requestInitializationTimerCancelled)
{
return false;
}
bool result = this.requestInitializationTimer.Cancel();
this.requestInitializationTimerCancelled = true;
return result;
}
Inside the OnRequestInitializationTimeout method, we can see that it cancels the HttpPipeline which aborts the HttpPipeline.httpRequestContext:
EmptyHttpPipeline.OnRequestInitializationTimeoutstatic void OnRequestInitializationTimeout(object obj)
{
Fx.Assert(obj != null, "obj should not be null.");
HttpPipeline thisPtr = (HttpPipeline)obj;
thisPtr.Cancel();
}
HttpPipeline.Cancelpublic virtual void Cancel()
{
this.httpRequestContext.Abort();
}
EmptyHttpPipeline object?We get an EmptyHttpPipeline if we set transportIntegrationHandler = null using the static HttpPipeline.CreateHttpPipeline that accepts a TransportIntegrationHandler and there is no HttpRequestContext.HttpMessagesSupported:
public static HttpPipeline CreateHttpPipeline(HttpRequestContext httpRequestContext, TransportIntegrationHandler transportIntegrationHandler, bool isWebSocketTransport)
{
if (transportIntegrationHandler == null)
{
Fx.Assert(!isWebSocketTransport, "isWebSocketTransport should be false if there's no HTTP message handler existing.");
if (httpRequestContext.HttpMessagesSupported)
{
return new HttpMessageSupportedHttpPipeline(httpRequestContext);
}
return new EmptyHttpPipeline(httpRequestContext);
}
return NormalHttpPipeline.CreatePipeline(httpRequestContext, transportIntegrationHandler, isWebSocketTransport);
}
The CreateHttpPipeline is called on HttpRequestContext.InitializeHttpPipeline:
HttpRequestContext.InitializeHttpPipelinepublic void InitializeHttpPipeline(TransportIntegrationHandler transportIntegrationHandler)
{
this.httpPipeline = HttpPipeline.CreateHttpPipeline(this, transportIntegrationHandler, this.IsWebSocketRequest);
}
which is called on HttpRequestContext requestContext (passed in HttpContextReceivedAsyncResult constructor) inside HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsync using HttpChannelListener<TListenerChannel>.transportIntegrationHandler
HttpContextReceivedAsyncResult<TListenerChannel>.ProcessHttpContextAsyncAsyncCompletionResult ProcessHttpContextAsync()
{
bool abort = false;
try
{
this.context.InitializeHttpPipeline(this.listener.transportIntegrationHandler);
...
}
...
}
All these leave us with the conclusion that when a HttpPipeline is created without a TransportIntegrationHandler and no HttpMessagesSupported for the HttpRequestContext, then the new EmptyHttpPipeline object will have a timeout (if RequestInitializationTimeout is set) for BeginProcessInboundRequest/EndProcessInboundRequest and set an exception when setting the message using EmptyHttpPipeline.HttpRequestContext.SetMessage inside EmptyHttpPipeline.OnParseComplete.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With