private static void IOCompleted(ResponseStreamAsyncResult asyncResult, uint errorCode, uint numBytes)
    {
        var logger = asyncResult._responseStream.RequestContext.Logger;

        try
        {
            if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF)
            {
                if (asyncResult._cancellationToken.IsCancellationRequested)
                {
                    Log.WriteCancelled(logger, errorCode);
                    asyncResult.Cancel(asyncResult._responseStream.ThrowWriteExceptions);
                }
                else if (asyncResult._responseStream.ThrowWriteExceptions)
                {
                    var exception = new IOException(string.Empty, new HttpSysException((int)errorCode));
                    Log.WriteError(logger, exception);
                    asyncResult.Fail(exception);
                }
                else
                {
                    Log.WriteErrorIgnored(logger, errorCode);
                    asyncResult.FailSilently();
                }
            }
            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)
        {
            Log.WriteError(logger, e);
            asyncResult.Fail(e);
        }
    }
Exemple #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)
        {
            Log.FileSendAsyncError(Logger, e);
            asyncResult.Dispose();
            Abort();
            throw;
        }

        if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_IO_PENDING)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                Log.FileSendAsyncCancelled(Logger, statusCode);
                asyncResult.Cancel(ThrowWriteExceptions);
            }
            else if (ThrowWriteExceptions)
            {
                asyncResult.Dispose();
                var exception = new IOException(string.Empty, new HttpSysException((int)statusCode));
                Log.FileSendAsyncError(Logger, exception);
                Abort();
                throw exception;
            }
            else
            {
                // Abort the request but do not close the stream, let future writes complete
                Log.FileSendAsyncErrorIgnored(Logger, 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);
    }
Exemple #3
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;
        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)
        {
            Log.ErrorWhenFlushAsync(Logger, e);
            asyncResult.Dispose();
            Abort();
            throw;
        }

        if (statusCode != ErrorCodes.ERROR_SUCCESS && statusCode != ErrorCodes.ERROR_IO_PENDING)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                Log.WriteFlushCancelled(Logger, statusCode);
                asyncResult.Cancel(ThrowWriteExceptions);
            }
            else if (ThrowWriteExceptions)
            {
                asyncResult.Dispose();
                Exception exception = new IOException(string.Empty, new HttpSysException((int)statusCode));
                Log.ErrorWhenFlushAsync(Logger, exception);
                Abort();
                throw exception;
            }
            else
            {
                // Abort the request but do not close the stream, let future writes complete silently
                Log.WriteErrorIgnored(Logger, 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);
    }