aspnetcore/src/Microsoft.Net.Http.Server/NativeInterop/RequestQueue.cs

136 lines
4.8 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.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace Microsoft.Net.Http.Server
{
internal class RequestQueue
{
private static readonly int BindingInfoSize =
Marshal.SizeOf<HttpApi.HTTP_BINDING_INFO>();
private readonly UrlGroup _urlGroup;
private readonly ILogger _logger;
private bool _disposed;
internal RequestQueue(UrlGroup urlGroup, ILogger logger)
{
_urlGroup = urlGroup;
_logger = logger;
HttpRequestQueueV2Handle requestQueueHandle = null;
var statusCode = HttpApi.HttpCreateRequestQueue(
HttpApi.Version, null, null, 0, out requestQueueHandle);
if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
{
throw new WebListenerException((int)statusCode);
}
// Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS)
if (WebListener.SkipIOCPCallbackOnSuccess &&
!UnsafeNclNativeMethods.SetFileCompletionNotificationModes(
requestQueueHandle,
UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipCompletionPortOnSuccess |
UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipSetEventOnHandle))
{
throw new WebListenerException(Marshal.GetLastWin32Error());
}
Handle = requestQueueHandle;
BoundHandle = ThreadPoolBoundHandle.BindHandle(Handle);
}
internal SafeHandle Handle { get; }
internal ThreadPoolBoundHandle BoundHandle { get; }
internal unsafe void AttachToUrlGroup()
{
CheckDisposed();
// Set the association between request queue and url group. After this, requests for registered urls will
// get delivered to this request queue.
var info = new HttpApi.HTTP_BINDING_INFO();
info.Flags = HttpApi.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
info.RequestQueueHandle = Handle.DangerousGetHandle();
var infoptr = new IntPtr(&info);
_urlGroup.SetProperty(HttpApi.HTTP_SERVER_PROPERTY.HttpServerBindingProperty,
infoptr, (uint)BindingInfoSize);
}
internal unsafe void DetachFromUrlGroup()
{
CheckDisposed();
// Break the association between request queue and url group. After this, requests for registered urls
// will get 503s.
// Note that this method may be called multiple times (Stop() and then Abort()). This
// is fine since http.sys allows to set HttpServerBindingProperty multiple times for valid
// Url groups.
var info = new HttpApi.HTTP_BINDING_INFO();
info.Flags = HttpApi.HTTP_FLAGS.NONE;
info.RequestQueueHandle = IntPtr.Zero;
var infoptr = new IntPtr(&info);
_urlGroup.SetProperty(HttpApi.HTTP_SERVER_PROPERTY.HttpServerBindingProperty,
infoptr, (uint)BindingInfoSize, throwOnError: false);
}
// The listener must be active for this to work.
internal unsafe void SetLengthLimit(long length)
{
CheckDisposed();
var result = HttpApi.HttpSetRequestQueueProperty(Handle,
HttpApi.HTTP_SERVER_PROPERTY.HttpServerQueueLengthProperty,
new IntPtr((void*)&length), (uint)Marshal.SizeOf<long>(), 0, IntPtr.Zero);
if (result != 0)
{
throw new WebListenerException((int)result);
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
BoundHandle.Dispose();
Handle.Dispose();
}
private void CheckDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
}
}
}