Example #1
0
        private void WriteCore(byte[] buffer, int offset, int size)
        {
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (size == 0 && _leftToWrite != 0)
            {
                return;
            }
            if (_leftToWrite >= 0 && size > _leftToWrite)
            {
                throw new ProtocolViolationException(SR.net_entitytoobig);
            }

            uint statusCode;
            uint dataToWrite = (uint)size;
            SafeLocalAllocHandle?bufferAsIntPtr = null;
            IntPtr pBufferAsIntPtr = IntPtr.Zero;
            bool   sentHeaders     = _httpContext.Response.SentHeaders;

            try
            {
                if (size == 0)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, null, flags, false);
                }
                else
                {
                    fixed(byte *pDataBuffer = buffer)
                    {
                        byte *pBuffer = pDataBuffer;

                        if (_httpContext.Response.BoundaryType == BoundaryType.Chunked)
                        {
                            string chunkHeader = size.ToString("x", CultureInfo.InvariantCulture);
                            dataToWrite     = dataToWrite + (uint)(chunkHeader.Length + 4);
                            bufferAsIntPtr  = SafeLocalAllocHandle.LocalAlloc((int)dataToWrite);
                            pBufferAsIntPtr = bufferAsIntPtr.DangerousGetHandle();
                            for (int i = 0; i < chunkHeader.Length; i++)
                            {
                                Marshal.WriteByte(pBufferAsIntPtr, i, (byte)chunkHeader[i]);
                            }
                            Marshal.WriteInt16(pBufferAsIntPtr, chunkHeader.Length, 0x0A0D);
                            Marshal.Copy(buffer, offset, pBufferAsIntPtr + chunkHeader.Length + 2, size);
                            Marshal.WriteInt16(pBufferAsIntPtr, (int)(dataToWrite - 2), 0x0A0D);
                            pBuffer = (byte *)pBufferAsIntPtr;
                            offset  = 0;
                        }
                        Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = default;
                        dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                        dataChunk.pBuffer       = (byte *)(pBuffer + offset);
                        dataChunk.BufferLength  = dataToWrite;

                        flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
                        if (!sentHeaders)
                        {
                            statusCode = _httpContext.Response.SendHeaders(&dataChunk, null, flags, false);
                        }
                        else
                        {
                            if (NetEventSource.Log.IsEnabled())
                            {
                                NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                            }

                            statusCode =
                                Interop.HttpApi.HttpSendResponseEntityBody(
                                    _httpContext.RequestQueueHandle,
                                    _httpContext.RequestId,
                                    (uint)flags,
                                    1,
                                    &dataChunk,
                                    null,
                                    SafeLocalAllocHandle.Zero,
                                    0,
                                    null,
                                    null);

                            if (NetEventSource.Log.IsEnabled())
                            {
                                NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                            }
                            if (_httpContext.Listener !.IgnoreWriteExceptions)
                            {
                                if (NetEventSource.Log.IsEnabled())
                                {
                                    NetEventSource.Info(this, "Write() suppressing error");
                                }
                                statusCode = Interop.HttpApi.ERROR_SUCCESS;
                            }
                        }
                    }
                }
            }
            finally
            {
                // free unmanaged buffer
                bufferAsIntPtr?.Close();
            }

            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
            {
                Exception exception = new HttpListenerException((int)statusCode);
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(this, exception.ToString());
                }
                _closed = true;
                _httpContext.Abort();
                throw exception;
            }
            UpdateAfterWrite(dataToWrite);
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.DumpBuffer(this, buffer, offset, (int)dataToWrite);
            }
        }
        // return value indicates sync vs async completion
        // false: sync completion
        // true: async completion or with error
        private unsafe bool WriteAsyncFast(HttpListenerAsyncEventArgs eventArgs)
        {
            Interop.HttpApi.HTTP_FLAGS flags = Interop.HttpApi.HTTP_FLAGS.NONE;

            eventArgs.StartOperationCommon(this, _outputStream.InternalHttpContext.RequestQueueBoundHandle);
            eventArgs.StartOperationSend();

            uint statusCode;
            bool completedAsynchronouslyOrWithError;

            try
            {
                if (_outputStream.Closed ||
                    (eventArgs.Buffer != null && eventArgs.Count == 0))
                {
                    eventArgs.FinishOperationSuccess(eventArgs.Count, true);
                    return(false);
                }

                if (eventArgs.ShouldCloseOutput)
                {
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
                }
                else
                {
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
                    // When using HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA HTTP.SYS will copy the payload to
                    // kernel memory (Non-Paged Pool). Http.Sys will buffer up to
                    // Math.Min(16 MB, current TCP window size)
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA;
                }

                uint bytesSent;
                statusCode =
                    Interop.HttpApi.HttpSendResponseEntityBody(
                        _outputStream.InternalHttpContext.RequestQueueHandle,
                        _outputStream.InternalHttpContext.RequestId,
                        (uint)flags,
                        eventArgs.EntityChunkCount,
                        (Interop.HttpApi.HTTP_DATA_CHUNK *)eventArgs.EntityChunks,
                        &bytesSent,
                        SafeLocalAllocHandle.Zero,
                        0,
                        eventArgs.NativeOverlapped,
                        null);

                if (statusCode != Interop.HttpApi.ERROR_SUCCESS &&
                    statusCode != Interop.HttpApi.ERROR_IO_PENDING)
                {
                    throw new HttpListenerException((int)statusCode);
                }
                else if (statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                         HttpListener.SkipIOCPCallbackOnSuccess)
                {
                    // IO operation completed synchronously - callback won't be called to signal completion.
                    eventArgs.FinishOperationSuccess((int)bytesSent, true);
                    completedAsynchronouslyOrWithError = false;
                }
                else
                {
                    completedAsynchronouslyOrWithError = true;
                }
            }
            catch (Exception e)
            {
                _writeEventArgs !.FinishOperationFailure(e, true);
                _outputStream.SetClosedFlag();
                _outputStream.InternalHttpContext.Abort();

                completedAsynchronouslyOrWithError = true;
            }

            return(completedAsynchronouslyOrWithError);
        }
