Example #1
0
        /*
         * 12.3
         * HttpSendHttpResponse() and HttpSendResponseEntityBody() Flag Values.
         * The following flags can be used on calls to HttpSendHttpResponse() and HttpSendResponseEntityBody() API calls:
         *
         #define HTTP_SEND_RESPONSE_FLAG_DISCONNECT          0x00000001
         #define HTTP_SEND_RESPONSE_FLAG_MORE_DATA           0x00000002
         #define HTTP_SEND_RESPONSE_FLAG_RAW_HEADER          0x00000004
         #define HTTP_SEND_RESPONSE_FLAG_VALID               0x00000007
         *
         * HTTP_SEND_RESPONSE_FLAG_DISCONNECT:
         *  specifies that the network connection should be disconnected immediately after
         *  sending the response, overriding the HTTP protocol's persistent connection features.
         * HTTP_SEND_RESPONSE_FLAG_MORE_DATA:
         *  specifies that additional entity body data will be sent by the caller. Thus,
         *  the last call HttpSendResponseEntityBody for a RequestId, will have this flag reset.
         * HTTP_SEND_RESPONSE_RAW_HEADER:
         *  specifies that a caller of HttpSendResponseEntityBody() is intentionally omitting
         *  a call to HttpSendHttpResponse() in order to bypass normal header processing. The
         *  actual HTTP header will be generated by the application and sent as entity body.
         *  This flag should be passed on the first call to HttpSendResponseEntityBody, and
         *  not after. Thus, flag is not applicable to HttpSendHttpResponse.
         */

        // TODO: Consider using HTTP_SEND_RESPONSE_RAW_HEADER with HttpSendResponseEntityBody instead of calling HttpSendHttpResponse.
        // This will give us more control of the bytes that hit the wire, including encodings, HTTP 1.0, etc..
        // It may also be faster to do this work in managed code and then pass down only one buffer.
        // What would we loose by bypassing HttpSendHttpResponse?
        //
        // TODO: Consider using the HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA flag for most/all responses rather than just Opaque.
        internal unsafe uint SendHeaders(HttpApiTypes.HTTP_DATA_CHUNK[] dataChunks,
                                         ResponseStreamAsyncResult asyncResult,
                                         HttpApiTypes.HTTP_FLAGS flags,
                                         bool isOpaqueUpgrade)
        {
            Debug.Assert(!HasStarted, "HttpListenerResponse::SendHeaders()|SentHeaders is true.");

            _responseState = ResponseState.Started;
            var reasonPhrase = GetReasonPhrase(StatusCode);

            uint            statusCode;
            uint            bytesSent;
            List <GCHandle> pinnedHeaders = SerializeHeaders(isOpaqueUpgrade);

            try
            {
                if (dataChunks != null)
                {
                    if (pinnedHeaders == null)
                    {
                        pinnedHeaders = new List <GCHandle>();
                    }
                    var handle = GCHandle.Alloc(dataChunks, GCHandleType.Pinned);
                    pinnedHeaders.Add(handle);
                    _nativeResponse.Response_V1.EntityChunkCount = (ushort)dataChunks.Length;
                    _nativeResponse.Response_V1.pEntityChunks    = (HttpApiTypes.HTTP_DATA_CHUNK *)handle.AddrOfPinnedObject();
                }
                else if (asyncResult != null && asyncResult.DataChunks != null)
                {
                    _nativeResponse.Response_V1.EntityChunkCount = asyncResult.DataChunkCount;
                    _nativeResponse.Response_V1.pEntityChunks    = asyncResult.DataChunks;
                }
                else
                {
                    _nativeResponse.Response_V1.EntityChunkCount = 0;
                    _nativeResponse.Response_V1.pEntityChunks    = null;
                }

                var cachePolicy = new HttpApiTypes.HTTP_CACHE_POLICY();
                if (_cacheTtl.HasValue && _cacheTtl.Value > TimeSpan.Zero)
                {
                    cachePolicy.Policy        = HttpApiTypes.HTTP_CACHE_POLICY_TYPE.HttpCachePolicyTimeToLive;
                    cachePolicy.SecondsToLive = (uint)Math.Min(_cacheTtl.Value.Ticks / TimeSpan.TicksPerSecond, Int32.MaxValue);
                }

                byte[] reasonPhraseBytes = HeaderEncoding.GetBytes(reasonPhrase);
                fixed(byte *pReasonPhrase = reasonPhraseBytes)
                {
                    _nativeResponse.Response_V1.ReasonLength = (ushort)reasonPhraseBytes.Length;
                    _nativeResponse.Response_V1.pReason      = (byte *)pReasonPhrase;
                    fixed(HttpApiTypes.HTTP_RESPONSE_V2 *pResponse = &_nativeResponse)
                    {
                        statusCode =
                            HttpApi.HttpSendHttpResponse(
                                RequestContext.Server.RequestQueue.Handle,
                                Request.RequestId,
                                (uint)flags,
                                pResponse,
                                &cachePolicy,
                                &bytesSent,
                                IntPtr.Zero,
                                0,
                                asyncResult == null ? SafeNativeOverlapped.Zero : asyncResult.NativeOverlapped,
                                IntPtr.Zero);

                        // GoAway is only supported on later versions. Retry.
                        if (statusCode == ErrorCodes.ERROR_INVALID_PARAMETER &&
                            (flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY) != 0)
                        {
                            flags     &= ~HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_GOAWAY;
                            statusCode =
                                HttpApi.HttpSendHttpResponse(
                                    RequestContext.Server.RequestQueue.Handle,
                                    Request.RequestId,
                                    (uint)flags,
                                    pResponse,
                                    &cachePolicy,
                                    &bytesSent,
                                    IntPtr.Zero,
                                    0,
                                    asyncResult == null ? SafeNativeOverlapped.Zero : asyncResult.NativeOverlapped,
                                    IntPtr.Zero);

                            // Succeeded without GoAway, disable them.
                            if (statusCode != ErrorCodes.ERROR_INVALID_PARAMETER)
                            {
                                SupportsGoAway = false;
                            }
                        }

                        if (asyncResult != null &&
                            statusCode == ErrorCodes.ERROR_SUCCESS &&
                            HttpSysListener.SkipIOCPCallbackOnSuccess)
                        {
                            asyncResult.BytesSent = bytesSent;
                            // The caller will invoke IOCompleted
                        }
                    }
                }
            }
            finally
            {
                FreePinnedHeaders(pinnedHeaders);
            }
            return(statusCode);
        }
