Reorder SendFileAsync to match Write/FlushAsync
This commit is contained in:
parent
c2a1b8d17f
commit
c2f52db3a5
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -81,6 +82,17 @@ namespace Microsoft.Net.Http.Server
|
||||||
return tcs.Task;
|
return tcs.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static ArraySegment<byte> GetChunkHeader(long size)
|
||||||
|
{
|
||||||
|
if (size < int.MaxValue)
|
||||||
|
{
|
||||||
|
return GetChunkHeader((int)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Greater than 2gb, perf is no longer our concern
|
||||||
|
return new ArraySegment<byte>(Encoding.ASCII.GetBytes(size.ToString("X") + "\r\n"));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A private utility routine to convert an integer to a chunk header,
|
/// A private utility routine to convert an integer to a chunk header,
|
||||||
/// which is an ASCII hex number followed by a CRLF.The header is returned
|
/// which is an ASCII hex number followed by a CRLF.The header is returned
|
||||||
|
|
|
||||||
|
|
@ -308,12 +308,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
// TODO: Verbose log
|
// TODO: Verbose log
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_requestAbortSource != null)
|
_requestAbortSource?.Dispose();
|
||||||
{
|
|
||||||
_requestAbortSource.Dispose();
|
|
||||||
}
|
|
||||||
Response.Dispose();
|
Response.Dispose();
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Abort();
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Request.Dispose();
|
Request.Dispose();
|
||||||
|
|
@ -334,14 +335,19 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
_requestAbortSource.Cancel();
|
_requestAbortSource.Cancel();
|
||||||
}
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogHelper.LogException(Logger, "Abort", ex);
|
LogHelper.LogDebug(Logger, "Abort", ex);
|
||||||
}
|
}
|
||||||
_requestAbortSource.Dispose();
|
_requestAbortSource.Dispose();
|
||||||
}
|
}
|
||||||
ForceCancelRequest();
|
ForceCancelRequest();
|
||||||
Request.Dispose();
|
Request.Dispose();
|
||||||
|
// Only Abort, Response.Dispose() tries a graceful flush
|
||||||
|
Response.Abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Abort(object state)
|
private static void Abort(object state)
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
|
||||||
EnsureResponseStream();
|
EnsureResponseStream();
|
||||||
return _nativeStream;
|
return _nativeStream;
|
||||||
}
|
}
|
||||||
|
|
@ -245,6 +244,12 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Abort()
|
||||||
|
{
|
||||||
|
// Update state for HasStarted. Do not attempt a graceful Dispose.
|
||||||
|
_responseState = ResponseState.Closed;
|
||||||
|
}
|
||||||
|
|
||||||
// should only be called from RequestContext
|
// should only be called from RequestContext
|
||||||
internal void Dispose()
|
internal void Dispose()
|
||||||
{
|
{
|
||||||
|
|
@ -685,14 +690,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckDisposed()
|
|
||||||
{
|
|
||||||
if (_responseState >= ResponseState.Closed)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(GetType().FullName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void CancelLastWrite()
|
internal void CancelLastWrite()
|
||||||
{
|
{
|
||||||
_nativeStream?.CancelLastWrite();
|
_nativeStream?.CancelLastWrite();
|
||||||
|
|
|
||||||
|
|
@ -132,12 +132,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
var started = _requestContext.Response.HasStarted;
|
var started = _requestContext.Response.HasStarted;
|
||||||
if (data.Count == 0 && started && !endOfRequest)
|
if (data.Count == 0 && started && !endOfRequest)
|
||||||
{
|
{
|
||||||
// Empty flush
|
// No data to send and we've already sent the headers
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all validation is performed before this computes the headers
|
||||||
var flags = ComputeLeftToWrite(endOfRequest);
|
var flags = ComputeLeftToWrite(endOfRequest);
|
||||||
if (!_inOpaqueMode && endOfRequest && _leftToWrite > data.Count)
|
if (!_inOpaqueMode && endOfRequest && _leftToWrite > 0)
|
||||||
{
|
{
|
||||||
_requestContext.Abort();
|
_requestContext.Abort();
|
||||||
// This is logged rather than thrown because it is too late for an exception to be visible in user code.
|
// This is logged rather than thrown because it is too late for an exception to be visible in user code.
|
||||||
|
|
@ -303,7 +304,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
var started = _requestContext.Response.HasStarted;
|
var started = _requestContext.Response.HasStarted;
|
||||||
if (data.Count == 0 && started)
|
if (data.Count == 0 && started)
|
||||||
{
|
{
|
||||||
// Empty flush
|
// No data to send and we've already sent the headers
|
||||||
return Helpers.CompletedTask();
|
return Helpers.CompletedTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +314,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
return Helpers.CanceledTask<int>();
|
return Helpers.CanceledTask<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all validation is performed before this computes the headers
|
||||||
var flags = ComputeLeftToWrite();
|
var flags = ComputeLeftToWrite();
|
||||||
if (_leftToWrite != data.Count)
|
if (_leftToWrite != data.Count)
|
||||||
{
|
{
|
||||||
|
|
@ -464,30 +466,31 @@ namespace Microsoft.Net.Http.Server
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
{
|
{
|
||||||
// Validates for null and bounds. Allows count == 0.
|
// Validates for null and bounds. Allows count == 0.
|
||||||
|
// TODO: Verbose log parameters
|
||||||
var data = new ArraySegment<byte>(buffer, offset, count);
|
var data = new ArraySegment<byte>(buffer, offset, count);
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
// TODO: Verbose log parameters
|
|
||||||
|
|
||||||
var contentLength = _requestContext.Response.ContentLength;
|
CheckWriteCount(count);
|
||||||
if (contentLength.HasValue && !_requestContext.Response.HasComputedHeaders && contentLength.Value <= data.Count)
|
|
||||||
{
|
|
||||||
if (contentLength.Value < data.Count)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("More bytes written than specified in the Content-Length header.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The last write in a response that has already started, flush immediately
|
|
||||||
else if (_requestContext.Response.HasComputedHeaders && _leftToWrite >= 0 && _leftToWrite <= data.Count)
|
|
||||||
{
|
|
||||||
if (_leftToWrite < data.Count)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("More bytes written than specified in the Content-Length header.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FlushInternal(endOfRequest: false, data: data);
|
FlushInternal(endOfRequest: false, data: data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CheckWriteCount(long? count)
|
||||||
|
{
|
||||||
|
var contentLength = _requestContext.Response.ContentLength;
|
||||||
|
// First write with more bytes written than the entire content-length
|
||||||
|
if (!_requestContext.Response.HasComputedHeaders && contentLength < count)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("More bytes written than specified in the Content-Length header.");
|
||||||
|
}
|
||||||
|
// A write in a response that has already started where the count exceeds the remainder of the content-length
|
||||||
|
else if (_requestContext.Response.HasComputedHeaders && _requestContext.Response.BoundaryType == BoundaryType.ContentLength
|
||||||
|
&& _leftToWrite < count)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("More bytes written than specified in the Content-Length header.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if NETSTANDARD1_3
|
#if NETSTANDARD1_3
|
||||||
public IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
public IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||||
#else
|
#else
|
||||||
|
|
@ -512,26 +515,11 @@ namespace Microsoft.Net.Http.Server
|
||||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// Validates for null and bounds. Allows count == 0.
|
// Validates for null and bounds. Allows count == 0.
|
||||||
|
// TODO: Verbose log parameters
|
||||||
var data = new ArraySegment<byte>(buffer, offset, count);
|
var data = new ArraySegment<byte>(buffer, offset, count);
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
// TODO: Verbose log parameters
|
|
||||||
|
|
||||||
var contentLength = _requestContext.Response.ContentLength;
|
CheckWriteCount(count);
|
||||||
if (contentLength.HasValue && !_requestContext.Response.HasComputedHeaders && contentLength.Value <= data.Count)
|
|
||||||
{
|
|
||||||
if (contentLength.Value < data.Count)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("More bytes written than specified in the Content-Length header.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The last write in a response that has already started, flush immediately
|
|
||||||
else if (_requestContext.Response.HasComputedHeaders && _leftToWrite > 0 && _leftToWrite <= data.Count)
|
|
||||||
{
|
|
||||||
if (_leftToWrite < data.Count)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("More bytes written than specified in the Content-Length header.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FlushInternalAsync(data, cancellationToken);
|
return FlushInternalAsync(data, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -540,12 +528,15 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
// It's too expensive to validate the file attributes before opening the file. Open the file and then check the lengths.
|
// It's too expensive to validate the file attributes before opening the file. Open the file and then check the lengths.
|
||||||
// This all happens inside of ResponseStreamAsyncResult.
|
// This all happens inside of ResponseStreamAsyncResult.
|
||||||
|
// TODO: Verbose log parameters
|
||||||
if (string.IsNullOrWhiteSpace(fileName))
|
if (string.IsNullOrWhiteSpace(fileName))
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("fileName");
|
throw new ArgumentNullException("fileName");
|
||||||
}
|
}
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
|
CheckWriteCount(count);
|
||||||
|
|
||||||
// We can't mix await and unsafe so separate the unsafe code into another method.
|
// We can't mix await and unsafe so separate the unsafe code into another method.
|
||||||
await SendFileAsyncCore(fileName, offset, count, cancellationToken);
|
await SendFileAsyncCore(fileName, offset, count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -557,43 +548,65 @@ namespace Microsoft.Net.Http.Server
|
||||||
return Helpers.CompletedTask();
|
return Helpers.CompletedTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags = ComputeLeftToWrite();
|
var started = _requestContext.Response.HasStarted;
|
||||||
if (count == 0 && _leftToWrite != 0)
|
if (count == 0 && started)
|
||||||
{
|
{
|
||||||
|
// No data to send and we've already sent the headers
|
||||||
return Helpers.CompletedTask();
|
return Helpers.CompletedTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_leftToWrite >= 0 && count > _leftToWrite)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(Resources.Exception_TooMuchWritten);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
Abort(ThrowWriteExceptions);
|
Abort(ThrowWriteExceptions);
|
||||||
return Helpers.CanceledTask<int>();
|
return Helpers.CanceledTask<int>();
|
||||||
}
|
}
|
||||||
// TODO: Verbose log
|
|
||||||
|
|
||||||
|
// 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();
|
||||||
uint statusCode;
|
uint statusCode;
|
||||||
uint bytesSent = 0;
|
uint bytesSent = 0;
|
||||||
var started = _requestContext.Response.HasStarted;
|
|
||||||
var chunked = _requestContext.Response.BoundaryType == BoundaryType.Chunked;
|
var chunked = _requestContext.Response.BoundaryType == BoundaryType.Chunked;
|
||||||
var asyncResult = new ResponseStreamAsyncResult(this, fileName, offset, count, chunked, cancellationToken);
|
var asyncResult = new ResponseStreamAsyncResult(this, fileStream, offset, count.Value, chunked, cancellationToken);
|
||||||
|
|
||||||
long bytesWritten;
|
long bytesWritten;
|
||||||
if (chunked)
|
if (chunked)
|
||||||
{
|
{
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
}
|
}
|
||||||
else if (count.HasValue)
|
else
|
||||||
{
|
{
|
||||||
bytesWritten = count.Value;
|
bytesWritten = count.Value;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
bytesWritten = asyncResult.FileLength - offset;
|
|
||||||
}
|
|
||||||
// Update _leftToWrite now so we can queue up additional calls to SendFileAsync.
|
// 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;
|
flags |= _leftToWrite == bytesWritten ? HttpApi.HTTP_FLAGS.NONE : HttpApi.HTTP_FLAGS.HTTP_SEND_RESPONSE_FLAG_MORE_DATA;
|
||||||
UpdateWritenCount((uint)bytesWritten);
|
UpdateWritenCount((uint)bytesWritten);
|
||||||
|
|
|
||||||
|
|
@ -114,33 +114,15 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ResponseStreamAsyncResult(ResponseStream responseStream, string fileName, long offset,
|
internal ResponseStreamAsyncResult(ResponseStream responseStream, FileStream fileStream, long offset,
|
||||||
long? count, bool chunked, CancellationToken cancellationToken)
|
long count, bool chunked, CancellationToken cancellationToken)
|
||||||
: this(responseStream, cancellationToken)
|
: this(responseStream, cancellationToken)
|
||||||
{
|
{
|
||||||
var boundHandle = responseStream.RequestContext.Server.RequestQueue.BoundHandle;
|
var boundHandle = responseStream.RequestContext.Server.RequestQueue.BoundHandle;
|
||||||
|
|
||||||
int bufferSize = 1024 * 64; // TODO: Validate buffer size choice.
|
_fileStream = fileStream;
|
||||||
#if NETSTANDARD1_3
|
|
||||||
_fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize /*, useAsync: true*/); // Extremely expensive.
|
|
||||||
#else
|
|
||||||
// It's too expensive to validate anything before opening the file. Open the file and then check the lengths.
|
|
||||||
_fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize,
|
|
||||||
FileOptions.Asynchronous | FileOptions.SequentialScan); // Extremely expensive.
|
|
||||||
#endif
|
|
||||||
long length = _fileStream.Length; // Expensive
|
|
||||||
if (offset < 0 || offset > length)
|
|
||||||
{
|
|
||||||
_fileStream.Dispose();
|
|
||||||
throw new ArgumentOutOfRangeException("offset", offset, string.Empty);
|
|
||||||
}
|
|
||||||
if (count.HasValue && (count < 0 || count > length - offset))
|
|
||||||
{
|
|
||||||
_fileStream.Dispose();
|
|
||||||
throw new ArgumentOutOfRangeException("count", count, string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0 || (!count.HasValue && _fileStream.Length == 0))
|
if (count == 0)
|
||||||
{
|
{
|
||||||
_dataChunks = null;
|
_dataChunks = null;
|
||||||
_overlapped = new SafeNativeOverlapped(boundHandle,
|
_overlapped = new SafeNativeOverlapped(boundHandle,
|
||||||
|
|
@ -156,14 +138,14 @@ namespace Microsoft.Net.Http.Server
|
||||||
var chunkHeaderBuffer = new ArraySegment<byte>();
|
var chunkHeaderBuffer = new ArraySegment<byte>();
|
||||||
if (chunked)
|
if (chunked)
|
||||||
{
|
{
|
||||||
chunkHeaderBuffer = Helpers.GetChunkHeader((int)(count ?? _fileStream.Length - offset));
|
chunkHeaderBuffer = Helpers.GetChunkHeader(count);
|
||||||
_dataChunks[0].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
|
_dataChunks[0].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory;
|
||||||
_dataChunks[0].fromMemory.BufferLength = (uint)chunkHeaderBuffer.Count;
|
_dataChunks[0].fromMemory.BufferLength = (uint)chunkHeaderBuffer.Count;
|
||||||
objectsToPin[0] = chunkHeaderBuffer.Array;
|
objectsToPin[0] = chunkHeaderBuffer.Array;
|
||||||
|
|
||||||
_dataChunks[1].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromFileHandle;
|
_dataChunks[1].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromFileHandle;
|
||||||
_dataChunks[1].fromFile.offset = (ulong)offset;
|
_dataChunks[1].fromFile.offset = (ulong)offset;
|
||||||
_dataChunks[1].fromFile.count = (ulong)(count ?? -1);
|
_dataChunks[1].fromFile.count = (ulong)count;
|
||||||
_dataChunks[1].fromFile.fileHandle = _fileStream.SafeFileHandle.DangerousGetHandle();
|
_dataChunks[1].fromFile.fileHandle = _fileStream.SafeFileHandle.DangerousGetHandle();
|
||||||
// Nothing to pin for the file handle.
|
// Nothing to pin for the file handle.
|
||||||
|
|
||||||
|
|
@ -175,7 +157,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
{
|
{
|
||||||
_dataChunks[0].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromFileHandle;
|
_dataChunks[0].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromFileHandle;
|
||||||
_dataChunks[0].fromFile.offset = (ulong)offset;
|
_dataChunks[0].fromFile.offset = (ulong)offset;
|
||||||
_dataChunks[0].fromFile.count = (ulong)(count ?? -1);
|
_dataChunks[0].fromFile.count = (ulong)count;
|
||||||
_dataChunks[0].fromFile.fileHandle = _fileStream.SafeFileHandle.DangerousGetHandle();
|
_dataChunks[0].fromFile.fileHandle = _fileStream.SafeFileHandle.DangerousGetHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,11 +230,6 @@ namespace Microsoft.Net.Http.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal long FileLength
|
|
||||||
{
|
|
||||||
get { return _fileStream == null ? 0 : _fileStream.Length; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool EndCalled { get; set; }
|
internal bool EndCalled { get; set; }
|
||||||
|
|
||||||
internal void IOCompleted(uint errorCode)
|
internal void IOCompleted(uint errorCode)
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
// returned from HttpReceiveClientCertificate when using the
|
// returned from HttpReceiveClientCertificate when using the
|
||||||
// FileCompletionNotificationModes.SkipCompletionPortOnSuccess flag.
|
// FileCompletionNotificationModes.SkipCompletionPortOnSuccess flag.
|
||||||
// This bug was only hit when the buffer passed into HttpReceiveClientCertificate
|
// This bug was only hit when the buffer passed into HttpReceiveClientCertificate
|
||||||
// (1500 bytes initially) is tool small for the certificate.
|
// (1500 bytes initially) is too small for the certificate.
|
||||||
// Due to this bug in downlevel operating systems the FileCompletionNotificationModes.SkipCompletionPortOnSuccess
|
// Due to this bug in downlevel operating systems the FileCompletionNotificationModes.SkipCompletionPortOnSuccess
|
||||||
// flag is only used on Win8 and later.
|
// flag is only used on Win8 and later.
|
||||||
internal static readonly bool SkipIOCPCallbackOnSuccess = ComNetOS.IsWin8orLater;
|
internal static readonly bool SkipIOCPCallbackOnSuccess = ComNetOS.IsWin8orLater;
|
||||||
|
|
|
||||||
|
|
@ -221,12 +221,14 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
using (Utilities.CreateHttpServer(out address, async httpContext =>
|
using (Utilities.CreateHttpServer(out address, async httpContext =>
|
||||||
{
|
{
|
||||||
var sendFile = httpContext.Features.Get<IHttpSendFileFeature>();
|
var sendFile = httpContext.Features.Get<IHttpSendFileFeature>();
|
||||||
await sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None);
|
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() =>
|
||||||
|
sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None));
|
||||||
completed = true;
|
completed = true;
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAsync<HttpRequestException>(() => SendRequestAsync(address));
|
var response = await SendRequestAsync(address);
|
||||||
Assert.False(completed);
|
response.EnsureSuccessStatusCode();
|
||||||
|
Assert.True(completed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,12 +240,14 @@ namespace Microsoft.AspNetCore.Server.WebListener
|
||||||
using (Utilities.CreateHttpServer(out address, async httpContext =>
|
using (Utilities.CreateHttpServer(out address, async httpContext =>
|
||||||
{
|
{
|
||||||
var sendFile = httpContext.Features.Get<IHttpSendFileFeature>();
|
var sendFile = httpContext.Features.Get<IHttpSendFileFeature>();
|
||||||
await sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None);
|
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() =>
|
||||||
|
sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None));
|
||||||
completed = true;
|
completed = true;
|
||||||
}))
|
}))
|
||||||
{
|
{
|
||||||
await Assert.ThrowsAsync<HttpRequestException>(() => SendRequestAsync(address));
|
var response = await SendRequestAsync(address);
|
||||||
Assert.False(completed);
|
response.EnsureSuccessStatusCode();
|
||||||
|
Assert.True(completed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,15 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await Assert.ThrowsAsync<FileNotFoundException>(() =>
|
await Assert.ThrowsAsync<FileNotFoundException>(() =>
|
||||||
context.Response.SendFileAsync("Missing.txt", 0, null, CancellationToken.None));
|
context.Response.SendFileAsync("Missing.txt", 0, null, CancellationToken.None));
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,13 +48,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> ignored;
|
IEnumerable<string> ignored;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length");
|
||||||
|
|
@ -68,13 +69,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(RelativeFilePath, 0, null, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> ignored;
|
IEnumerable<string> ignored;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length");
|
||||||
|
|
@ -89,13 +90,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -110,14 +111,14 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -132,13 +133,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -153,14 +154,15 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
|
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
|
||||||
() => context.Response.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None));
|
() => context.Response.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None));
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,14 +172,15 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
|
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(
|
||||||
() => context.Response.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None));
|
() => context.Response.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None));
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -187,13 +190,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -212,7 +215,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
await context.Response.SendFileAsync(emptyFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(emptyFilePath, 0, null, CancellationToken.None);
|
||||||
|
|
@ -221,7 +224,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
File.Delete(emptyFilePath);
|
File.Delete(emptyFilePath);
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -236,13 +239,13 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
context.Response.Headers["Content-lenGth"] = FileLength.ToString();
|
context.Response.Headers["Content-lenGth"] = FileLength.ToString();
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None);
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -258,14 +261,14 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
context.Response.Headers["Content-lenGth"] = "10";
|
context.Response.Headers["Content-lenGth"] = "10";
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -281,14 +284,14 @@ namespace Microsoft.Net.Http.Server
|
||||||
string address;
|
string address;
|
||||||
using (var server = Utilities.CreateHttpServer(out address))
|
using (var server = Utilities.CreateHttpServer(out address))
|
||||||
{
|
{
|
||||||
Task<HttpResponseMessage> responseTask = SendRequestAsync(address);
|
var responseTask = SendRequestAsync(address);
|
||||||
|
|
||||||
var context = await server.AcceptAsync();
|
var context = await server.AcceptAsync();
|
||||||
context.Response.Headers["Content-lenGth"] = "0";
|
context.Response.Headers["Content-lenGth"] = "0";
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
IEnumerable<string> contentLength;
|
IEnumerable<string> contentLength;
|
||||||
Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length");
|
||||||
|
|
@ -313,7 +316,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, cts.Token);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, cts.Token);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
Assert.Equal(FileLength * 2, (await response.Content.ReadAsByteArrayAsync()).Length);
|
Assert.Equal(FileLength * 2, (await response.Content.ReadAsByteArrayAsync()).Length);
|
||||||
}
|
}
|
||||||
|
|
@ -335,7 +338,7 @@ namespace Microsoft.Net.Http.Server
|
||||||
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, cts.Token);
|
await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, cts.Token);
|
||||||
context.Dispose();
|
context.Dispose();
|
||||||
|
|
||||||
HttpResponseMessage response = await responseTask;
|
var response = await responseTask;
|
||||||
Assert.Equal(200, (int)response.StatusCode);
|
Assert.Equal(200, (int)response.StatusCode);
|
||||||
Assert.Equal(FileLength * 2, (await response.Content.ReadAsByteArrayAsync()).Length);
|
Assert.Equal(FileLength * 2, (await response.Content.ReadAsByteArrayAsync()).Length);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue