diff --git a/src/Microsoft.Net.Http.Server/Helpers.cs b/src/Microsoft.Net.Http.Server/Helpers.cs index a117fe2490..6d9aaaba0e 100644 --- a/src/Microsoft.Net.Http.Server/Helpers.cs +++ b/src/Microsoft.Net.Http.Server/Helpers.cs @@ -23,6 +23,7 @@ using System; using System.Runtime.CompilerServices; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -81,6 +82,17 @@ namespace Microsoft.Net.Http.Server return tcs.Task; } + internal static ArraySegment GetChunkHeader(long size) + { + if (size < int.MaxValue) + { + return GetChunkHeader((int)size); + } + + // Greater than 2gb, perf is no longer our concern + return new ArraySegment(Encoding.ASCII.GetBytes(size.ToString("X") + "\r\n")); + } + /// /// 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 diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestContext.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestContext.cs index 34b68d7242..00044c2eed 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/RequestContext.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/RequestContext.cs @@ -308,12 +308,13 @@ namespace Microsoft.Net.Http.Server // TODO: Verbose log try { - if (_requestAbortSource != null) - { - _requestAbortSource.Dispose(); - } + _requestAbortSource?.Dispose(); Response.Dispose(); } + catch + { + Abort(); + } finally { Request.Dispose(); @@ -334,14 +335,19 @@ namespace Microsoft.Net.Http.Server { _requestAbortSource.Cancel(); } + catch (ObjectDisposedException) + { + } catch (Exception ex) { - LogHelper.LogException(Logger, "Abort", ex); + LogHelper.LogDebug(Logger, "Abort", ex); } _requestAbortSource.Dispose(); } ForceCancelRequest(); Request.Dispose(); + // Only Abort, Response.Dispose() tries a graceful flush + Response.Abort(); } private static void Abort(object state) diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/Response.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/Response.cs index 1c6dc9c2c9..07622b5a87 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/Response.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/Response.cs @@ -109,7 +109,6 @@ namespace Microsoft.Net.Http.Server { get { - CheckDisposed(); EnsureResponseStream(); 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 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() { _nativeStream?.CancelLastWrite(); diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStream.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStream.cs index b5bfca3915..4d23aafa68 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStream.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStream.cs @@ -132,12 +132,13 @@ namespace Microsoft.Net.Http.Server var started = _requestContext.Response.HasStarted; if (data.Count == 0 && started && !endOfRequest) { - // Empty flush + // No data to send and we've already sent the headers return; } + // Make sure all validation is performed before this computes the headers var flags = ComputeLeftToWrite(endOfRequest); - if (!_inOpaqueMode && endOfRequest && _leftToWrite > data.Count) + if (!_inOpaqueMode && endOfRequest && _leftToWrite > 0) { _requestContext.Abort(); // 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; if (data.Count == 0 && started) { - // Empty flush + // No data to send and we've already sent the headers return Helpers.CompletedTask(); } @@ -313,6 +314,7 @@ namespace Microsoft.Net.Http.Server return Helpers.CanceledTask(); } + // Make sure all validation is performed before this computes the headers var flags = ComputeLeftToWrite(); if (_leftToWrite != data.Count) { @@ -464,30 +466,31 @@ namespace Microsoft.Net.Http.Server public override void Write(byte[] buffer, int offset, int count) { // Validates for null and bounds. Allows count == 0. + // TODO: Verbose log parameters var data = new ArraySegment(buffer, offset, count); CheckDisposed(); - // TODO: Verbose log parameters - var contentLength = _requestContext.Response.ContentLength; - 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."); - } - } + CheckWriteCount(count); 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 public IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) #else @@ -512,26 +515,11 @@ namespace Microsoft.Net.Http.Server public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { // Validates for null and bounds. Allows count == 0. + // TODO: Verbose log parameters var data = new ArraySegment(buffer, offset, count); CheckDisposed(); - // TODO: Verbose log parameters - var contentLength = _requestContext.Response.ContentLength; - 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."); - } - } + CheckWriteCount(count); 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. // This all happens inside of ResponseStreamAsyncResult. + // TODO: Verbose log parameters if (string.IsNullOrWhiteSpace(fileName)) { throw new ArgumentNullException("fileName"); } CheckDisposed(); + CheckWriteCount(count); + // We can't mix await and unsafe so separate the unsafe code into another method. await SendFileAsyncCore(fileName, offset, count, cancellationToken); } @@ -557,43 +548,65 @@ namespace Microsoft.Net.Http.Server return Helpers.CompletedTask(); } - var flags = ComputeLeftToWrite(); - if (count == 0 && _leftToWrite != 0) + var started = _requestContext.Response.HasStarted; + if (count == 0 && started) { + // No data to send and we've already sent the headers return Helpers.CompletedTask(); } - if (_leftToWrite >= 0 && count > _leftToWrite) - { - throw new InvalidOperationException(Resources.Exception_TooMuchWritten); - } - if (cancellationToken.IsCancellationRequested) { Abort(ThrowWriteExceptions); return Helpers.CanceledTask(); } - // 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 bytesSent = 0; - var started = _requestContext.Response.HasStarted; 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; if (chunked) { bytesWritten = 0; } - else if (count.HasValue) + else { 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); diff --git a/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStreamAsyncResult.cs b/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStreamAsyncResult.cs index 49f6ab154b..451a327025 100644 --- a/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStreamAsyncResult.cs +++ b/src/Microsoft.Net.Http.Server/RequestProcessing/ResponseStreamAsyncResult.cs @@ -114,33 +114,15 @@ namespace Microsoft.Net.Http.Server } } - internal ResponseStreamAsyncResult(ResponseStream responseStream, string fileName, long offset, - long? count, bool chunked, CancellationToken cancellationToken) + internal ResponseStreamAsyncResult(ResponseStream responseStream, FileStream fileStream, long offset, + long count, bool chunked, CancellationToken cancellationToken) : this(responseStream, cancellationToken) { var boundHandle = responseStream.RequestContext.Server.RequestQueue.BoundHandle; - int bufferSize = 1024 * 64; // TODO: Validate buffer size choice. -#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); - } + _fileStream = fileStream; - if (count == 0 || (!count.HasValue && _fileStream.Length == 0)) + if (count == 0) { _dataChunks = null; _overlapped = new SafeNativeOverlapped(boundHandle, @@ -156,14 +138,14 @@ namespace Microsoft.Net.Http.Server var chunkHeaderBuffer = new ArraySegment(); 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].fromMemory.BufferLength = (uint)chunkHeaderBuffer.Count; objectsToPin[0] = chunkHeaderBuffer.Array; _dataChunks[1].DataChunkType = HttpApi.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromFileHandle; _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(); // 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].fromFile.offset = (ulong)offset; - _dataChunks[0].fromFile.count = (ulong)(count ?? -1); + _dataChunks[0].fromFile.count = (ulong)count; _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 void IOCompleted(uint errorCode) diff --git a/src/Microsoft.Net.Http.Server/WebListener.cs b/src/Microsoft.Net.Http.Server/WebListener.cs index f7dde384fc..cb72439299 100644 --- a/src/Microsoft.Net.Http.Server/WebListener.cs +++ b/src/Microsoft.Net.Http.Server/WebListener.cs @@ -40,7 +40,7 @@ namespace Microsoft.Net.Http.Server // returned from HttpReceiveClientCertificate when using the // FileCompletionNotificationModes.SkipCompletionPortOnSuccess flag. // 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 // flag is only used on Win8 and later. internal static readonly bool SkipIOCPCallbackOnSuccess = ComNetOS.IsWin8orLater; diff --git a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ResponseSendFileTests.cs b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ResponseSendFileTests.cs index 7cc9269f12..62616c6a6e 100644 --- a/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ResponseSendFileTests.cs +++ b/test/Microsoft.AspNetCore.Server.WebListener.FunctionalTests/ResponseSendFileTests.cs @@ -221,12 +221,14 @@ namespace Microsoft.AspNetCore.Server.WebListener using (Utilities.CreateHttpServer(out address, async httpContext => { var sendFile = httpContext.Features.Get(); - await sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None); + await Assert.ThrowsAsync(() => + sendFile.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None)); completed = true; })) { - await Assert.ThrowsAsync(() => SendRequestAsync(address)); - Assert.False(completed); + var response = await SendRequestAsync(address); + response.EnsureSuccessStatusCode(); + Assert.True(completed); } } @@ -238,12 +240,14 @@ namespace Microsoft.AspNetCore.Server.WebListener using (Utilities.CreateHttpServer(out address, async httpContext => { var sendFile = httpContext.Features.Get(); - await sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None); + await Assert.ThrowsAsync(() => + sendFile.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None)); completed = true; })) { - await Assert.ThrowsAsync(() => SendRequestAsync(address)); - Assert.False(completed); + var response = await SendRequestAsync(address); + response.EnsureSuccessStatusCode(); + Assert.True(completed); } } diff --git a/test/Microsoft.Net.Http.Server.FunctionalTests/ResponseSendFileTests.cs b/test/Microsoft.Net.Http.Server.FunctionalTests/ResponseSendFileTests.cs index 89073fefbc..a34729f912 100644 --- a/test/Microsoft.Net.Http.Server.FunctionalTests/ResponseSendFileTests.cs +++ b/test/Microsoft.Net.Http.Server.FunctionalTests/ResponseSendFileTests.cs @@ -30,14 +30,15 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await Assert.ThrowsAsync(() => context.Response.SendFileAsync("Missing.txt", 0, null, CancellationToken.None)); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; + response.EnsureSuccessStatusCode(); } } @@ -47,13 +48,13 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable ignored; Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length"); @@ -68,13 +69,13 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await context.Response.SendFileAsync(RelativeFilePath, 0, null, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable ignored; Assert.False(response.Content.Headers.TryGetValues("content-length", out ignored), "Content-Length"); @@ -89,13 +90,13 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -110,14 +111,14 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); 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(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -132,13 +133,13 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await context.Response.SendFileAsync(AbsoluteFilePath, 0, FileLength / 2, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -153,14 +154,15 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await Assert.ThrowsAsync( () => context.Response.SendFileAsync(AbsoluteFilePath, 1234567, null, CancellationToken.None)); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; + response.EnsureSuccessStatusCode(); } } @@ -170,14 +172,15 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await Assert.ThrowsAsync( () => context.Response.SendFileAsync(AbsoluteFilePath, 0, 1234567, CancellationToken.None)); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; + response.EnsureSuccessStatusCode(); } } @@ -187,13 +190,13 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await context.Response.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -212,7 +215,7 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); await context.Response.SendFileAsync(emptyFilePath, 0, null, CancellationToken.None); @@ -221,7 +224,7 @@ namespace Microsoft.Net.Http.Server context.Dispose(); File.Delete(emptyFilePath); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.False(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -236,13 +239,13 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); context.Response.Headers["Content-lenGth"] = FileLength.ToString(); await context.Response.SendFileAsync(AbsoluteFilePath, 0, null, CancellationToken.None); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -258,14 +261,14 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); context.Response.Headers["Content-lenGth"] = "10"; await context.Response.SendFileAsync(AbsoluteFilePath, 0, 10, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; Assert.True(response.Content.Headers.TryGetValues("content-length", out contentLength), "Content-Length"); @@ -281,14 +284,14 @@ namespace Microsoft.Net.Http.Server string address; using (var server = Utilities.CreateHttpServer(out address)) { - Task responseTask = SendRequestAsync(address); + var responseTask = SendRequestAsync(address); var context = await server.AcceptAsync(); context.Response.Headers["Content-lenGth"] = "0"; await context.Response.SendFileAsync(AbsoluteFilePath, 0, 0, CancellationToken.None); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); IEnumerable contentLength; 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); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); 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); context.Dispose(); - HttpResponseMessage response = await responseTask; + var response = await responseTask; Assert.Equal(200, (int)response.StatusCode); Assert.Equal(FileLength * 2, (await response.Content.ReadAsByteArrayAsync()).Length); }