|
using System.Diagnostics;
using System.Net.Configuration;
namespace System.Net {
//
// See the native HTTP_TIMEOUT_LIMIT_INFO structure documentation for additional information.
// http://msdn.microsoft.com/en-us/library/aa364661.aspx
//
public class HttpListenerTimeoutManager
{
private HttpListener listener;
private int[] timeouts;
private uint minSendBytesPerSecond;
internal HttpListenerTimeoutManager(HttpListener context)
{
listener = context;
//
// We have to maintain local state since we allow applications to set individual timeouts. Native Http
// API for setting timeouts expects all timeout values in every call so we have remember timeout values
// to fill in the blanks. Except MinSendBytesPerSecond, local state for remaining five timeouts is
// maintained in timeouts array.
//
// No initialization is required because a value of zero indicates that system defaults should be used.
timeouts = new int[5];
LoadConfigurationSettings();
}
// Initial values come from the config. The values can then be overridden using this public API.
private void LoadConfigurationSettings()
{
long[] configTimeouts = SettingsSectionInternal.Section.HttpListenerTimeouts;
Debug.Assert(configTimeouts != null);
Debug.Assert(configTimeouts.Length == (timeouts.Length + 1));
bool setNonDefaults = false;
for (int i = 0; i < timeouts.Length; i++)
{
if (configTimeouts[i] != 0)
{
Debug.Assert(configTimeouts[i] <= ushort.MaxValue, "Timeout out of range: " + configTimeouts[i]);
timeouts[i] = (int)configTimeouts[i];
setNonDefaults = true;
}
}
if (configTimeouts[5] != 0)
{
Debug.Assert(configTimeouts[5] <= uint.MaxValue, "Timeout out of range: " + configTimeouts[5]);
minSendBytesPerSecond = (uint)configTimeouts[5];
setNonDefaults = true;
}
if (setNonDefaults)
{
listener.SetServerTimeout(timeouts, minSendBytesPerSecond);
}
}
#region Helpers
private TimeSpan GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE type)
{
//
// Since we maintain local state, GET is local.
//
return new TimeSpan(0, 0, (int)timeouts[(int)type]);
}
private void SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE type, TimeSpan value)
{
Int64 timeoutValue;
//
// All timeouts are defined as USHORT in native layer (except MinSendRate, which is ULONG). Make sure that
// timeout value is within range.
//
timeoutValue = Convert.ToInt64(value.TotalSeconds);
if (timeoutValue < 0 || timeoutValue > ushort.MaxValue)
{
throw new ArgumentOutOfRangeException("value");
}
//
// Use local state to get values for other timeouts. Call into the native layer and if that
// call succeeds, update local state.
//
int[] currentTimeouts = timeouts;
currentTimeouts[(int)type] = (int)timeoutValue;
listener.SetServerTimeout(currentTimeouts, minSendBytesPerSecond);
timeouts[(int)type] = (int)timeoutValue;
}
#endregion Helpers
#region Properties
// The time, in seconds, allowed for the request entity body to arrive. The default timer is 2 minutes.
//
// The HTTP Server API turns on this timer when the request has an entity body. The timer expiration is
// initially set to the configured value. When the HTTP Server API receives additional data indications on the
// request, it resets the timer to give the connection another interval.
//
// Use TimeSpan.Zero to indiate that system defaults should be used.
public TimeSpan EntityBody
{
get
{
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.EntityBody);
}
set
{
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.EntityBody, value);
}
}
// The time, in seconds, allowed for the HTTP Server API to drain the entity body on a Keep-Alive connection.
// The default timer is 2 minutes.
//
// On a Keep-Alive connection, after the application has sent a response for a request and before the request
// entity body has completely arrived, the HTTP Server API starts draining the remainder of the entity body to
// reach another potentially pipelined request from the client. If the time to drain the remaining entity body
// exceeds the allowed period the connection is timed out.
//
// Use TimeSpan.Zero to indiate that system defaults should be used.
public TimeSpan DrainEntityBody
{
get
{
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.DrainEntityBody);
}
set
{
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.DrainEntityBody, value);
}
}
// The time, in seconds, allowed for the request to remain in the request queue before the application picks
// it up. The default timer is 2 minutes.
//
// Use TimeSpan.Zero to indiate that system defaults should be used.
public TimeSpan RequestQueue
{
get
{
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.RequestQueue);
}
set
{
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.RequestQueue, value);
}
}
// The time, in seconds, allowed for an idle connection. The default timer is 2 minutes.
//
// This timeout is only enforced after the first request on the connection is routed to the application.
//
// Use TimeSpan.Zero to indiate that system defaults should be used.
public TimeSpan IdleConnection
{
get
{
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.IdleConnection);
}
set
{
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.IdleConnection, value);
}
}
// The time, in seconds, allowed for the HTTP Server API to parse the request header. The default timer is
// 2 minutes.
//
// This timeout is only enforced after the first request on the connection is routed to the application.
//
// Use TimeSpan.Zero to indiate that system defaults should be used.
public TimeSpan HeaderWait
{
get
{
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.HeaderWait);
}
set
{
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.HeaderWait, value);
}
}
// The minimum send rate, in bytes-per-second, for the response. The default response send rate is 150
// bytes-per-second.
//
// To disable this timer set it to UInt32.MaxValue
public long MinSendBytesPerSecond
{
get
{
//
// Since we maintain local state, GET is local.
//
return minSendBytesPerSecond;
}
set
{
//
// MinSendRate value is ULONG in native layer.
//
if (value < 0 || value > uint.MaxValue)
{
throw new ArgumentOutOfRangeException("value");
}
listener.SetServerTimeout(timeouts, (uint)value);
minSendBytesPerSecond = (uint)value;
}
}
#endregion Properties
}
}
|