Example #2
0
        internal unsafe Task SendFileAsyncCore(string fileName, long offset, long?count, CancellationToken cancellationToken)
        {
            if (_skipWrites)
            {
                return(Task.CompletedTask);
            }

            var started = _requestContext.Response.HasStarted;

            if (count == 0 && started)
            {
                // No data to send and we've already sent the headers
                return(Task.CompletedTask);
            }

            if (cancellationToken.IsCancellationRequested)
            {
                Abort(ThrowWriteExceptions);
                return(Task.FromCanceled <int>(cancellationToken));
            }

            // We are setting buffer size to 1 to prevent FileStream from allocating it's internal buffer
            // It's too expensive to validate anything before opening the file. Open the file and then check the lengths.
            var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize: 1,
                                            options: FileOptions.Asynchronous | FileOptions.SequentialScan); // Extremely expensive.

            try
            {
                var length = fileStream.Length; // Expensive, only do it once
                if (!count.HasValue)
                {
                    count = length - offset;
                }
                if (offset < 0 || offset > length)
                {
                    throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty);
                }
                if (count < 0 || count > length - offset)
                {
                    throw new ArgumentOutOfRangeException(nameof(count), count, string.Empty);
                }

                CheckWriteCount(count);
            }
            catch
            {
                fileStream.Dispose();
                throw;
            }

            // Make sure all validation is performed before this computes the headers
            var  flags = ComputeLeftToWrite(count.Value);
            uint statusCode;
            uint bytesSent   = 0;
            var  chunked     = _requestContext.Response.BoundaryType == BoundaryType.Chunked;
            var  asyncResult = new ResponseStreamAsyncResult(this, fileStream, offset, count.Value, chunked, cancellationToken);

            try
            {
                if (!started)
                {
                    statusCode = _requestContext.Response.SendHeaders(null, asyncResult, flags, false);
                    bytesSent  = asyncResult.BytesSent;
                }
                else
                {
                    // TODO: If opaque then include the buffer data flag.
                    statusCode = HttpApi.HttpSendResponseEntityBody(
                        RequestQueueHandle,
                        RequestId,
                        (uint)flags,
                        asyncResult.DataChunkCount,
                        asyncResult.DataChunks,
                        &bytesSent,
                        IntPtr.Zero,
                        0,
                        asyncResult.NativeOverlapped,
                        IntPtr.Zero);
                }
            }
            catch (Exception e)
            {
                Logger.LogError(LoggerEventIds.FileSendAsyncError, e, "SendFileAsync");
                asyncResult.Dispose();
                Abort();
                throw;
            }

            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    Logger.LogDebug(LoggerEventIds.FileSendAsyncCancelled, $"SendFileAsync; Write cancelled with error code: {statusCode}");
                    asyncResult.Cancel(ThrowWriteExceptions);
                }
                else if (ThrowWriteExceptions)
                {
                    asyncResult.Dispose();
                    var exception = new IOException(string.Empty, new HttpSysException((int)statusCode));
                    Logger.LogError(LoggerEventIds.FileSendAsyncError, exception, "SendFileAsync");
                    Abort();
                    throw exception;
                }
                else
                {
                    // Abort the request but do not close the stream, let future writes complete silently
                    Logger.LogDebug(LoggerEventIds.FileSendAsyncErrorIgnored, $"SendFileAsync; Ignored write exception: {statusCode}");
                    asyncResult.FailSilently();
                }
            }

            if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess)
            {
                // IO operation completed synchronously - callback won't be called to signal completion.
                asyncResult.IOCompleted(statusCode, bytesSent);
            }

            // Last write, cache it for special cancellation handling.
            if ((flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
            {
                _lastWrite = asyncResult;
            }

            return(asyncResult.Task);
        }
        private unsafe CancellationToken CreateDisconnectToken(ulong connectionId)
        {
            LogHelper.LogDebug(_logger, "CreateDisconnectToken", "Registering connection for disconnect for connection ID: " + connectionId);

            // Create a nativeOverlapped callback so we can register for disconnect callback
            var cts         = new CancellationTokenSource();
            var returnToken = cts.Token;

            SafeNativeOverlapped nativeOverlapped = null;
            var boundHandle = _requestQueue.BoundHandle;

            nativeOverlapped = new SafeNativeOverlapped(boundHandle, boundHandle.AllocateNativeOverlapped(
                                                            (errorCode, numBytes, overlappedPtr) =>
            {
                LogHelper.LogDebug(_logger, "CreateDisconnectToken", "http.sys disconnect callback fired for connection ID: " + connectionId);

                // Free the overlapped
                nativeOverlapped.Dispose();

                // Pull the token out of the list and Cancel it.
                ConnectionCancellation token;
                _connectionCancellationTokens.TryRemove(connectionId, out token);
                try
                {
                    cts.Cancel();
                }
                catch (AggregateException exception)
                {
                    LogHelper.LogException(_logger, "CreateDisconnectToken Callback", exception);
                }
            },
                                                            null, null));

            uint statusCode;

            try
            {
                statusCode = HttpApi.HttpWaitForDisconnectEx(requestQueueHandle: _requestQueue.Handle,
                                                             connectionId: connectionId, reserved: 0, overlapped: nativeOverlapped);
            }
            catch (Win32Exception exception)
            {
                statusCode = (uint)exception.NativeErrorCode;
                LogHelper.LogException(_logger, "CreateDisconnectToken", exception);
            }

            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING &&
                statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
            {
                // We got an unknown result, assume the connection has been closed.
                nativeOverlapped.Dispose();
                ConnectionCancellation ignored;
                _connectionCancellationTokens.TryRemove(connectionId, out ignored);
                LogHelper.LogDebug(_logger, "HttpWaitForDisconnectEx", new Win32Exception((int)statusCode));
                cts.Cancel();
            }

            if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess)
            {
                // IO operation completed synchronously - callback won't be called to signal completion
                nativeOverlapped.Dispose();
                ConnectionCancellation ignored;
                _connectionCancellationTokens.TryRemove(connectionId, out ignored);
                cts.Cancel();
            }

            return(returnToken);
        }
