281 lines
11 KiB
C#
281 lines
11 KiB
C#
// Copyright (c) Microsoft Open Technologies, Inc.
|
|
// All Rights Reserved
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR
|
|
// CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
|
// WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
|
|
// TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR
|
|
// NON-INFRINGEMENT.
|
|
// See the Apache 2 License for the specific language governing
|
|
// permissions and limitations under the License.
|
|
|
|
// -----------------------------------------------------------------------
|
|
// <copyright file="TimeoutManager.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// -----------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Microsoft.Net.Http.Server
|
|
{
|
|
// See the native HTTP_TIMEOUT_LIMIT_INFO structure documentation for additional information.
|
|
// http://msdn.microsoft.com/en-us/library/aa364661.aspx
|
|
|
|
/// <summary>
|
|
/// Exposes the Http.Sys timeout configurations. These may also be configured in the registry.
|
|
/// </summary>
|
|
public sealed class TimeoutManager
|
|
{
|
|
private static readonly int TimeoutLimitSize =
|
|
Marshal.SizeOf<UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_LIMIT_INFO>();
|
|
|
|
private WebListener _server;
|
|
private int[] _timeouts;
|
|
private uint _minSendBytesPerSecond;
|
|
|
|
internal TimeoutManager(WebListener listener)
|
|
{
|
|
_server = listener;
|
|
|
|
// 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();
|
|
}
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// 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 indicate that system defaults should be used.
|
|
/// </summary>
|
|
public TimeSpan EntityBody
|
|
{
|
|
get
|
|
{
|
|
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.EntityBody);
|
|
}
|
|
set
|
|
{
|
|
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.EntityBody, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 indicate that system defaults should be used.
|
|
/// </summary>
|
|
public TimeSpan DrainEntityBody
|
|
{
|
|
get
|
|
{
|
|
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.DrainEntityBody);
|
|
}
|
|
set
|
|
{
|
|
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.DrainEntityBody, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 indicate that system defaults should be used.
|
|
/// </summary>
|
|
public TimeSpan RequestQueue
|
|
{
|
|
get
|
|
{
|
|
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.RequestQueue);
|
|
}
|
|
set
|
|
{
|
|
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.RequestQueue, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 indicate that system defaults should be used.
|
|
/// </summary>
|
|
public TimeSpan IdleConnection
|
|
{
|
|
get
|
|
{
|
|
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.IdleConnection);
|
|
}
|
|
set
|
|
{
|
|
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.IdleConnection, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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 indicate that system defaults should be used.
|
|
/// </summary>
|
|
public TimeSpan HeaderWait
|
|
{
|
|
get
|
|
{
|
|
return GetTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.HeaderWait);
|
|
}
|
|
set
|
|
{
|
|
SetTimespanTimeout(UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.HeaderWait, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
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");
|
|
}
|
|
|
|
SetServerTimeout(_timeouts, (uint)value);
|
|
_minSendBytesPerSecond = (uint)value;
|
|
}
|
|
}
|
|
|
|
#endregion Properties
|
|
|
|
// Initial values come from the config. The values can then be overridden using this public API.
|
|
private void LoadConfigurationSettings()
|
|
{
|
|
long[] configTimeouts = new long[_timeouts.Length + 1]; // 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)
|
|
{
|
|
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;
|
|
SetServerTimeout(currentTimeouts, _minSendBytesPerSecond);
|
|
_timeouts[(int)type] = (int)timeoutValue;
|
|
}
|
|
|
|
private unsafe void SetServerTimeout(int[] timeouts, uint minSendBytesPerSecond)
|
|
{
|
|
UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_LIMIT_INFO timeoutinfo =
|
|
new UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_LIMIT_INFO();
|
|
|
|
timeoutinfo.Flags = UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
|
|
timeoutinfo.DrainEntityBody =
|
|
(ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.DrainEntityBody];
|
|
timeoutinfo.EntityBody =
|
|
(ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.EntityBody];
|
|
timeoutinfo.RequestQueue =
|
|
(ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.RequestQueue];
|
|
timeoutinfo.IdleConnection =
|
|
(ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.IdleConnection];
|
|
timeoutinfo.HeaderWait =
|
|
(ushort)timeouts[(int)UnsafeNclNativeMethods.HttpApi.HTTP_TIMEOUT_TYPE.HeaderWait];
|
|
timeoutinfo.MinSendRate = minSendBytesPerSecond;
|
|
|
|
IntPtr infoptr = new IntPtr(&timeoutinfo);
|
|
|
|
_server.SetUrlGroupProperty(
|
|
UnsafeNclNativeMethods.HttpApi.HTTP_SERVER_PROPERTY.HttpServerTimeoutsProperty,
|
|
infoptr, (uint)TimeoutLimitSize);
|
|
}
|
|
|
|
#endregion Helpers
|
|
}
|
|
}
|