private static void IOCompleted(ResponseStreamAsyncResult asyncResult, uint errorCode, uint numBytes)
 {
     try
     {
         if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF)
         {
             asyncResult.Fail(new IOException(string.Empty, new WebListenerException((int)errorCode)));
         }
         else
         {
             if (asyncResult._dataChunks == null)
             {
                 // TODO: Verbose log data written
             }
             else
             {
                 // TODO: Verbose log
                 // for (int i = 0; i < asyncResult._dataChunks.Length; i++)
                 // {
                 // Logging.Dump(Logging.HttpListener, asyncResult, "Callback", (IntPtr)asyncResult._dataChunks[0].fromMemory.pBuffer, (int)asyncResult._dataChunks[0].fromMemory.BufferLength);
                 // }
             }
             asyncResult.Complete();
         }
     }
     catch (Exception e)
     {
         asyncResult.Fail(e);
     }
 }
Exemple #2
0
        internal unsafe void CancelLastWrite(SafeHandle requestQueueHandle)
        {
            ResponseStreamAsyncResult asyncState = _lastWrite;

            if (asyncState != null && !asyncState.IsCompleted)
            {
                UnsafeNclNativeMethods.CancelIoEx(requestQueueHandle, asyncState.NativeOverlapped);
            }
        }
Exemple #3
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(HttpApi.HTTP_DATA_CHUNK[] dataChunks,
                                         ResponseStreamAsyncResult asyncResult,
                                         HttpApi.HTTP_FLAGS flags,
                                         bool isOpaqueUpgrade)
        {
            Debug.Assert(!HasStartedSending, "HttpListenerResponse::SendHeaders()|SentHeaders is true.");

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

            /*
             * if (m_BoundaryType==BoundaryType.Raw) {
             *  use HTTP_SEND_RESPONSE_FLAG_RAW_HEADER;
             * }
             */
            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    = (HttpApi.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 HttpApi.HTTP_CACHE_POLICY();
                var cacheTtl    = CacheTtl;
                if (cacheTtl.HasValue && cacheTtl.Value > TimeSpan.Zero)
                {
                    cachePolicy.Policy        = HttpApi.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      = (sbyte *)pReasonPhrase;
                    fixed(HttpApi.HTTP_RESPONSE_V2 *pResponse = &_nativeResponse)
                    {
                        statusCode =
                            HttpApi.HttpSendHttpResponse(
                                RequestContext.RequestQueueHandle,
                                Request.RequestId,
                                (uint)flags,
                                pResponse,
                                &cachePolicy,
                                &bytesSent,
                                SafeLocalFree.Zero,
                                0,
                                asyncResult == null ? SafeNativeOverlapped.Zero : asyncResult.NativeOverlapped,
                                IntPtr.Zero);

                        if (asyncResult != null &&
                            statusCode == ErrorCodes.ERROR_SUCCESS &&
                            WebListener.SkipIOCPCallbackOnSuccess)
                        {
                            asyncResult.BytesSent = bytesSent;
                            // The caller will invoke IOCompleted
                        }
                    }
                }
            }
            finally
            {
                FreePinnedHeaders(pinnedHeaders);
            }
            return(statusCode);
        }
Exemple #4
0
        internal unsafe Task SendFileAsyncCore(string fileName, long offset, long?count, CancellationToken cancellationToken)
        {
            _requestContext.Response.Start();
            UnsafeNclNativeMethods.HttpApi.HTTP_FLAGS flags = ComputeLeftToWrite();
            if (count == 0 && _leftToWrite != 0)
            {
                return(Helpers.CompletedTask());
            }
            if (_leftToWrite >= 0 && count > _leftToWrite)
            {
                throw new InvalidOperationException(Resources.Exception_TooMuchWritten);
            }
            // TODO: Verbose log

            if (cancellationToken.IsCancellationRequested)
            {
                return(Helpers.CanceledTask <int>());
            }

            var cancellationRegistration = default(CancellationTokenRegistration);

            if (cancellationToken.CanBeCanceled)
            {
                cancellationRegistration = cancellationToken.Register(RequestContext.AbortDelegate, _requestContext);
            }

            uint statusCode;
            uint bytesSent      = 0;
            bool startedSending = _requestContext.Response.HasStartedSending;
            var  chunked        = _requestContext.Response.BoundaryType == BoundaryType.Chunked;
            ResponseStreamAsyncResult asyncResult = new ResponseStreamAsyncResult(this, fileName, offset, count, chunked, cancellationRegistration);

            long bytesWritten;

            if (chunked)
            {
                bytesWritten = 0;
            }
            else if (count.HasValue)
            {
                bytesWritten = count.Value;
            }
            else
            {
                bytesWritten = asyncResult.FileLength - offset;
            }
            // Update _leftToWrite now so we can queue up additional calls to SendFileAsync.
            flags |= _leftToWrite == bytesWritten ? HttpApi.HTTP_FLAGS.NONE : HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
            UpdateWritenCount((uint)bytesWritten);

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

            if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
            {
                asyncResult.Dispose();
                if (_requestContext.Server.IgnoreWriteExceptions && startedSending)
                {
                    asyncResult.Complete();
                }
                else
                {
                    Exception exception = new IOException(string.Empty, new WebListenerException((int)statusCode));
                    LogHelper.LogException(_requestContext.Logger, "SendFileAsync", exception);
                    Abort();
                    throw exception;
                }
            }

            if (statusCode == ErrorCodes.ERROR_SUCCESS && WebListener.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 & HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
            {
                _lastWrite = asyncResult;
            }

            return(asyncResult.Task);
        }
Exemple #5
0
        // Simpler than Flush because it will never be called at the end of the request from Dispose.
        public unsafe override Task FlushAsync(CancellationToken cancellationToken)
        {
            if (_closed)
            {
                return(Helpers.CompletedTask());
            }

            bool startedSending = _requestContext.Response.HasStartedSending;
            var  byteCount      = _buffer.TotalBytes;

            if (byteCount == 0 && startedSending)
            {
                // Empty flush
                return(Helpers.CompletedTask());
            }

            var cancellationRegistration = default(CancellationTokenRegistration);

            if (cancellationToken.CanBeCanceled)
            {
                cancellationRegistration = cancellationToken.Register(RequestContext.AbortDelegate, _requestContext);
            }

            var flags = ComputeLeftToWrite();

            if (_leftToWrite != byteCount)
            {
                flags |= HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
            }

            UpdateWritenCount((uint)byteCount);
            uint statusCode  = 0;
            var  chunked     = _requestContext.Response.BoundaryType == BoundaryType.Chunked;
            var  asyncResult = new ResponseStreamAsyncResult(this, _buffer, chunked, cancellationRegistration);
            uint bytesSent   = 0;

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

            if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING)
            {
                asyncResult.Dispose();
                if (_requestContext.Server.IgnoreWriteExceptions && startedSending)
                {
                    asyncResult.Complete();
                }
                else
                {
                    Exception exception = new IOException(string.Empty, new WebListenerException((int)statusCode));
                    LogHelper.LogException(_requestContext.Logger, "FlushAsync", exception);
                    Abort();
                    throw exception;
                }
            }

            if (statusCode == ErrorCodes.ERROR_SUCCESS && WebListener.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 & HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0)
            {
                _lastWrite = asyncResult;
            }

            return(asyncResult.Task);
        }