Example #4
0
        // Simpler than Flush because it will never be called at the end of the request from Dispose.
        private unsafe Task FlushInternalAsync(ArraySegment <byte> data, CancellationToken cancellationToken)
        {
            if (_skipWrites)
            {
                return(Task.CompletedTask);
            }

            var started = _requestContext.Response.HasStarted;

            if (data.Count == 0 && started)
            {
                // No data to send and we've already sent the headers
                return(Task.CompletedTask);
            }

            if (cancellationToken.IsCancellationRequested)
            {
                Abort(ThrowWriteExceptions);
                return(Task.FromCanceled <int>(cancellationToken));
            }

            // Make sure all validation is performed before this computes the headers
            var  flags       = ComputeLeftToWrite(data.Count);
            uint statusCode  = 0;
            var  chunked     = _requestContext.Response.BoundaryType == BoundaryType.Chunked;
            var  asyncResult = new ResponseStreamAsyncResult(this, data, chunked, cancellationToken);
            uint bytesSent   = 0;

            try
            {
                if (!started)
                {
                    statusCode = _requestContext.Response.SendHeaders(null, asyncResult, flags, false);
                    bytesSent  = asyncResult.BytesSent;
                }
                else
                {
                    statusCode = HttpApi.HttpSendResponseEntityBody(
                        RequestQueueHandle,
                        RequestId,
                        (uint)flags,
                        asyncResult.DataChunkCount,
                        asyncResult.DataChunks,
                        &bytesSent,
                        IntPtr.Zero,
                        0,
                        asyncResult.NativeOverlapped,
                        IntPtr.Zero);
                }
            }
            catch (Exception e)
            {
                Logger.LogError(LoggerEventIds.ErrorWhenFlushAsync, e, "FlushAsync");
                asyncResult.Dispose();
                Abort();
                throw;
            }

            if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    Logger.LogDebug(LoggerEventIds.WriteFlushCancelled, $"FlushAsync; Write cancelled with error code: {statusCode}");
                    asyncResult.Cancel(ThrowWriteExceptions);
                }
                else if (ThrowWriteExceptions)
                {
                    asyncResult.Dispose();
                    Exception exception = new IOException(string.Empty, new HttpSysException((int)statusCode));
                    Logger.LogError(LoggerEventIds.ErrorWhenFlushAsync, exception, "FlushAsync");
                    Abort();
                    throw exception;
                }
                else
                {
                    // Abort the request but do not close the stream, let future writes complete silently
                    Logger.LogDebug(LoggerEventIds.WriteErrorIgnored, $"FlushAsync; Ignored write exception: {statusCode}");
                    asyncResult.FailSilently();
                }
            }

            if (statusCode == ErrorCodes.ERROR_SUCCESS && HttpSysListener.SkipIOCPCallbackOnSuccess)
            {
                // IO operation completed synchronously - callback won't be called to signal completion.
                asyncResult.IOCompleted(statusCode, bytesSent);
            }

            // Last write, cache it for special cancellation handling.
            if ((flags & HttpApiTypes.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
            {
                _lastWrite = asyncResult;
            }

            return(asyncResult.Task);
        }
Example #5
0
        // We never expect endOfRequest and data at the same time
        private unsafe void FlushInternal(bool endOfRequest, ArraySegment <byte> data = new ArraySegment <byte>())
        {
            Debug.Assert(!(endOfRequest && data.Count > 0), "Data is not supported at the end of the request.");

            if (_skipWrites)
            {
                return;
            }

            var started = _requestContext.Response.HasStarted;

            if (data.Count == 0 && started && !endOfRequest)
            {
                // No data to send and we've already sent the headers
                return;
            }

            // Make sure all validation is performed before this computes the headers
            var flags = ComputeLeftToWrite(data.Count, endOfRequest);

            if (endOfRequest && _leftToWrite > 0)
            {
                if (!RequestContext.DisconnectToken.IsCancellationRequested)
                {
                    // This is logged rather than thrown because it is too late for an exception to be visible in user code.
                    Logger.LogError(LoggerEventIds.FewerBytesThanExpected, "ResponseStream::Dispose; Fewer bytes were written than were specified in the Content-Length.");
                }
                _requestContext.Abort();
                return;
            }

            uint statusCode = 0;

            HttpApiTypes.HTTP_DATA_CHUNK[] dataChunks;
            var pinnedBuffers = PinDataBuffers(endOfRequest, data, out dataChunks);

            try
            {
                if (!started)
                {
                    statusCode = _requestContext.Response.SendHeaders(dataChunks, null, flags, false);
                }
                else
                {
                    fixed(HttpApiTypes.HTTP_DATA_CHUNK *pDataChunks = dataChunks)
                    {
                        statusCode = HttpApi.HttpSendResponseEntityBody(
                            RequestQueueHandle,
                            RequestId,
                            (uint)flags,
                            (ushort)dataChunks.Length,
                            pDataChunks,
                            null,
                            IntPtr.Zero,
                            0,
                            SafeNativeOverlapped.Zero,
                            IntPtr.Zero);
                    }
                }
            }
            finally
            {
                FreeDataBuffers(pinnedBuffers);
            }

            if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_HANDLE_EOF
                // Don't throw for disconnects, we were already finished with the response.
                && (!endOfRequest || (statusCode != ErrorCodes.ERROR_CONNECTION_INVALID && statusCode != ErrorCodes.ERROR_INVALID_PARAMETER)))
            {
                if (ThrowWriteExceptions)
                {
                    var exception = new IOException(string.Empty, new HttpSysException((int)statusCode));
                    Logger.LogError(LoggerEventIds.WriteError, exception, "Flush");
                    Abort();
                    throw exception;
                }
                else
                {
                    // Abort the request but do not close the stream, let future writes complete silently
                    Logger.LogDebug(LoggerEventIds.WriteErrorIgnored, $"Flush; Ignored write exception: {statusCode}");
                    Abort(dispose: false);
                }
            }
        }
