// 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(); 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(), 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); } } } }