Example #3
0
        private IAsyncResult BeginWriteCore(byte[] buffer, int offset, int size, AsyncCallback?callback, object?state)
        {
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (_closed || (size == 0 && _leftToWrite != 0))
            {
                HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback);
                result.InvokeCallback((uint)0);
                return(result);
            }
            if (_leftToWrite >= 0 && size > _leftToWrite)
            {
                throw new ProtocolViolationException(SR.net_entitytoobig);
            }

            uint statusCode;
            uint bytesSent = 0;

            flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
            bool sentHeaders = _httpContext.Response.SentHeaders;
            HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, _httpContext.Response.BoundaryType == BoundaryType.Chunked, sentHeaders, _httpContext.RequestQueueBoundHandle);

            // Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite.
            UpdateAfterWrite((uint)((_httpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size));

            try
            {
                if (!sentHeaders)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, asyncResult, flags, false);
                }
                else
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                    }

                    statusCode =
                        Interop.HttpApi.HttpSendResponseEntityBody(
                            _httpContext.RequestQueueHandle,
                            _httpContext.RequestId,
                            (uint)flags,
                            asyncResult.dataChunkCount,
                            asyncResult.pDataChunks,
                            &bytesSent,
                            SafeLocalAllocHandle.Zero,
                            0,
                            asyncResult._pOverlapped,
                            null);

                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                    }
                }
            }
            catch (Exception e)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(this, e.ToString());
                }
                asyncResult.InternalCleanup();
                _closed = true;
                _httpContext.Abort();
                throw;
            }

            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING)
            {
                asyncResult.InternalCleanup();
                if (_httpContext.Listener !.IgnoreWriteExceptions && sentHeaders)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Info(this, "BeginWrite() Suppressing error");
                    }
                }
                else
                {
                    Exception exception = new HttpListenerException((int)statusCode);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(this, exception.ToString());
                    }
                    _closed = true;
                    _httpContext.Abort();
                    throw exception;
                }
            }
        private void DisposeCore()
        {
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (_leftToWrite > 0 && !_inOpaqueMode)
            {
                throw new InvalidOperationException(SR.net_io_notenoughbyteswritten);
            }
            bool sentHeaders = _httpContext.Response.SentHeaders;

            if (sentHeaders && _leftToWrite == 0)
            {
                return;
            }

            uint statusCode = 0;

            if ((_httpContext.Response.BoundaryType == BoundaryType.Chunked || _httpContext.Response.BoundaryType == BoundaryType.None) && !string.Equals(_httpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase))
            {
                if (_httpContext.Response.BoundaryType == BoundaryType.None)
                {
                    flags |= Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
                }

                fixed(void *pBuffer = &s_chunkTerminator[0])
                {
                    Interop.HttpApi.HTTP_DATA_CHUNK *pDataChunk = null;
                    if (_httpContext.Response.BoundaryType == BoundaryType.Chunked)
                    {
                        Interop.HttpApi.HTTP_DATA_CHUNK dataChunk = default;
                        dataChunk.DataChunkType = Interop.HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
                        dataChunk.pBuffer       = (byte *)pBuffer;
                        dataChunk.BufferLength  = (uint)s_chunkTerminator.Length;
                        pDataChunk = &dataChunk;
                    }
                    if (!sentHeaders)
                    {
                        statusCode = _httpContext.Response.SendHeaders(pDataChunk, null, flags, false);
                    }
                    else
                    {
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                        }

                        statusCode =
                            Interop.HttpApi.HttpSendResponseEntityBody(
                                _httpContext.RequestQueueHandle,
                                _httpContext.RequestId,
                                (uint)flags,
                                pDataChunk != null ? (ushort)1 : (ushort)0,
                                pDataChunk,
                                null,
                                SafeLocalAllocHandle.Zero,
                                0,
                                null,
                                null);

                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                        }
                        if (_httpContext.Listener.IgnoreWriteExceptions)
                        {
                            if (NetEventSource.Log.IsEnabled())
                            {
                                NetEventSource.Info(this, "Suppressing error");
                            }
                            statusCode = Interop.HttpApi.ERROR_SUCCESS;
                        }
                    }
                }
            }
            else
            {
                if (!sentHeaders)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, null, flags, false);
                }
            }
            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_HANDLE_EOF)
            {
                Exception exception = new HttpListenerException((int)statusCode);
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(this, exception.ToString());
                }
                _httpContext.Abort();
                throw exception;
            }
            _leftToWrite = 0;
        }
Example #5
0
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, "buffer.Length:" + buffer.Length + " size:" + size + " offset:" + offset);
            }
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }
            if (offset < 0 || offset > buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }
            if (size < 0 || size > buffer.Length - offset)
            {
                throw new ArgumentOutOfRangeException(nameof(size));
            }
            Interop.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (_closed || (size == 0 && _leftToWrite != 0))
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this);
                }
                HttpResponseStreamAsyncResult result = new HttpResponseStreamAsyncResult(this, state, callback);
                result.InvokeCallback((uint)0);
                return(result);
            }
            if (_leftToWrite >= 0 && size > _leftToWrite)
            {
                throw new ProtocolViolationException(SR.net_entitytoobig);
            }

            uint statusCode;
            uint bytesSent = 0;

            flags |= _leftToWrite == size ? Interop.HttpApi.HTTP_FLAGS.NONE : Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
            bool sentHeaders = _httpContext.Response.SentHeaders;
            HttpResponseStreamAsyncResult asyncResult = new HttpResponseStreamAsyncResult(this, state, callback, buffer, offset, size, _httpContext.Response.BoundaryType == BoundaryType.Chunked, sentHeaders, _httpContext.RequestQueueBoundHandle);

            // Update m_LeftToWrite now so we can queue up additional BeginWrite's without waiting for EndWrite.
            UpdateAfterWrite((uint)((_httpContext.Response.BoundaryType == BoundaryType.Chunked) ? 0 : size));

            try
            {
                if (!sentHeaders)
                {
                    statusCode = _httpContext.Response.SendHeaders(null, asyncResult, flags, false);
                }
                else
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendResponseEntityBody");
                    }

                    statusCode =
                        Interop.HttpApi.HttpSendResponseEntityBody(
                            _httpContext.RequestQueueHandle,
                            _httpContext.RequestId,
                            (uint)flags,
                            asyncResult.dataChunkCount,
                            asyncResult.pDataChunks,
                            &bytesSent,
                            SafeLocalAllocHandle.Zero,
                            0,
                            asyncResult._pOverlapped,
                            null);

                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendResponseEntityBody returned:" + statusCode);
                    }
                }
            }
            catch (Exception e)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Error(this, e.ToString());
                }
                asyncResult.InternalCleanup();
                _closed = true;
                _httpContext.Abort();
                throw;
            }

            if (statusCode != Interop.HttpApi.ERROR_SUCCESS && statusCode != Interop.HttpApi.ERROR_IO_PENDING)
            {
                asyncResult.InternalCleanup();
                if (_httpContext.Listener.IgnoreWriteExceptions && sentHeaders)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Info(this, "BeginWrite() Suppressing error");
                    }
                }
                else
                {
                    Exception exception = new HttpListenerException((int)statusCode);
                    if (NetEventSource.IsEnabled)
                    {
                        NetEventSource.Error(this, exception.ToString());
                    }
                    _closed = true;
                    _httpContext.Abort();
                    throw exception;
                }
            }

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

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

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
            return(asyncResult);
        }
        internal Interop.HttpApi.HTTP_FLAGS ComputeHeaders()
        {
            Interop.HttpApi.HTTP_FLAGS flags = Interop.HttpApi.HTTP_FLAGS.NONE;
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this);
            }
            Debug.Assert(!ComputedHeaders, "ComputedHeaders is true.");
            _responseState = ResponseState.ComputedHeaders;

            ComputeCoreHeaders();

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this,
                                    $"flags: {flags} _boundaryType: {_boundaryType} _contentLength: {_contentLength} _keepAlive: {_keepAlive}");
            }
            if (_boundaryType == BoundaryType.None)
            {
                if (HttpListenerRequest.ProtocolVersion.Minor == 0)
                {
                    _keepAlive = false;
                }
                else
                {
                    _boundaryType = BoundaryType.Chunked;
                }
                if (CanSendResponseBody(_httpContext.Response.StatusCode))
                {
                    _contentLength = -1;
                }
                else
                {
                    ContentLength64 = 0;
                }
            }

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"flags:{flags} _BoundaryType:{_boundaryType} _contentLength:{_contentLength} _keepAlive: {_keepAlive}");
            }
            if (_boundaryType == BoundaryType.ContentLength)
            {
                Headers[HttpResponseHeader.ContentLength] = _contentLength.ToString("D", NumberFormatInfo.InvariantInfo);
                if (_contentLength == 0)
                {
                    flags = Interop.HttpApi.HTTP_FLAGS.NONE;
                }
            }
            else if (_boundaryType == BoundaryType.Chunked)
            {
                Headers[HttpResponseHeader.TransferEncoding] = "chunked";
            }
            else if (_boundaryType == BoundaryType.None)
            {
                flags = Interop.HttpApi.HTTP_FLAGS.NONE; // seems like HTTP_SEND_RESPONSE_FLAG_MORE_DATA but this hangs the app;
            }
            else
            {
                _keepAlive = false;
            }
            if (!_keepAlive)
            {
                Headers.Add(HttpResponseHeader.Connection, "close");
                if (flags == Interop.HttpApi.HTTP_FLAGS.NONE)
                {
                    flags = Interop.HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_DISCONNECT;
                }
            }
            else
            {
                if (HttpListenerRequest.ProtocolVersion.Minor == 0)
                {
                    Headers[HttpResponseHeader.KeepAlive] = "true";
                }
            }
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"flags:{flags} _BoundaryType:{_boundaryType} _contentLength:{_contentLength} _keepAlive: {_keepAlive}");
            }
            return(flags);
        }
        /*
         * 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.
         */
        internal unsafe uint SendHeaders(Interop.HttpApi.HTTP_DATA_CHUNK *pDataChunk,
                                         HttpResponseStreamAsyncResult asyncResult,
                                         Interop.HttpApi.HTTP_FLAGS flags,
                                         bool isWebSocketHandshake)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"pDataChunk: { ((IntPtr)pDataChunk)}, asyncResult: {asyncResult}");
            }
            Debug.Assert(!SentHeaders, "SentHeaders is true.");

            if (StatusCode == (int)HttpStatusCode.Unauthorized)
            {
                // User set 401
                // Using the configured Auth schemes, populate the auth challenge headers. This is for scenarios where
                // Anonymous access is allowed for some resources, but the server later determines that authorization
                // is required for this request.
                HttpListenerContext.SetAuthenticationHeaders();
            }

            // Log headers
            if (NetEventSource.IsEnabled)
            {
                StringBuilder sb = new StringBuilder("HttpListenerResponse Headers:\n");
                for (int i = 0; i < Headers.Count; i++)
                {
                    sb.Append("\t");
                    sb.Append(Headers.GetKey(i));
                    sb.Append(" : ");
                    sb.Append(Headers.Get(i));
                    sb.Append("\n");
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, sb.ToString());
                }
            }
            _responseState = ResponseState.SentHeaders;

            uint            statusCode;
            uint            bytesSent;
            List <GCHandle> pinnedHeaders = SerializeHeaders(ref _nativeResponse.Headers, isWebSocketHandshake);

            try
            {
                if (pDataChunk != null)
                {
                    _nativeResponse.EntityChunkCount = 1;
                    _nativeResponse.pEntityChunks    = pDataChunk;
                }
                else if (asyncResult != null && asyncResult.pDataChunks != null)
                {
                    _nativeResponse.EntityChunkCount = asyncResult.dataChunkCount;
                    _nativeResponse.pEntityChunks    = asyncResult.pDataChunks;
                }
                else
                {
                    _nativeResponse.EntityChunkCount = 0;
                    _nativeResponse.pEntityChunks    = null;
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "Calling Interop.HttpApi.HttpSendHttpResponse flags:" + flags);
                }
                if (StatusDescription.Length > 0)
                {
                    byte[] statusDescriptionBytes = new byte[WebHeaderEncoding.GetByteCount(StatusDescription)];
                    fixed(byte *pStatusDescription = statusDescriptionBytes)
                    {
                        _nativeResponse.ReasonLength = (ushort)statusDescriptionBytes.Length;
                        WebHeaderEncoding.GetBytes(StatusDescription, 0, statusDescriptionBytes.Length, statusDescriptionBytes, 0);
                        _nativeResponse.pReason = (sbyte *)pStatusDescription;
                        fixed(Interop.HttpApi.HTTP_RESPONSE *pResponse = &_nativeResponse)
                        {
                            statusCode =
                                Interop.HttpApi.HttpSendHttpResponse(
                                    HttpListenerContext.RequestQueueHandle,
                                    HttpListenerRequest.RequestId,
                                    (uint)flags,
                                    pResponse,
                                    null,
                                    &bytesSent,
                                    SafeLocalAllocHandle.Zero,
                                    0,
                                    asyncResult == null ? null : asyncResult._pOverlapped,
                                    null);

                            if (asyncResult != null &&
                                statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                                HttpListener.SkipIOCPCallbackOnSuccess)
                            {
                                asyncResult.IOCompleted(statusCode, bytesSent);
                                // IO operation completed synchronously - callback won't be called to signal completion.
                            }
                        }
                    }
                }
                else
                {
                    fixed(Interop.HttpApi.HTTP_RESPONSE *pResponse = &_nativeResponse)
                    {
                        statusCode =
                            Interop.HttpApi.HttpSendHttpResponse(
                                HttpListenerContext.RequestQueueHandle,
                                HttpListenerRequest.RequestId,
                                (uint)flags,
                                pResponse,
                                null,
                                &bytesSent,
                                SafeLocalAllocHandle.Zero,
                                0,
                                asyncResult == null ? null : asyncResult._pOverlapped,
                                null);

                        if (asyncResult != null &&
                            statusCode == Interop.HttpApi.ERROR_SUCCESS &&
                            HttpListener.SkipIOCPCallbackOnSuccess)
                        {
                            asyncResult.IOCompleted(statusCode, bytesSent);
                            // IO operation completed synchronously - callback won't be called to signal completion.
                        }
                    }
                }
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "Call to Interop.HttpApi.HttpSendHttpResponse returned:" + statusCode);
                }
            }
            finally
            {
                FreePinnedHeaders(pinnedHeaders);
            }
            return(statusCode);
        }