Example #6
0
        internal static unsafe ChannelBinding?GetChannelBindingFromTls(RequestQueue requestQueue, ulong connectionId, ILogger logger)
        {
            // +128 since a CBT is usually <128 thus we need to call HRCC just once. If the CBT
            // is >128 we will get ERROR_MORE_DATA and call again
            int size = RequestChannelBindStatusSize + 128;

            Debug.Assert(size >= 0);

            byte[]? blob = null;
            SafeLocalFreeChannelBinding?token = null;

            uint bytesReceived = 0;;
            uint statusCode;

            do
            {
                blob = new byte[size];
                fixed(byte *blobPtr = blob)
                {
                    // Http.sys team: ServiceName will always be null if
                    // HTTP_RECEIVE_SECURE_CHANNEL_TOKEN flag is set.
                    statusCode = HttpApi.HttpReceiveClientCertificate(
                        requestQueue.Handle,
                        connectionId,
                        (uint)HttpApiTypes.HTTP_FLAGS.HTTP_RECEIVE_SECURE_CHANNEL_TOKEN,
                        blobPtr,
                        (uint)size,
                        &bytesReceived,
                        SafeNativeOverlapped.Zero);

                    if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                    {
                        int tokenOffset = GetTokenOffsetFromBlob((IntPtr)blobPtr);
                        int tokenSize   = GetTokenSizeFromBlob((IntPtr)blobPtr);
                        Debug.Assert(tokenSize < Int32.MaxValue);

                        token = SafeLocalFreeChannelBinding.LocalAlloc(tokenSize);

                        Marshal.Copy(blob, tokenOffset, token.DangerousGetHandle(), tokenSize);
                    }
                    else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                    {
                        int tokenSize = GetTokenSizeFromBlob((IntPtr)blobPtr);
                        Debug.Assert(tokenSize < Int32.MaxValue);

                        size = RequestChannelBindStatusSize + tokenSize;
                    }
                    else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_PARAMETER)
                    {
                        logger.LogError(LoggerEventIds.ChannelBindingUnSupported, "GetChannelBindingFromTls; Channel binding is not supported.");
                        return(null); // old schannel library which doesn't support CBT
                    }
                    else
                    {
                        // It's up to the consumer to fail if the missing ChannelBinding matters to them.
                        logger.LogError(LoggerEventIds.ChannelBindingMissing, new HttpSysException((int)statusCode), "GetChannelBindingFromTls");
                        break;
                    }
                }
            }while (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS);

            return(token);
        }
