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.CompleteParseAndEnqueue
void 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.CancelRequestInitializationTimer
protected 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.OnRequestInitializationTimeout
static void OnRequestInitializationTimeout(object obj)
{
Fx.Assert(obj != null, "obj should not be null.");
HttpPipeline thisPtr = (HttpPipeline)obj;
thisPtr.Cancel();
}
HttpPipeline.Cancel
public 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.InitializeHttpPipeline
public 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>.ProcessHttpContextAsync
AsyncCompletionResult 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