Example #7
0
        private static unsafe void IOCompleted(ClientCertLoader asyncResult, uint errorCode, uint numBytes)
        {
            RequestContext requestContext = asyncResult.RequestContext;

            try
            {
                if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA)
                {
                    // There is a bug that has existed in http.sys since w2k3.  Bytesreceived will only
                    // return the size of the initial cert structure.  To get the full size,
                    // we need to add the certificate encoding size as well.

                    HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult.RequestBlob;
                    asyncResult.Reset(numBytes + pClientCertInfo->CertEncodedSize);

                    uint bytesReceived = 0;
                    errorCode =
                        HttpApi.HttpReceiveClientCertificate(
                            requestContext.Server.RequestQueue.Handle,
                            requestContext.Request.UConnectionId,
                            (uint)HttpApiTypes.HTTP_FLAGS.NONE,
                            asyncResult._memoryBlob,
                            asyncResult._size,
                            &bytesReceived,
                            asyncResult._overlapped !);

                    if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING ||
                        (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && !HttpSysListener.SkipIOCPCallbackOnSuccess))
                    {
                        return;
                    }
                }

                if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND)
                {
                    // The client did not send a cert.
                    asyncResult.Complete(0, null);
                }
                else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
                {
                    asyncResult.Fail(new HttpSysException((int)errorCode));
                }
                else
                {
                    HttpApiTypes.HTTP_SSL_CLIENT_CERT_INFO *pClientCertInfo = asyncResult._memoryBlob;
                    if (pClientCertInfo == null)
                    {
                        asyncResult.Complete(0, null);
                    }
                    else
                    {
                        if (pClientCertInfo->pCertEncoded != null)
                        {
                            try
                            {
                                byte[] certEncoded = new byte[pClientCertInfo->CertEncodedSize];
                                Marshal.Copy((IntPtr)pClientCertInfo->pCertEncoded, certEncoded, 0, certEncoded.Length);
                                asyncResult.Complete((int)pClientCertInfo->CertFlags, new X509Certificate2(certEncoded));
                            }
                            catch (CryptographicException exception)
                            {
                                // TODO: Log
                                asyncResult.Fail(exception);
                            }
                            catch (SecurityException exception)
                            {
                                // TODO: Log
                                asyncResult.Fail(exception);
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                asyncResult.Fail(exception);
            }
        }
Example #8
0
        internal RequestQueue(UrlGroup urlGroup, string requestQueueName, RequestQueueMode mode, ILogger logger)
        {
            _mode     = mode;
            _urlGroup = urlGroup;
            _logger   = logger;

            var flags = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.None;

            Created = true;
            if (_mode == RequestQueueMode.Attach)
            {
                flags   = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting;
                Created = false;
            }

            var statusCode = HttpApi.HttpCreateRequestQueue(
                HttpApi.Version,
                requestQueueName,
                null,
                flags,
                out var requestQueueHandle);

            if (_mode == RequestQueueMode.CreateOrAttach && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_ALREADY_EXISTS)
            {
                // Tried to create, but it already exists so attach to it instead.
                Created    = false;
                flags      = HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting;
                statusCode = HttpApi.HttpCreateRequestQueue(
                    HttpApi.Version,
                    requestQueueName,
                    null,
                    flags,
                    out requestQueueHandle);
            }

            if (flags == HttpApiTypes.HTTP_CREATE_REQUEST_QUEUE_FLAG.OpenExisting && statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_FILE_NOT_FOUND)
            {
                throw new HttpSysException((int)statusCode, $"Failed to attach to the given request queue '{requestQueueName}', the queue could not be found.");
            }
            else if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_INVALID_NAME)
            {
                throw new HttpSysException((int)statusCode, $"The given request queue name '{requestQueueName}' is invalid.");
            }
            else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
            {
                throw new HttpSysException((int)statusCode);
            }

            // Disabling callbacks when IO operation completes synchronously (returns ErrorCodes.ERROR_SUCCESS)
            if (HttpSysListener.SkipIOCPCallbackOnSuccess &&
                !UnsafeNclNativeMethods.SetFileCompletionNotificationModes(
                    requestQueueHandle,
                    UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipCompletionPortOnSuccess |
                    UnsafeNclNativeMethods.FileCompletionNotificationModes.SkipSetEventOnHandle))
            {
                requestQueueHandle.Dispose();
                throw new HttpSysException(Marshal.GetLastWin32Error());
            }

            Handle      = requestQueueHandle;
            BoundHandle = ThreadPoolBoundHandle.BindHandle(Handle);

            if (!Created)
            {
                _logger.LogInformation(LoggerEventIds.AttachedToQueue, "Attached to an existing request queue '{requestQueueName}', some options do not apply.", requestQueueName);
            }
        }