From f253dbc0c074337ea7cb21c6809b88ab9be991bd Mon Sep 17 00:00:00 2001 From: Cesar Blum Silveira Date: Thu, 6 Apr 2017 19:09:40 -0700 Subject: [PATCH] Split transport-specific tests and general tests into distinct test projects (#1588). * Rename EngineTests to LibuvTransportTests. * Move libuv-specific tests into their own test project. * Move LibuvOutputConsumerTests.AllocCommitCanBeCalledAfterConnectionClose to new OutputProducerTests class and rename it to WritesNoopAfterConnectionCloses. * Remove TransportContext from TestServiceContext. * Make KestrelTests depend on Kestrel.Core only. * Rename Microsoft.AspNetCore.Server.Kestrel.KestrelTests to Microsoft.AspNetCore.Server.Kestrel.Core.Tests. * Add Microsoft.AspNetCore.Server.Kestrel.Tests test project for WebHostBuilderKestrelExtensionsTests. * Increase socket receive timeout in MaxRequestBufferSizeTests to mitigate flakiness. * Anything using TestServer should be a functional test. * Move out of LibuvTransportTests tests that are not specific to LibuvTransport. - Move to RequestTests: - Http11 (rename to Http11KeptAliveByDefault) - Http10ContentLength (rename to Http10NotKeptAliveByDefault) - Http10KeepAlive - Http10KeepAliveNotUsedIfResponseContentLengthNotSet (rename to Http10KeepAliveNotHonoredIfResponseContentLengthNotSet) - Http10ContentLengthKeepAlive (rename to Http10KeepAliveHonoredIfResponseContentLengthSet) - Expect100ContinueForBody (rename to Expect100ContinueHonored) - ZeroContentLengthAssumedOnNonKeepAliveRequestsWithoutContentLengthOrTransferEncodingHeader - ConnectionClosesWhenFinReceivedBeforeRequestCompletes (test was actually not marked as Theory, and was incorrect) - RequestsCanBeAbortedMidRead - RequestHeadersAreResetOnEachRequest - UpgradeRequestIsNotKeptAliveOrChunked - HeadersAndStreamsAreReused (rename to HeadersAndStreamsAreReusedAcrossRequests) - Move to ResponseTests: - Http10RequestReceivesHttp11Response (rename to Http11ResponseSentToHttp10Request) - ZeroContentLengthSetAutomaticallyAfterNoWrites - ZeroContentLengthSetAutomaticallyForNonKeepAliveRequests - ZeroContentLengthNotSetAutomaticallyForHeadRequests - ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes - ConnectionClosedAfter101Response - ThrowingResultsIn500Response - ThrowingAfterWritingKillsConnection - ThrowingAfterPartialWriteKillsConnection - ThrowingInOnStartingResultsInFailedWritesAnd500Response - ThrowingInOnCompletedIsLoggedAndClosesConnection - FailedWritesResultInAbortedRequest - NoErrorsLoggedWhenServerEndsConnectionBeforeClient - NoResponseSentWhenConnectionIsClosedByServerBeforeClientFinishesSendingRequest - ResponseHeadersAreResetOnEachRequest - OnStartingCallbacksAreCalledInLastInFirstOutOrder - OnCompletedCallbacksAreCalledInLastInFirstOutOrder - Remove: - RePathEscapeTests (theory data to HttpParsingData) - ReDisconnectingClient (what was that testing?) --- KestrelHttpServer.sln | 37 +- .../Properties/AssemblyInfo.cs | 3 +- .../AsciiDecoding.cs | 2 +- .../ChunkWriterTests.cs | 2 +- .../CreateIPEndpointTests.cs | 2 +- .../DateHeaderValueManagerTests.cs | 2 +- .../FrameHeadersTests.cs | 2 +- .../FrameRequestHeadersTests.cs | 2 +- .../FrameRequestStreamTests.cs | 2 +- .../FrameResponseHeadersTests.cs | 2 +- .../FrameResponseStreamTests.cs | 4 +- .../FrameTests.cs | 2 +- .../HttpParserTests.cs | 2 +- .../HttpUtilitiesTest.cs | 2 +- .../KestrelEventSourceTests.cs | 2 +- .../KestrelServerLimitsTests.cs | 2 +- .../KestrelServerOptionsTests.cs | 2 +- .../KestrelServerTests.cs | 2 +- .../LoggingThreadPoolTests.cs | 2 +- .../MessageBodyTests.cs | 2 +- ...pNetCore.Server.Kestrel.Core.Tests.csproj} | 4 +- .../OutputProducerTests.cs | 61 + .../PathNormalizerTests.cs | 2 +- .../PipeOptionsTests.cs | 4 +- .../PipelineExtensionTests.cs | 2 +- .../ServerAddressTests.cs | 2 +- .../StreamSocketOutputTests.cs | 2 +- .../TestHelpers/AssertExtensions.cs | 0 .../TestHelpers/MockFrameControl.cs | 2 +- .../TestInput.cs | 2 +- .../xunit.runner.json | 0 .../ChunkedRequestTests.cs | 2 +- .../ChunkedResponseTests.cs | 2 +- .../ConnectionAdapterTests.cs | 2 +- .../DefaultHeaderTests.cs | 2 +- .../HttpsConnectionAdapterTests.cs | 2 +- .../MaxRequestBufferSizeTests.cs | 2 +- .../RequestTargetProcessingTests.cs | 2 +- .../RequestTests.cs | 564 +++++++ .../ResponseTests.cs | 757 +++++++++ .../TestServer.cs | 19 +- ...pNetCore.Server.Kestrel.Performance.csproj | 1 - ...oft.AspNetCore.Server.Kestrel.Tests.csproj | 31 + .../WebHostBuilderKestrelExtensionsTests.cs | 2 +- .../xunit.runner.json | 5 + .../EngineTests.cs | 1431 ----------------- .../ConnectionTests.cs | 16 +- .../LibuvOutputConsumerTests.cs | 66 +- .../LibuvTransportFactoryTests.cs | 3 +- .../LibuvTransportOptionsTests.cs | 3 +- .../LibuvTransportTests.cs | 80 + .../ListenerPrimaryTests.cs | 61 +- ...erver.Kestrel.Transport.Libuv.Tests.csproj | 35 + .../MultipleLoopTests.cs | 6 +- .../NetworkingTests.cs | 2 +- .../TestHelpers}/MockConnection.cs | 3 +- .../TestHelpers/MockConnectionHandler.cs | 2 +- .../TestHelpers/MockLibuv.cs | 2 +- .../TestHelpers/MockSocket.cs | 2 +- .../TestHelpers/TestConstants.cs | 2 +- .../TestHelpers/TestLibuvTransportContext.cs | 26 + .../UvStreamHandleTests.cs | 4 +- .../UvTimerHandleTests.cs | 2 +- .../xunit.runner.json | 6 + test/shared/HttpParsingData.cs | 2 + test/shared/TestApplicationErrorLogger.cs | 1 - test/shared/TestServiceContext.cs | 16 - 67 files changed, 1727 insertions(+), 1596 deletions(-) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/AsciiDecoding.cs (97%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/ChunkWriterTests.cs (95%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/CreateIPEndpointTests.cs (94%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/DateHeaderValueManagerTests.cs (98%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/FrameHeadersTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/FrameRequestHeadersTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/FrameRequestStreamTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/FrameResponseHeadersTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/FrameResponseStreamTests.cs (96%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/FrameTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/HttpParserTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/HttpUtilitiesTest.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/KestrelEventSourceTests.cs (94%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/KestrelServerLimitsTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/KestrelServerOptionsTests.cs (92%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/KestrelServerTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/LoggingThreadPoolTests.cs (96%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/MessageBodyTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests/Microsoft.AspNetCore.Server.KestrelTests.csproj => Microsoft.AspNetCore.Server.Kestrel.Core.Tests/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj} (88%) create mode 100644 test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/OutputProducerTests.cs rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/PathNormalizerTests.cs (97%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/PipeOptionsTests.cs (94%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/PipelineExtensionTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/ServerAddressTests.cs (98%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/StreamSocketOutputTests.cs (98%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/TestHelpers/AssertExtensions.cs (100%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/TestHelpers/MockFrameControl.cs (92%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/TestInput.cs (97%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Core.Tests}/xunit.runner.json (100%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/ChunkedRequestTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/ChunkedResponseTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/ConnectionAdapterTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/DefaultHeaderTests.cs (96%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/HttpsConnectionAdapterTests.cs (99%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/RequestTargetProcessingTests.cs (98%) rename test/{shared => Microsoft.AspNetCore.Server.Kestrel.FunctionalTests}/TestServer.cs (73%) create mode 100644 test/Microsoft.AspNetCore.Server.Kestrel.Tests/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Kestrel.Tests}/WebHostBuilderKestrelExtensionsTests.cs (96%) create mode 100644 test/Microsoft.AspNetCore.Server.Kestrel.Tests/xunit.runner.json delete mode 100644 test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/ConnectionTests.cs (76%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/LibuvOutputConsumerTests.cs (90%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/LibuvTransportFactoryTests.cs (92%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/LibuvTransportOptionsTests.cs (87%) create mode 100644 test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportTests.cs rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/ListenerPrimaryTests.cs (80%) create mode 100644 test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.csproj rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/MultipleLoopTests.cs (98%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/NetworkingTests.cs (98%) rename test/{shared => Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers}/MockConnection.cs (92%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/TestHelpers/MockConnectionHandler.cs (96%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/TestHelpers/MockLibuv.cs (98%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/TestHelpers/MockSocket.cs (89%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/TestHelpers/TestConstants.cs (76%) create mode 100644 test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestLibuvTransportContext.cs rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/UvStreamHandleTests.cs (87%) rename test/{Microsoft.AspNetCore.Server.KestrelTests => Microsoft.AspNetCore.Server.Transport.Libuv.Tests}/UvTimerHandleTests.cs (96%) create mode 100644 test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/xunit.runner.json diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 5e243d4c7c..5119c68fa0 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26327.1 +VisualStudioVersion = 15.0.26405.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}" ProjectSection(SolutionItems) = preProject @@ -24,11 +24,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC test\shared\HttpParsingData.cs = test\shared\HttpParsingData.cs test\shared\KestrelTestLoggerFactory.cs = test\shared\KestrelTestLoggerFactory.cs test\shared\LifetimeNotImplemented.cs = test\shared\LifetimeNotImplemented.cs - test\shared\MockConnection.cs = test\shared\MockConnection.cs test\shared\MockFrameControl.cs = test\shared\MockFrameControl.cs test\shared\MockSocketOutput.cs = test\shared\MockSocketOutput.cs test\shared\MockSystemClock.cs = test\shared\MockSystemClock.cs - test\shared\SocketInputExtensions.cs = test\shared\SocketInputExtensions.cs test\shared\StringExtensions.cs = test\shared\StringExtensions.cs test\shared\TestApp.cs = test\shared\TestApp.cs test\shared\TestApplicationErrorLogger.cs = test\shared\TestApplicationErrorLogger.cs @@ -36,13 +34,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC test\shared\TestFrame.cs = test\shared\TestFrame.cs test\shared\TestKestrelTrace.cs = test\shared\TestKestrelTrace.cs test\shared\TestResources.cs = test\shared\TestResources.cs - test\shared\TestServer.cs = test\shared\TestServer.cs test\shared\TestServiceContext.cs = test\shared\TestServiceContext.cs EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Core", "src\Microsoft.AspNetCore.Server.Kestrel.Core\Microsoft.AspNetCore.Server.Kestrel.Core.csproj", "{F510611A-3BEE-4B88-A613-5F4A74ED82A1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.KestrelTests", "test\Microsoft.AspNetCore.Server.KestrelTests\Microsoft.AspNetCore.Server.KestrelTests.csproj", "{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Core.Tests", "test\Microsoft.AspNetCore.Server.Kestrel.Core.Tests\Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj", "{37F3BFB2-6454-49E5-9D7F-581BF755CCFE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp", "samples\SampleApp\SampleApp.csproj", "{2C3CB3DC-EEBF-4F52-9E1C-4F2F972E76C3}" EndProject @@ -67,6 +64,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions", "src\Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions\Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.csproj", "{2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests", "test\Microsoft.AspNetCore.Server.Transport.Libuv.Tests\Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.csproj", "{D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Tests", "test\Microsoft.AspNetCore.Server.Kestrel.Tests\Microsoft.AspNetCore.Server.Kestrel.Tests.csproj", "{4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -209,6 +210,30 @@ Global {2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}.Release|x64.Build.0 = Release|Any CPU {2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}.Release|x86.ActiveCfg = Release|Any CPU {2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D}.Release|x86.Build.0 = Release|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Debug|x64.ActiveCfg = Debug|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Debug|x64.Build.0 = Debug|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Debug|x86.ActiveCfg = Debug|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Debug|x86.Build.0 = Debug|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Release|Any CPU.Build.0 = Release|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Release|x64.ActiveCfg = Release|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Release|x64.Build.0 = Release|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Release|x86.ActiveCfg = Release|Any CPU + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4}.Release|x86.Build.0 = Release|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Debug|x64.ActiveCfg = Debug|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Debug|x64.Build.0 = Debug|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Debug|x86.ActiveCfg = Debug|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Debug|x86.Build.0 = Debug|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Release|Any CPU.Build.0 = Release|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Release|x64.ActiveCfg = Release|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Release|x64.Build.0 = Release|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Release|x86.ActiveCfg = Release|Any CPU + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -227,5 +252,7 @@ Global {A76B8C8C-0DC5-4DD3-9B1F-02E51A0915F4} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} {56139957-5C29-4E7D-89BD-7D20598B4EAF} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} {2E9CB89D-EC8F-4DD9-A72B-08D5BABF752D} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} + {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} + {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/AssemblyInfo.cs index 8d850b7d81..c93c4396ee 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/AssemblyInfo.cs @@ -4,5 +4,6 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.KestrelTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] [assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] +[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/AsciiDecoding.cs similarity index 97% rename from test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/AsciiDecoding.cs index 7d2a812930..63ed4d4b93 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/AsciiDecoding.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/AsciiDecoding.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Internal.System; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class AsciiDecodingTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ChunkWriterTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/ChunkWriterTests.cs similarity index 95% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ChunkWriterTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/ChunkWriterTests.cs index d7a7f35770..722c0281ab 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ChunkWriterTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/ChunkWriterTests.cs @@ -6,7 +6,7 @@ using System.Text; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class ChunkWriterTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/CreateIPEndpointTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/CreateIPEndpointTests.cs similarity index 94% rename from test/Microsoft.AspNetCore.Server.KestrelTests/CreateIPEndpointTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/CreateIPEndpointTests.cs index e8a3cdfd2c..bf120febd4 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/CreateIPEndpointTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/CreateIPEndpointTests.cs @@ -5,7 +5,7 @@ using System.Net; using Microsoft.AspNetCore.Server.Kestrel.Core; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class CreateIPEndpointTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/DateHeaderValueManagerTests.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/DateHeaderValueManagerTests.cs index 810814219d..875e0f5d73 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/DateHeaderValueManagerTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/DateHeaderValueManagerTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class DateHeaderValueManagerTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameHeadersTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameHeadersTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/FrameHeadersTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameHeadersTests.cs index 06a0b4cc15..65774c6022 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameHeadersTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameHeadersTests.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameHeadersTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameRequestHeadersTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameRequestHeadersTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/FrameRequestHeadersTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameRequestHeadersTests.cs index cc8baef3c6..182ffbadeb 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameRequestHeadersTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameRequestHeadersTests.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.Extensions.Primitives; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameRequestHeadersTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameRequestStreamTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameRequestStreamTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/FrameRequestStreamTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameRequestStreamTests.cs index 62d6a41ffd..9f723aa903 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameRequestStreamTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameRequestStreamTests.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Moq; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameRequestStreamTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameResponseHeadersTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameResponseHeadersTests.cs index 86f420673b..7ca2c6eed7 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseHeadersTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameResponseHeadersTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Primitives; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameResponseHeadersTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseStreamTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameResponseStreamTests.cs similarity index 96% rename from test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseStreamTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameResponseStreamTests.cs index 289125fbfc..c6a3ec8a4d 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameResponseStreamTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameResponseStreamTests.cs @@ -5,10 +5,10 @@ using System; using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers; +using Microsoft.AspNetCore.Server.Kestrel.Core.Tests.TestHelpers; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameResponseStreamTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs index bae0765e88..b5c0b1546f 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/FrameTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/FrameTests.cs @@ -26,7 +26,7 @@ using Microsoft.Extensions.Primitives; using Moq; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameTests : IDisposable { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/HttpParserTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/HttpParserTests.cs index db1d324ee0..8ef554e4f5 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpParserTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/HttpParserTests.cs @@ -16,7 +16,7 @@ using Microsoft.Extensions.Logging; using Moq; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class HttpParserTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpUtilitiesTest.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/HttpUtilitiesTest.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/HttpUtilitiesTest.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/HttpUtilitiesTest.cs index 37b227bd5c..08efbb4d40 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpUtilitiesTest.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/HttpUtilitiesTest.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Internal.System; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class HttpUtilitiesTest { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelEventSourceTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelEventSourceTests.cs similarity index 94% rename from test/Microsoft.AspNetCore.Server.KestrelTests/KestrelEventSourceTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelEventSourceTests.cs index fc51df41f3..4f1944387b 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelEventSourceTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelEventSourceTests.cs @@ -7,7 +7,7 @@ using System.Reflection; using Microsoft.AspNetCore.Server.Kestrel.Core; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class KestrelEventSourceTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerLimitsTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerLimitsTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerLimitsTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerLimitsTests.cs index ff30838839..a4bf75c59b 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerLimitsTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerLimitsTests.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.Kestrel.Core; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class KestrelServerLimitsTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerOptionsTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerOptionsTests.cs similarity index 92% rename from test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerOptionsTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerOptionsTests.cs index 13a1d442b6..e0e97f2aac 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerOptionsTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerOptionsTests.cs @@ -5,7 +5,7 @@ using System.Net; using Microsoft.AspNetCore.Server.Kestrel.Core; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class KestrelServerOptionsTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerTests.cs index 8c3e5fa0b2..bbb3f21c1d 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/KestrelServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/KestrelServerTests.cs @@ -16,7 +16,7 @@ using Microsoft.Extensions.Options; using Moq; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class KestrelServerTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/LoggingThreadPoolTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/LoggingThreadPoolTests.cs similarity index 96% rename from test/Microsoft.AspNetCore.Server.KestrelTests/LoggingThreadPoolTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/LoggingThreadPoolTests.cs index 3209179abc..9616315bae 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/LoggingThreadPoolTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/LoggingThreadPoolTests.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class LoggingThreadPoolTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MessageBodyTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/MessageBodyTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/MessageBodyTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/MessageBodyTests.cs index da912b65a8..5b06b25acc 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/MessageBodyTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/MessageBodyTests.cs @@ -16,7 +16,7 @@ using Moq; using Xunit; using Xunit.Sdk; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { /// /// Summary description for MessageBodyTests diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/Microsoft.AspNetCore.Server.KestrelTests.csproj b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj similarity index 88% rename from test/Microsoft.AspNetCore.Server.KestrelTests/Microsoft.AspNetCore.Server.KestrelTests.csproj rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj index 748a4a79f9..be64007718 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/Microsoft.AspNetCore.Server.KestrelTests.csproj +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/Microsoft.AspNetCore.Server.Kestrel.Core.Tests.csproj @@ -22,12 +22,12 @@ - - + + diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/OutputProducerTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/OutputProducerTests.cs new file mode 100644 index 0000000000..ff5591b109 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/OutputProducerTests.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; +using Microsoft.AspNetCore.Testing; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests +{ + public class OutputProducerTests + { + private readonly PipeFactory _pipeFactory; + + public OutputProducerTests() + { + _pipeFactory = new PipeFactory(); + } + + public void Dispose() + { + _pipeFactory.Dispose(); + } + + [Fact] + public void WritesNoopAfterConnectionCloses() + { + var pipeOptions = new PipeOptions + { + ReaderScheduler = Mock.Of(), + }; + + using (var socketOutput = CreateOutputProducer(pipeOptions)) + { + // Close + socketOutput.Dispose(); + + var called = false; + + ((ISocketOutput)socketOutput).Write((buffer, state) => + { + called = true; + }, + null); + + Assert.False(called); + } + } + + private OutputProducer CreateOutputProducer(PipeOptions pipeOptions) + { + var pipe = _pipeFactory.Create(pipeOptions); + var serviceContext = new TestServiceContext(); + var frame = new Frame(null, new FrameContext { ServiceContext = serviceContext }); + var socketOutput = new OutputProducer(pipe.Writer, frame, "0", serviceContext.Log); + + return socketOutput; + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/PathNormalizerTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PathNormalizerTests.cs similarity index 97% rename from test/Microsoft.AspNetCore.Server.KestrelTests/PathNormalizerTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PathNormalizerTests.cs index 644580a10d..1440c1ba6e 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/PathNormalizerTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PathNormalizerTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Internal.System; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class PathNormalizerTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/PipeOptionsTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs similarity index 94% rename from test/Microsoft.AspNetCore.Server.KestrelTests/PipeOptionsTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs index a542711393..5a4cc05601 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/PipeOptionsTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipeOptionsTests.cs @@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Testing; using Moq; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class PipeOptionsTests { @@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests [Theory] [InlineData(10, 10, 10)] [InlineData(null, 0, 0)] - public void LibuvInputPipeOptionsConfiguredCorrectly(long? maxRequestBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh) + public void InputPipeOptionsConfiguredCorrectly(long? maxRequestBufferSize, long expectedMaximumSizeLow, long expectedMaximumSizeHigh) { var serviceContext = new TestServiceContext(); serviceContext.ServerOptions.Limits.MaxRequestBufferSize = maxRequestBufferSize; diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/PipelineExtensionTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/PipelineExtensionTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs index 614e81bb39..ec85ef3b5e 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/PipelineExtensionTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Internal.System; using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class PipelineExtensionTests : IDisposable { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/ServerAddressTests.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/ServerAddressTests.cs index a412527653..9c9c7a654b 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ServerAddressTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/ServerAddressTests.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.Kestrel.Core; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class ServerAddressTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/StreamSocketOutputTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/StreamSocketOutputTests.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/StreamSocketOutputTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/StreamSocketOutputTests.cs index bc481be73f..0a3f383dd9 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/StreamSocketOutputTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/StreamSocketOutputTests.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal; using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class StreamSocketOutputTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/AssertExtensions.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestHelpers/AssertExtensions.cs similarity index 100% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/AssertExtensions.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestHelpers/AssertExtensions.cs diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockFrameControl.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestHelpers/MockFrameControl.cs similarity index 92% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockFrameControl.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestHelpers/MockFrameControl.cs index bf4b4b0d3b..cf26ef8f68 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockFrameControl.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestHelpers/MockFrameControl.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.Extensions.Internal; -namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests.TestHelpers { public class MockFrameControl : IFrameControl { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestInput.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestInput.cs similarity index 97% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestInput.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestInput.cs index f5707b962c..50f8c9c30d 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestInput.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/TestInput.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Internal; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { class TestInput : ITimeoutControl, IFrameControl, IDisposable { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/xunit.runner.json b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/xunit.runner.json similarity index 100% rename from test/Microsoft.AspNetCore.Server.KestrelTests/xunit.runner.json rename to test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/xunit.runner.json diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ChunkedRequestTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ChunkedRequestTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ChunkedRequestTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ChunkedRequestTests.cs index 0ed47420b9..be1718d76a 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ChunkedRequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ChunkedRequestTests.cs @@ -13,7 +13,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Internal; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class ChunkedRequestTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ChunkedResponseTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ChunkedResponseTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ChunkedResponseTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ChunkedResponseTests.cs index c52c1fa5bc..d68283a1ab 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ChunkedResponseTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ChunkedResponseTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class ChunkedResponseTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionAdapterTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ConnectionAdapterTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionAdapterTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ConnectionAdapterTests.cs index 57e89f56f2..0d8150f62c 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionAdapterTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ConnectionAdapterTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class ConnectionAdapterTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/DefaultHeaderTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/DefaultHeaderTests.cs similarity index 96% rename from test/Microsoft.AspNetCore.Server.KestrelTests/DefaultHeaderTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/DefaultHeaderTests.cs index 2493387b9b..91ff1816cf 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/DefaultHeaderTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/DefaultHeaderTests.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Internal; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class DefaultHeaderTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionAdapterTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsConnectionAdapterTests.cs similarity index 99% rename from test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionAdapterTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsConnectionAdapterTests.cs index 25c637b628..e4d6e0eb22 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/HttpsConnectionAdapterTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/HttpsConnectionAdapterTests.cs @@ -20,7 +20,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Internal; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class HttpsConnectionAdapterTests { diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs index d60ae18dc4..8803ded61a 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/MaxRequestBufferSizeTests.cs @@ -248,7 +248,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests // Timeouts large enough to prevent false positives, but small enough to fail quickly. socket.SendTimeout = 10 * 1000; - socket.ReceiveTimeout = 10 * 1000; + socket.ReceiveTimeout = 30 * 1000; socket.Connect(IPAddress.Loopback, port); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/RequestTargetProcessingTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTargetProcessingTests.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/RequestTargetProcessingTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTargetProcessingTests.cs index 3dea2881cf..3d9a55b9f6 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/RequestTargetProcessingTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTargetProcessingTests.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class RequestTargetProcessingTests { diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs index a8d20e1d69..abb9dace50 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/RequestTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Concurrent; using System.Globalization; using System.IO; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Sockets; @@ -15,6 +16,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; @@ -35,6 +38,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests private const int _connectionResetEventId = 19; private const int _semaphoreWaitTimeout = 2500; + public static TheoryData ConnectionAdapterData => new TheoryData + { + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)), + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)) + { + ConnectionAdapters = { new PassThroughConnectionAdapter() } + } + }; + [Theory] [InlineData(10 * 1024 * 1024, true)] // In the following dataset, send at least 2GB. @@ -633,6 +645,558 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests } } + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Http11KeptAliveByDefault(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + "GET / HTTP/1.1", + "Connection: close", + "Content-Length: 7", + "", + "Goodbye"); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 7", + "", + "Goodbye"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Http10NotKeptAliveByDefault(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoApp, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.0", + "Content-Length: 11", + "", + "Hello World"); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "", + "Hello World"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Http10KeepAlive(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "Connection: keep-alive", + "", + "POST / HTTP/1.0", + "Content-Length: 7", + "", + "Goodbye"); + await connection.Receive( + "HTTP/1.1 200 OK", + "Connection: keep-alive", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "\r\n"); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 7", + "", + "Goodbye"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Http10KeepAliveNotHonoredIfResponseContentLengthNotSet(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoApp, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "Connection: keep-alive", + "", + ""); + + await connection.Receive( + "HTTP/1.1 200 OK", + "Connection: keep-alive", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "\r\n"); + + await connection.Send( + "POST / HTTP/1.0", + "Connection: keep-alive", + "Content-Length: 7", + "", + "Goodbye"); + + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "", + "Goodbye"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Http10KeepAliveHonoredIfResponseContentLengthSet(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.0", + "Content-Length: 11", + "Connection: keep-alive", + "", + "Hello World"); + + await connection.Receive( + "HTTP/1.1 200 OK", + "Connection: keep-alive", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello World"); + + await connection.Send( + "POST / HTTP/1.0", + "Connection: keep-alive", + "Content-Length: 11", + "", + "Hello Again"); + + await connection.Receive( + "HTTP/1.1 200 OK", + "Connection: keep-alive", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello Again"); + + await connection.Send( + "POST / HTTP/1.0", + "Content-Length: 7", + "", + "Goodbye"); + + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 7", + "", + "Goodbye"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Expect100ContinueHonored(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.1", + "Expect: 100-continue", + "Connection: close", + "Content-Length: 11", + "\r\n"); + await connection.Receive( + "HTTP/1.1 100 Continue", + "", + ""); + await connection.Send("Hello World"); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello World"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ZeroContentLengthAssumedOnNonKeepAliveRequestsWithoutContentLengthOrTransferEncodingHeader(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(async httpContext => + { + // This will hang if 0 content length is not assumed by the server + Assert.Equal(0, await httpContext.Request.Body.ReadAsync(new byte[1], 0, 1).TimeoutAfter(TimeSpan.FromSeconds(10))); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + // Use Send instead of SendEnd to ensure the connection will remain open while + // the app runs and reads 0 bytes from the body nonetheless. This checks that + // https://github.com/aspnet/KestrelHttpServer/issues/1104 is not regressing. + await connection.Send( + "GET / HTTP/1.1", + "Connection: close", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.1"); + connection.Shutdown(SocketShutdown.Send); + await connection.ReceiveForcedEnd(); + } + + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.1", + "Content-Length: 7"); + connection.Shutdown(SocketShutdown.Send); + await connection.ReceiveForcedEnd(); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task RequestsCanBeAbortedMidRead(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + var readTcs = new TaskCompletionSource(); + var registrationTcs = new TaskCompletionSource(); + var requestId = 0; + + using (var server = new TestServer(async httpContext => + { + requestId++; + + var response = httpContext.Response; + var request = httpContext.Request; + var lifetime = httpContext.Features.Get(); + + lifetime.RequestAborted.Register(() => registrationTcs.TrySetResult(requestId)); + + if (requestId == 1) + { + response.Headers["Content-Length"] = new[] { "5" }; + + await response.WriteAsync("World"); + } + else + { + var readTask = request.Body.CopyToAsync(Stream.Null); + + lifetime.Abort(); + + try + { + await readTask; + } + catch (Exception ex) + { + readTcs.SetException(ex); + throw; + } + + readTcs.SetException(new Exception("This shouldn't be reached.")); + } + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + // Never send the body so CopyToAsync always fails. + await connection.Send( + "POST / HTTP/1.1", + "Content-Length: 5", + "", + "HelloPOST / HTTP/1.1", + "Content-Length: 5", + "", + ""); + + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 5", + "", + "World"); + } + } + + await Assert.ThrowsAsync(async () => await readTcs.Task); + + // The cancellation token for only the last request should be triggered. + var abortedRequestId = await registrationTcs.Task; + Assert.Equal(2, abortedRequestId); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task RequestHeadersAreResetOnEachRequest(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + IHeaderDictionary originalRequestHeaders = null; + var firstRequest = true; + + using (var server = new TestServer(httpContext => + { + var requestFeature = httpContext.Features.Get(); + + if (firstRequest) + { + originalRequestHeaders = requestFeature.Headers; + requestFeature.Headers = new FrameRequestHeaders(); + firstRequest = false; + } + else + { + Assert.Same(originalRequestHeaders, requestFeature.Headers); + } + + return TaskCache.CompletedTask; + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task UpgradeRequestIsNotKeptAliveOrChunked(ListenOptions listenOptions) + { + const string message = "Hello World"; + + var testContext = new TestServiceContext(); + + using (var server = new TestServer(async context => + { + var upgradeFeature = context.Features.Get(); + var duplexStream = await upgradeFeature.UpgradeAsync(); + + var buffer = new byte[message.Length]; + var read = 0; + while (read < message.Length) + { + read += await duplexStream.ReadAsync(buffer, read, buffer.Length - read).TimeoutAfter(TimeSpan.FromSeconds(10)); + } + + await duplexStream.WriteAsync(buffer, 0, read); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "Connection: Upgrade", + "", + message); + await connection.ReceiveForcedEnd( + "HTTP/1.1 101 Switching Protocols", + "Connection: Upgrade", + $"Date: {testContext.DateHeaderValue}", + "", + message); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task HeadersAndStreamsAreReusedAcrossRequests(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + var streamCount = 0; + var requestHeadersCount = 0; + var responseHeadersCount = 0; + var loopCount = 20; + Stream lastStream = null; + IHeaderDictionary lastRequestHeaders = null; + IHeaderDictionary lastResponseHeaders = null; + + using (var server = new TestServer(async context => + { + if (context.Request.Body != lastStream) + { + lastStream = context.Request.Body; + streamCount++; + } + if (context.Request.Headers != lastRequestHeaders) + { + lastRequestHeaders = context.Request.Headers; + requestHeadersCount++; + } + if (context.Response.Headers != lastResponseHeaders) + { + lastResponseHeaders = context.Response.Headers; + responseHeadersCount++; + } + + var ms = new MemoryStream(); + await context.Request.Body.CopyToAsync(ms); + var request = ms.ToArray(); + + context.Response.ContentLength = request.Length; + + await context.Response.Body.WriteAsync(request, 0, request.Length); + }, testContext)) + { + using (var connection = server.CreateConnection()) + { + var requestData = + Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount) + .Concat(new[] { "GET / HTTP/1.1\r\nContent-Length: 7\r\nConnection: close\r\n\r\nGoodbye" }); + + var response = string.Join("\r\n", new string[] { + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + ""}); + + var lastResponse = string.Join("\r\n", new string[] + { + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 7", + "", + "Goodbye" + }); + + var responseData = + Enumerable.Repeat(response, loopCount) + .Concat(new[] { lastResponse }); + + await connection.Send(requestData.ToArray()); + + await connection.ReceiveEnd(responseData.ToArray()); + } + + Assert.Equal(1, streamCount); + Assert.Equal(1, requestHeadersCount); + Assert.Equal(1, responseHeadersCount); + } + } + private async Task TestRemoteIPAddress(string registerAddress, string requestAddress, string expectAddress) { var builder = new WebHostBuilder() diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs index d68624bd3b..02b2590e16 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -14,6 +16,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Testing; @@ -27,6 +30,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { public class ResponseTests { + public static TheoryData ConnectionAdapterData => new TheoryData + { + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)), + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)) + { + ConnectionAdapters = { new PassThroughConnectionAdapter() } + } + }; + [Fact] public async Task LargeDownload() { @@ -1529,6 +1541,751 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests } } + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task Http11ResponseSentToHttp10Request(ListenOptions listenOptions) + { + var serviceContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EchoApp, serviceContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.0", + "Content-Length: 11", + "", + "Hello World"); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {serviceContext.DateHeaderValue}", + "", + "Hello World"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EmptyApp, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + "GET / HTTP/1.0", + "Connection: keep-alive", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + "HTTP/1.1 200 OK", + "Connection: keep-alive", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ZeroContentLengthSetAutomaticallyForNonKeepAliveRequests(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(async httpContext => + { + Assert.Equal(0, await httpContext.Request.Body.ReadAsync(new byte[1], 0, 1).TimeoutAfter(TimeSpan.FromSeconds(10))); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "Connection: close", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(TestApp.EmptyApp, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "HEAD / HTTP/1.1", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(async httpContext => + { + var request = httpContext.Request; + var response = httpContext.Response; + + using (var reader = new StreamReader(request.Body, Encoding.ASCII)) + { + var statusString = await reader.ReadLineAsync(); + response.StatusCode = int.Parse(statusString); + } + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.1", + "Content-Length: 3", + "", + "204POST / HTTP/1.1", + "Content-Length: 3", + "", + "205POST / HTTP/1.1", + "Content-Length: 3", + "", + "304POST / HTTP/1.1", + "Content-Length: 3", + "", + "200"); + await connection.ReceiveEnd( + "HTTP/1.1 204 No Content", + $"Date: {testContext.DateHeaderValue}", + "", + "HTTP/1.1 205 Reset Content", + $"Date: {testContext.DateHeaderValue}", + "", + "HTTP/1.1 304 Not Modified", + $"Date: {testContext.DateHeaderValue}", + "", + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ConnectionClosedAfter101Response(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(async httpContext => + { + var request = httpContext.Request; + var stream = await httpContext.Features.Get().UpgradeAsync(); + var response = Encoding.ASCII.GetBytes("hello, world"); + await stream.WriteAsync(response, 0, response.Length); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 101 Switching Protocols", + "Connection: Upgrade", + $"Date: {testContext.DateHeaderValue}", + "", + "hello, world"); + } + + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "Connection: keep-alive", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 101 Switching Protocols", + "Connection: Upgrade", + $"Date: {testContext.DateHeaderValue}", + "", + "hello, world"); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ThrowingResultsIn500Response(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + bool onStartingCalled = false; + + var testLogger = new TestApplicationErrorLogger(); + testContext.Log = new KestrelTrace(testLogger); + + using (var server = new TestServer(httpContext => + { + var response = httpContext.Response; + response.OnStarting(_ => + { + onStartingCalled = true; + return TaskCache.CompletedTask; + }, null); + + // Anything added to the ResponseHeaders dictionary is ignored + response.Headers["Content-Length"] = "11"; + throw new Exception(); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + "GET / HTTP/1.1", + "Connection: close", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 500 Internal Server Error", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + "HTTP/1.1 500 Internal Server Error", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + + Assert.False(onStartingCalled); + Assert.Equal(2, testLogger.ApplicationErrorsLogged); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ListenOptions listenOptions) + { + var callback1Called = false; + var callback2CallCount = 0; + + var testContext = new TestServiceContext(); + var testLogger = new TestApplicationErrorLogger(); + testContext.Log = new KestrelTrace(testLogger); + + using (var server = new TestServer(async httpContext => + { + var onStartingException = new Exception(); + + var response = httpContext.Response; + response.OnStarting(_ => + { + callback1Called = true; + throw onStartingException; + }, null); + response.OnStarting(_ => + { + callback2CallCount++; + throw onStartingException; + }, null); + + var writeException = await Assert.ThrowsAsync(async () => await response.Body.FlushAsync()); + Assert.Same(onStartingException, writeException.InnerException); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 500 Internal Server Error", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + "HTTP/1.1 500 Internal Server Error", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + + // The first registered OnStarting callback should have been called, + // since they are called LIFO order and the other one failed. + Assert.False(callback1Called); + Assert.Equal(2, callback2CallCount); + Assert.Equal(2, testLogger.ApplicationErrorsLogged); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + var onCompletedCalled1 = false; + var onCompletedCalled2 = false; + + var testLogger = new TestApplicationErrorLogger(); + testContext.Log = new KestrelTrace(testLogger); + + using (var server = new TestServer(async httpContext => + { + var response = httpContext.Response; + response.OnCompleted(_ => + { + onCompletedCalled1 = true; + throw new Exception(); + }, null); + response.OnCompleted(_ => + { + onCompletedCalled2 = true; + throw new Exception(); + }, null); + + response.Headers["Content-Length"] = new[] { "11" }; + + await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello World"); + } + } + + // All OnCompleted callbacks should be called even if they throw. + Assert.Equal(2, testLogger.ApplicationErrorsLogged); + Assert.True(onCompletedCalled1); + Assert.True(onCompletedCalled2); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ThrowingAfterWritingKillsConnection(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + bool onStartingCalled = false; + + var testLogger = new TestApplicationErrorLogger(); + testContext.Log = new KestrelTrace(testLogger); + + using (var server = new TestServer(async httpContext => + { + var response = httpContext.Response; + response.OnStarting(_ => + { + onStartingCalled = true; + return Task.FromResult(null); + }, null); + + response.Headers["Content-Length"] = new[] { "11" }; + await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); + throw new Exception(); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello World"); + } + } + + Assert.True(onStartingCalled); + Assert.Equal(1, testLogger.ApplicationErrorsLogged); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ThrowingAfterPartialWriteKillsConnection(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + bool onStartingCalled = false; + + var testLogger = new TestApplicationErrorLogger(); + testContext.Log = new KestrelTrace(testLogger); + + using (var server = new TestServer(async httpContext => + { + var response = httpContext.Response; + response.OnStarting(_ => + { + onStartingCalled = true; + return Task.FromResult(null); + }, null); + + response.Headers["Content-Length"] = new[] { "11" }; + await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); + throw new Exception(); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello"); + } + } + + Assert.True(onStartingCalled); + Assert.Equal(1, testLogger.ApplicationErrorsLogged); + } + + [MemberData(nameof(ConnectionAdapterData))] + public async Task FailedWritesResultInAbortedRequest(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + // This should match _maxBytesPreCompleted in SocketOutput + var maxBytesPreCompleted = 65536; + // Ensure string is long enough to disable write-behind buffering + var largeString = new string('a', maxBytesPreCompleted + 1); + + var writeTcs = new TaskCompletionSource(); + var registrationWh = new ManualResetEventSlim(); + var connectionCloseWh = new ManualResetEventSlim(); + + using (var server = new TestServer(async httpContext => + { + var response = httpContext.Response; + var request = httpContext.Request; + var lifetime = httpContext.Features.Get(); + + lifetime.RequestAborted.Register(() => registrationWh.Set()); + + await request.Body.CopyToAsync(Stream.Null); + connectionCloseWh.Wait(); + + try + { + // Ensure write is long enough to disable write-behind buffering + for (int i = 0; i < 100; i++) + { + await response.WriteAsync(largeString, lifetime.RequestAborted); + registrationWh.Wait(1000); + } + } + catch (Exception ex) + { + writeTcs.SetException(ex); + throw; + } + + writeTcs.SetException(new Exception("This shouldn't be reached.")); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.1", + "Content-Length: 5", + "", + "Hello"); + // Don't wait to receive the response. Just close the socket. + } + + connectionCloseWh.Set(); + + // Write failed + await Assert.ThrowsAsync(async () => await writeTcs.Task); + // RequestAborted tripped + Assert.True(registrationWh.Wait(1000)); + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task NoErrorsLoggedWhenServerEndsConnectionBeforeClient(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + var testLogger = new TestApplicationErrorLogger(); + testContext.Log = new KestrelTrace(testLogger); + + using (var server = new TestServer(async httpContext => + { + var response = httpContext.Response; + response.Headers["Content-Length"] = new[] { "11" }; + await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.0", + "", + ""); + await connection.ReceiveForcedEnd( + "HTTP/1.1 200 OK", + "Connection: close", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 11", + "", + "Hello World"); + } + } + + Assert.Equal(0, testLogger.TotalErrorsLogged); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task NoResponseSentWhenConnectionIsClosedByServerBeforeClientFinishesSendingRequest(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + using (var server = new TestServer(httpContext => + { + httpContext.Abort(); + return TaskCache.CompletedTask; + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "POST / HTTP/1.0", + "Content-Length: 1", + "", + ""); + await connection.ReceiveForcedEnd(); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ResponseHeadersAreResetOnEachRequest(ListenOptions listenOptions) + { + var testContext = new TestServiceContext(); + + IHeaderDictionary originalResponseHeaders = null; + var firstRequest = true; + + using (var server = new TestServer(httpContext => + { + var responseFeature = httpContext.Features.Get(); + + if (firstRequest) + { + originalResponseHeaders = responseFeature.Headers; + responseFeature.Headers = new FrameResponseHeaders(); + firstRequest = false; + } + else + { + Assert.Same(originalResponseHeaders, responseFeature.Headers); + } + + return TaskCache.CompletedTask; + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + } + } + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task OnStartingCallbacksAreCalledInLastInFirstOutOrder(ListenOptions listenOptions) + { + const string response = "hello, world"; + + var testContext = new TestServiceContext(); + + var callOrder = new Stack(); + var onStartingTcs = new TaskCompletionSource(); + + using (var server = new TestServer(async context => + { + context.Response.OnStarting(_ => + { + callOrder.Push(1); + onStartingTcs.SetResult(null); + return TaskCache.CompletedTask; + }, null); + context.Response.OnStarting(_ => + { + callOrder.Push(2); + return TaskCache.CompletedTask; + }, null); + + context.Response.ContentLength = response.Length; + await context.Response.WriteAsync(response); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + $"Content-Length: {response.Length}", + "", + "hello, world"); + + // Wait for all callbacks to be called. + await onStartingTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); + } + } + + Assert.Equal(1, callOrder.Pop()); + Assert.Equal(2, callOrder.Pop()); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task OnCompletedCallbacksAreCalledInLastInFirstOutOrder(ListenOptions listenOptions) + { + const string response = "hello, world"; + + var testContext = new TestServiceContext(); + + var callOrder = new Stack(); + var onCompletedTcs = new TaskCompletionSource(); + + using (var server = new TestServer(async context => + { + context.Response.OnCompleted(_ => + { + callOrder.Push(1); + onCompletedTcs.SetResult(null); + return TaskCache.CompletedTask; + }, null); + context.Response.OnCompleted(_ => + { + callOrder.Push(2); + return TaskCache.CompletedTask; + }, null); + + context.Response.ContentLength = response.Length; + await context.Response.WriteAsync(response); + }, testContext, listenOptions)) + { + using (var connection = server.CreateConnection()) + { + await connection.Send( + "GET / HTTP/1.1", + "", + ""); + await connection.ReceiveEnd( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + $"Content-Length: {response.Length}", + "", + "hello, world"); + + // Wait for all callbacks to be called. + await onCompletedTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); + } + } + + Assert.Equal(1, callOrder.Pop()); + Assert.Equal(2, callOrder.Pop()); + } + public static TheoryData NullHeaderData { get diff --git a/test/shared/TestServer.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/TestServer.cs similarity index 73% rename from test/shared/TestServer.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/TestServer.cs index f9224babdb..42bfa542ab 100644 --- a/test/shared/TestServer.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/TestServer.cs @@ -8,8 +8,10 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; +using Microsoft.AspNetCore.Testing; -namespace Microsoft.AspNetCore.Testing +namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests { /// /// Summary description for TestServer @@ -44,11 +46,21 @@ namespace Microsoft.AspNetCore.Testing _listenOptions = listenOptions; Context = context; - Context.TransportContext.ConnectionHandler = new ConnectionHandler(listenOptions, Context, new DummyApplication(app, httpContextFactory)); + TransportContext = new LibuvTransportContext() + { + AppLifetime = new LifetimeNotImplemented(), + ConnectionHandler = new ConnectionHandler(listenOptions, Context, new DummyApplication(app, httpContextFactory)), + Log = new LibuvTrace(new TestApplicationErrorLogger()), + Options = new LibuvTransportOptions() + { + ThreadCount = 1, + ShutdownTimeout = TimeSpan.FromSeconds(5) + } + }; try { - _transport = new LibuvTransport(context.TransportContext, _listenOptions); + _transport = new LibuvTransport(TransportContext, _listenOptions); _transport.BindAsync().Wait(); } catch @@ -64,6 +76,7 @@ namespace Microsoft.AspNetCore.Testing public AddressFamily AddressFamily => _listenOptions.IPEndPoint.AddressFamily; public TestServiceContext Context { get; } + public LibuvTransportContext TransportContext { get; } public TestConnection CreateConnection() { diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj index ec86613394..f75a837b72 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj @@ -20,7 +20,6 @@ - diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Tests/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj b/test/Microsoft.AspNetCore.Server.Kestrel.Tests/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj new file mode 100644 index 0000000000..a453a89214 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Tests/Microsoft.AspNetCore.Server.Kestrel.Tests.csproj @@ -0,0 +1,31 @@ + + + + + + netcoreapp2.0;net46 + netcoreapp2.0 + x64 + true + true + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/WebHostBuilderKestrelExtensionsTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Tests/WebHostBuilderKestrelExtensionsTests.cs similarity index 96% rename from test/Microsoft.AspNetCore.Server.KestrelTests/WebHostBuilderKestrelExtensionsTests.cs rename to test/Microsoft.AspNetCore.Server.Kestrel.Tests/WebHostBuilderKestrelExtensionsTests.cs index 6e88011a92..fc5d6509ae 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/WebHostBuilderKestrelExtensionsTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Tests/WebHostBuilderKestrelExtensionsTests.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Tests { public class WebHostBuilderKestrelExtensionsTests { diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Tests/xunit.runner.json b/test/Microsoft.AspNetCore.Server.Kestrel.Tests/xunit.runner.json new file mode 100644 index 0000000000..c76780941f --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Tests/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "http://json.schemastore.org/xunit.runner.schema", + "methodDisplay": "method", + "longRunningTestSeconds": 60 +} diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs deleted file mode 100644 index 81a14ee9a1..0000000000 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/EngineTests.cs +++ /dev/null @@ -1,1431 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; -using Microsoft.AspNetCore.Testing; -using Microsoft.Extensions.Internal; -using Xunit; - -namespace Microsoft.AspNetCore.Server.KestrelTests -{ - /// - /// Summary description for EngineTests - /// - public class EngineTests - { - public static TheoryData ConnectionAdapterData => new TheoryData - { - new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)), - new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)) - { - ConnectionAdapters = { new PassThroughConnectionAdapter() } - } - }; - - [Fact] - public async Task TransportCanStartAndStop() - { - var serviceContext = new TestServiceContext(); - - // The transport can no longer start threads without binding to an endpoint. - var transport = new LibuvTransport(serviceContext.TransportContext, - new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))); - - await transport.BindAsync(); - await transport.StopAsync(); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ListenerCanCreateAndDispose(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - testContext.TransportContext.ConnectionHandler = new ConnectionHandler(listenOptions, testContext, new DummyApplication(TestApp.EchoApp)); - var transport = new LibuvTransport(testContext.TransportContext, listenOptions); - - await transport.BindAsync(); - await transport.UnbindAsync(); - await transport.StopAsync(); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - testContext.TransportContext.ConnectionHandler = new ConnectionHandler(listenOptions, testContext, new DummyApplication(TestApp.EchoApp)); - var transport = new LibuvTransport(testContext.TransportContext, listenOptions); - - await transport.BindAsync(); - - var socket = TestConnection.CreateConnectedLoopbackSocket(listenOptions.IPEndPoint.Port); - var data = "Hello World"; - socket.Send(Encoding.ASCII.GetBytes($"POST / HTTP/1.0\r\nContent-Length: 11\r\n\r\n{data}")); - var buffer = new byte[data.Length]; - var read = 0; - while (read < data.Length) - { - read += socket.Receive(buffer, read, buffer.Length - read, SocketFlags.None); - } - socket.Dispose(); - - await transport.UnbindAsync(); - await transport.StopAsync(); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Http10RequestReceivesHttp11Response(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoApp, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.0", - "Content-Length: 11", - "", - "Hello World"); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "", - "Hello World"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Http11(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "GET / HTTP/1.1", - "Connection: close", - "Content-Length: 7", - "", - "Goodbye"); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 7", - "", - "Goodbye"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task HeadersAndStreamsAreReused(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - var streamCount = 0; - var requestHeadersCount = 0; - var responseHeadersCount = 0; - var loopCount = 20; - Stream lastStream = null; - IHeaderDictionary lastRequestHeaders = null; - IHeaderDictionary lastResponseHeaders = null; - - using (var server = new TestServer( - async context => - { - if (context.Request.Body != lastStream) - { - lastStream = context.Request.Body; - streamCount++; - } - if (context.Request.Headers != lastRequestHeaders) - { - lastRequestHeaders = context.Request.Headers; - requestHeadersCount++; - } - if (context.Response.Headers != lastResponseHeaders) - { - lastResponseHeaders = context.Response.Headers; - responseHeadersCount++; - } - - var ms = new MemoryStream(); - await context.Request.Body.CopyToAsync(ms); - var request = ms.ToArray(); - - context.Response.ContentLength = request.Length; - - await context.Response.Body.WriteAsync(request, 0, request.Length); - }, - testContext)) - { - - using (var connection = server.CreateConnection()) - { - var requestData = - Enumerable.Repeat("GET / HTTP/1.1\r\n", loopCount) - .Concat(new[] { "GET / HTTP/1.1\r\nContent-Length: 7\r\nConnection: close\r\n\r\nGoodbye" }); - - var response = string.Join("\r\n", new string[] { - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - ""}); - - var lastResponse = string.Join("\r\n", new string[] - { - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 7", - "", - "Goodbye" - }); - - var responseData = - Enumerable.Repeat(response, loopCount) - .Concat(new[] { lastResponse }); - - await connection.Send(requestData.ToArray()); - - await connection.ReceiveEnd(responseData.ToArray()); - } - - Assert.Equal(1, streamCount); - Assert.Equal(1, requestHeadersCount); - Assert.Equal(1, responseHeadersCount); - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Http10ContentLength(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoApp, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.0", - "Content-Length: 11", - "", - "Hello World"); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "", - "Hello World"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Http10KeepAlive(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "Connection: keep-alive", - "", - "POST / HTTP/1.0", - "Content-Length: 7", - "", - "Goodbye"); - await connection.Receive( - "HTTP/1.1 200 OK", - "Connection: keep-alive", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "\r\n"); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 7", - "", - "Goodbye"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Http10KeepAliveNotUsedIfResponseContentLengthNotSet(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoApp, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "Connection: keep-alive", - "", - "POST / HTTP/1.0", - "Connection: keep-alive", - "Content-Length: 7", - "", - "Goodbye"); - await connection.Receive( - "HTTP/1.1 200 OK", - "Connection: keep-alive", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "\r\n"); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "", - "Goodbye"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Http10KeepAliveContentLength(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.0", - "Content-Length: 11", - "Connection: keep-alive", - "", - "Hello WorldPOST / HTTP/1.0", - "Content-Length: 7", - "", - "Goodbye"); - await connection.Receive( - "HTTP/1.1 200 OK", - "Connection: keep-alive", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 11", - "", - "Hello World"); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 7", - "", - "Goodbye"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task Expect100ContinueForBody(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.1", - "Expect: 100-continue", - "Connection: close", - "Content-Length: 11", - "\r\n"); - await connection.Receive( - "HTTP/1.1 100 Continue", - "", - ""); - await connection.Send("Hello World"); - await connection.Receive( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 11", - "", - "Hello World"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task DisconnectingClient(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoApp, testContext, listenOptions)) - { - var socket = TestConnection.CreateConnectedLoopbackSocket(server.Port); - await Task.Delay(200); - socket.Dispose(); - - await Task.Delay(200); - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ZeroContentLengthSetAutomaticallyAfterNoWrites(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EmptyApp, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "GET / HTTP/1.0", - "Connection: keep-alive", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 200 OK", - "Connection: keep-alive", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ZeroContentLengthSetAutomaticallyForNonKeepAliveRequests(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(async httpContext => - { - Assert.Equal(0, await httpContext.Request.Body.ReadAsync(new byte[1], 0, 1).TimeoutAfter(TimeSpan.FromSeconds(10))); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "Connection: close", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ZeroContentLengthNotSetAutomaticallyForHeadRequests(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EmptyApp, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "HEAD / HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ZeroContentLengthNotSetAutomaticallyForCertainStatusCodes(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(async httpContext => - { - var request = httpContext.Request; - var response = httpContext.Response; - - using (var reader = new StreamReader(request.Body, Encoding.ASCII)) - { - var statusString = await reader.ReadLineAsync(); - response.StatusCode = int.Parse(statusString); - } - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.1", - "Content-Length: 3", - "", - "204POST / HTTP/1.1", - "Content-Length: 3", - "", - "205POST / HTTP/1.1", - "Content-Length: 3", - "", - "304POST / HTTP/1.1", - "Content-Length: 3", - "", - "200"); - await connection.ReceiveEnd( - "HTTP/1.1 204 No Content", - $"Date: {testContext.DateHeaderValue}", - "", - "HTTP/1.1 205 Reset Content", - $"Date: {testContext.DateHeaderValue}", - "", - "HTTP/1.1 304 Not Modified", - $"Date: {testContext.DateHeaderValue}", - "", - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ZeroContentLengthAssumedOnNonKeepAliveRequestsWithoutContentLengthOrTransferEncodingHeader(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(async httpContext => - { - // This will hang if 0 content length is not assumed by the server - Assert.Equal(0, await httpContext.Request.Body.ReadAsync(new byte[1], 0, 1).TimeoutAfter(TimeSpan.FromSeconds(10))); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - // Use Send instead of SendEnd to ensure the connection will remain open while - // the app runs and reads 0 bytes from the body nonetheless. This checks that - // https://github.com/aspnet/KestrelHttpServer/issues/1104 is not regressing. - await connection.Send( - "GET / HTTP/1.1", - "Connection: close", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ConnectionClosedAfter101Response(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(async httpContext => - { - var request = httpContext.Request; - var stream = await httpContext.Features.Get().UpgradeAsync(); - var response = Encoding.ASCII.GetBytes("hello, world"); - await stream.WriteAsync(response, 0, response.Length); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 101 Switching Protocols", - "Connection: Upgrade", - $"Date: {testContext.DateHeaderValue}", - "", - "hello, world"); - } - - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "Connection: keep-alive", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 101 Switching Protocols", - "Connection: Upgrade", - $"Date: {testContext.DateHeaderValue}", - "", - "hello, world"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ThrowingResultsIn500Response(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - bool onStartingCalled = false; - - var testLogger = new TestApplicationErrorLogger(); - testContext.Log = new KestrelTrace(testLogger); - - using (var server = new TestServer(httpContext => - { - var response = httpContext.Response; - response.OnStarting(_ => - { - onStartingCalled = true; - return TaskCache.CompletedTask; - }, null); - - // Anything added to the ResponseHeaders dictionary is ignored - response.Headers["Content-Length"] = "11"; - throw new Exception(); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "GET / HTTP/1.1", - "Connection: close", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 500 Internal Server Error", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 500 Internal Server Error", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - - Assert.False(onStartingCalled); - Assert.Equal(2, testLogger.ApplicationErrorsLogged); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ThrowingAfterWritingKillsConnection(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - bool onStartingCalled = false; - - var testLogger = new TestApplicationErrorLogger(); - testContext.Log = new KestrelTrace(testLogger); - - using (var server = new TestServer(async httpContext => - { - var response = httpContext.Response; - response.OnStarting(_ => - { - onStartingCalled = true; - return Task.FromResult(null); - }, null); - - response.Headers["Content-Length"] = new[] { "11" }; - await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); - throw new Exception(); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 11", - "", - "Hello World"); - } - } - - Assert.True(onStartingCalled); - Assert.Equal(1, testLogger.ApplicationErrorsLogged); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ThrowingAfterPartialWriteKillsConnection(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - bool onStartingCalled = false; - - var testLogger = new TestApplicationErrorLogger(); - testContext.Log = new KestrelTrace(testLogger); - - using (var server = new TestServer(async httpContext => - { - var response = httpContext.Response; - response.OnStarting(_ => - { - onStartingCalled = true; - return Task.FromResult(null); - }, null); - - response.Headers["Content-Length"] = new[] { "11" }; - await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello"), 0, 5); - throw new Exception(); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 11", - "", - "Hello"); - } - } - - Assert.True(onStartingCalled); - Assert.Equal(1, testLogger.ApplicationErrorsLogged); - } - - [MemberData(nameof(ConnectionAdapterData))] - public async Task ConnectionClosesWhenFinReceivedBeforeRequestCompletes(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(TestApp.EchoAppChunked, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "POST / HTTP/1.1"); - connection.Shutdown(SocketShutdown.Send); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 400 Bad Request", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "POST / HTTP/1.1", - "Content-Length: 7"); - connection.Shutdown(SocketShutdown.Send); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 400 Bad Request", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ThrowingInOnStartingResultsInFailedWritesAnd500Response(ListenOptions listenOptions) - { - var callback1Called = false; - var callback2CallCount = 0; - - var testContext = new TestServiceContext(); - var testLogger = new TestApplicationErrorLogger(); - testContext.Log = new KestrelTrace(testLogger); - - using (var server = new TestServer(async httpContext => - { - var onStartingException = new Exception(); - - var response = httpContext.Response; - response.OnStarting(_ => - { - callback1Called = true; - throw onStartingException; - }, null); - response.OnStarting(_ => - { - callback2CallCount++; - throw onStartingException; - }, null); - - var writeException = await Assert.ThrowsAsync(async () => await response.Body.FlushAsync()); - Assert.Same(onStartingException, writeException.InnerException); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 500 Internal Server Error", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 500 Internal Server Error", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - - // The first registered OnStarting callback should have been called, - // since they are called LIFO order and the other one failed. - Assert.False(callback1Called); - Assert.Equal(2, callback2CallCount); - Assert.Equal(2, testLogger.ApplicationErrorsLogged); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ThrowingInOnCompletedIsLoggedAndClosesConnection(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - var onCompletedCalled1 = false; - var onCompletedCalled2 = false; - - var testLogger = new TestApplicationErrorLogger(); - testContext.Log = new KestrelTrace(testLogger); - - using (var server = new TestServer(async httpContext => - { - var response = httpContext.Response; - response.OnCompleted(_ => - { - onCompletedCalled1 = true; - throw new Exception(); - }, null); - response.OnCompleted(_ => - { - onCompletedCalled2 = true; - throw new Exception(); - }, null); - - response.Headers["Content-Length"] = new[] { "11" }; - - await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 11", - "", - "Hello World"); - } - } - - // All OnCompleted callbacks should be called even if they throw. - Assert.Equal(2, testLogger.ApplicationErrorsLogged); - Assert.True(onCompletedCalled1); - Assert.True(onCompletedCalled2); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task RequestsCanBeAbortedMidRead(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - var readTcs = new TaskCompletionSource(); - var registrationTcs = new TaskCompletionSource(); - var requestId = 0; - - using (var server = new TestServer(async httpContext => - { - requestId++; - - var response = httpContext.Response; - var request = httpContext.Request; - var lifetime = httpContext.Features.Get(); - - lifetime.RequestAborted.Register(() => registrationTcs.TrySetResult(requestId)); - - if (requestId == 1) - { - response.Headers["Content-Length"] = new[] { "5" }; - - await response.WriteAsync("World"); - } - else - { - var readTask = request.Body.CopyToAsync(Stream.Null); - - lifetime.Abort(); - - try - { - await readTask; - } - catch (Exception ex) - { - readTcs.SetException(ex); - throw; - } - - readTcs.SetException(new Exception("This shouldn't be reached.")); - } - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - // Never send the body so CopyToAsync always fails. - await connection.Send( - "POST / HTTP/1.1", - "Content-Length: 5", - "", - "HelloPOST / HTTP/1.1", - "Content-Length: 5", - "", - ""); - - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 5", - "", - "World"); - } - } - - await Assert.ThrowsAsync(async () => await readTcs.Task); - - // The cancellation token for only the last request should be triggered. - var abortedRequestId = await registrationTcs.Task; - Assert.Equal(2, abortedRequestId); - } - - [MemberData(nameof(ConnectionAdapterData))] - public async Task FailedWritesResultInAbortedRequest(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - // This should match _maxBytesPreCompleted in SocketOutput - var maxBytesPreCompleted = 65536; - // Ensure string is long enough to disable write-behind buffering - var largeString = new string('a', maxBytesPreCompleted + 1); - - var writeTcs = new TaskCompletionSource(); - var registrationWh = new ManualResetEventSlim(); - var connectionCloseWh = new ManualResetEventSlim(); - - using (var server = new TestServer(async httpContext => - { - var response = httpContext.Response; - var request = httpContext.Request; - var lifetime = httpContext.Features.Get(); - - lifetime.RequestAborted.Register(() => registrationWh.Set()); - - await request.Body.CopyToAsync(Stream.Null); - connectionCloseWh.Wait(); - - try - { - // Ensure write is long enough to disable write-behind buffering - for (int i = 0; i < 100; i++) - { - await response.WriteAsync(largeString, lifetime.RequestAborted); - registrationWh.Wait(1000); - } - } - catch (Exception ex) - { - writeTcs.SetException(ex); - throw; - } - - writeTcs.SetException(new Exception("This shouldn't be reached.")); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.1", - "Content-Length: 5", - "", - "Hello"); - // Don't wait to receive the response. Just close the socket. - } - - connectionCloseWh.Set(); - - // Write failed - await Assert.ThrowsAsync(async () => await writeTcs.Task); - // RequestAborted tripped - Assert.True(registrationWh.Wait(1000)); - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task NoErrorsLoggedWhenServerEndsConnectionBeforeClient(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - var testLogger = new TestApplicationErrorLogger(); - testContext.Log = new KestrelTrace(testLogger); - - using (var server = new TestServer(async httpContext => - { - var response = httpContext.Response; - response.Headers["Content-Length"] = new[] { "11" }; - await response.Body.WriteAsync(Encoding.ASCII.GetBytes("Hello World"), 0, 11); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.0", - "", - ""); - await connection.ReceiveForcedEnd( - "HTTP/1.1 200 OK", - "Connection: close", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 11", - "", - "Hello World"); - } - } - - Assert.Equal(0, testLogger.TotalErrorsLogged); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task NoResponseSentWhenConnectionIsClosedByServerBeforeClientFinishesSendingRequest(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - using (var server = new TestServer(httpContext => - { - httpContext.Abort(); - return TaskCache.CompletedTask; - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "POST / HTTP/1.0", - "Content-Length: 1", - "", - ""); - await connection.ReceiveForcedEnd(); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task RequestHeadersAreResetOnEachRequest(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - IHeaderDictionary originalRequestHeaders = null; - var firstRequest = true; - - using (var server = new TestServer(httpContext => - { - var requestFeature = httpContext.Features.Get(); - - if (firstRequest) - { - originalRequestHeaders = requestFeature.Headers; - requestFeature.Headers = new FrameRequestHeaders(); - firstRequest = false; - } - else - { - Assert.Same(originalRequestHeaders, requestFeature.Headers); - } - - return TaskCache.CompletedTask; - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task ResponseHeadersAreResetOnEachRequest(ListenOptions listenOptions) - { - var testContext = new TestServiceContext(); - - IHeaderDictionary originalResponseHeaders = null; - var firstRequest = true; - - using (var server = new TestServer(httpContext => - { - var responseFeature = httpContext.Features.Get(); - - if (firstRequest) - { - originalResponseHeaders = responseFeature.Headers; - responseFeature.Headers = new FrameResponseHeaders(); - firstRequest = false; - } - else - { - Assert.Same(originalResponseHeaders, responseFeature.Headers); - } - - return TaskCache.CompletedTask; - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - "Content-Length: 0", - "", - ""); - } - } - } - - [Theory] - [InlineData("/%%2000", "/% 00")] - [InlineData("/%25%30%30", "/%00")] - public async Task PathEscapeTests(string inputPath, string expectedPath) - { - using (var server = new TestServer(async httpContext => - { - var path = httpContext.Request.Path.Value; - httpContext.Response.Headers["Content-Length"] = new[] { path.Length.ToString() }; - await httpContext.Response.WriteAsync(path); - })) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - $"GET {inputPath} HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {server.Context.DateHeaderValue}", - $"Content-Length: {expectedPath.Length.ToString()}", - "", - $"{expectedPath}"); - } - } - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task OnStartingCallbacksAreCalledInLastInFirstOutOrder(ListenOptions listenOptions) - { - const string response = "hello, world"; - - var testContext = new TestServiceContext(); - - var callOrder = new Stack(); - var onStartingTcs = new TaskCompletionSource(); - - using (var server = new TestServer(async context => - { - context.Response.OnStarting(_ => - { - callOrder.Push(1); - onStartingTcs.SetResult(null); - return TaskCache.CompletedTask; - }, null); - context.Response.OnStarting(_ => - { - callOrder.Push(2); - return TaskCache.CompletedTask; - }, null); - - context.Response.ContentLength = response.Length; - await context.Response.WriteAsync(response); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - $"Content-Length: {response.Length}", - "", - "hello, world"); - - // Wait for all callbacks to be called. - await onStartingTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); - } - } - - Assert.Equal(1, callOrder.Pop()); - Assert.Equal(2, callOrder.Pop()); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task OnCompletedCallbacksAreCalledInLastInFirstOutOrder(ListenOptions listenOptions) - { - const string response = "hello, world"; - - var testContext = new TestServiceContext(); - - var callOrder = new Stack(); - var onCompletedTcs = new TaskCompletionSource(); - - using (var server = new TestServer(async context => - { - context.Response.OnCompleted(_ => - { - callOrder.Push(1); - onCompletedTcs.SetResult(null); - return TaskCache.CompletedTask; - }, null); - context.Response.OnCompleted(_ => - { - callOrder.Push(2); - return TaskCache.CompletedTask; - }, null); - - context.Response.ContentLength = response.Length; - await context.Response.WriteAsync(response); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "", - ""); - await connection.ReceiveEnd( - "HTTP/1.1 200 OK", - $"Date: {testContext.DateHeaderValue}", - $"Content-Length: {response.Length}", - "", - "hello, world"); - - // Wait for all callbacks to be called. - await onCompletedTcs.Task.TimeoutAfter(TimeSpan.FromSeconds(10)); - } - } - - Assert.Equal(1, callOrder.Pop()); - Assert.Equal(2, callOrder.Pop()); - } - - [Theory] - [MemberData(nameof(ConnectionAdapterData))] - public async Task UpgradeRequestIsNotKeptAliveOrChunked(ListenOptions listenOptions) - { - const string message = "Hello World"; - - var testContext = new TestServiceContext(); - - using (var server = new TestServer(async context => - { - var upgradeFeature = context.Features.Get(); - var duplexStream = await upgradeFeature.UpgradeAsync(); - - var buffer = new byte[message.Length]; - var read = 0; - while (read < message.Length) - { - read += await duplexStream.ReadAsync(buffer, read, buffer.Length - read).TimeoutAfter(TimeSpan.FromSeconds(10)); - } - - await duplexStream.WriteAsync(buffer, 0, read); - }, testContext, listenOptions)) - { - using (var connection = server.CreateConnection()) - { - await connection.Send( - "GET / HTTP/1.1", - "Connection: Upgrade", - "", - message); - await connection.ReceiveForcedEnd( - "HTTP/1.1 101 Switching Protocols", - "Connection: Upgrade", - $"Date: {testContext.DateHeaderValue}", - "", - message); - } - } - } - } -} diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/ConnectionTests.cs similarity index 76% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/ConnectionTests.cs index 0234cd08c2..d9110cba1e 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ConnectionTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/ConnectionTests.cs @@ -4,14 +4,12 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers; -using Microsoft.AspNetCore.Testing; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class ConnectionTests { @@ -21,10 +19,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests using (var mockConnectionHandler = new MockConnectionHandler()) { var mockLibuv = new MockLibuv(); - var serviceContext = new TestServiceContext(); - serviceContext.TransportContext.ConnectionHandler = mockConnectionHandler; - - var transport = new LibuvTransport(mockLibuv, serviceContext.TransportContext, null); + var transportContext = new TestLibuvTransportContext() { ConnectionHandler = mockConnectionHandler }; + var transport = new LibuvTransport(mockLibuv, transportContext, null); var thread = new LibuvThread(transport); try @@ -32,11 +28,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests await thread.StartAsync(); await thread.PostAsync(_ => { - var listenerContext = new ListenerContext(serviceContext.TransportContext) + var listenerContext = new ListenerContext(transportContext) { Thread = thread }; - var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, serviceContext.TransportContext.Log); + var socket = new MockSocket(mockLibuv, Thread.CurrentThread.ManagedThreadId, transportContext.Log); var connection = new LibuvConnection(listenerContext, socket); connection.Start(); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/LibuvOutputConsumerTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs similarity index 90% rename from test/Microsoft.AspNetCore.Server.KestrelTests/LibuvOutputConsumerTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs index 301897f6a6..12bcbdfd95 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/LibuvOutputConsumerTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs @@ -7,14 +7,13 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; -using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class LibuvOutputConsumerTests : IDisposable { @@ -37,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests _pipeFactory = new PipeFactory(); _mockLibuv = new MockLibuv(); - var libuvTransport = new LibuvTransport(_mockLibuv, new TestServiceContext().TransportContext, new ListenOptions(0)); + var libuvTransport = new LibuvTransport(_mockLibuv, new TestLibuvTransportContext(), new ListenOptions(0)); _libuvThread = new LibuvThread(libuvTransport, maxLoops: 1); _libuvThread.StartAsync().Wait(); } @@ -65,7 +64,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = maxResponseBufferSize ?? 0, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { // At least one run of this test should have a MaxResponseBufferSize < 1 MB. var bufferSize = 1024 * 1024; @@ -100,7 +99,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = 0, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { // Don't want to allocate anything too huge for perf. This is at least larger than the default buffer. var bufferSize = 1024 * 1024; @@ -146,7 +145,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = 1, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { var bufferSize = 1; var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); @@ -200,7 +199,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = maxResponseBufferSize, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { var bufferSize = maxResponseBufferSize - 1; var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); @@ -260,7 +259,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = maxResponseBufferSize, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { var bufferSize = maxResponseBufferSize / 2; var data = new byte[bufferSize]; @@ -328,7 +327,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = maxResponseBufferSize, }; - using (var socketOutput = CreateSocketOutput(pipeOptions, mockConnection)) + using (var socketOutput = CreateOutputProducer(pipeOptions, mockConnection)) { var bufferSize = maxResponseBufferSize - 1; @@ -400,7 +399,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = maxResponseBufferSize, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { var bufferSize = maxResponseBufferSize - 1; var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); @@ -458,7 +457,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests MaximumSizeLow = maxResponseBufferSize ?? 0, }; - using (var socketOutput = CreateSocketOutput(pipeOptions)) + using (var socketOutput = CreateOutputProducer(pipeOptions)) { _mockLibuv.KestrelThreadBlocker.Reset(); @@ -487,46 +486,21 @@ namespace Microsoft.AspNetCore.Server.KestrelTests } } - [Fact] - public async Task AllocCommitCanBeCalledAfterConnectionClose() - { - var pipeOptions = new PipeOptions - { - ReaderScheduler = _libuvThread, - }; + - using (var connection = new MockConnection()) - { - var socketOutput = CreateSocketOutput(pipeOptions, connection); - // Close SocketOutput - socketOutput.Dispose(); - - await _mockLibuv.OnPostTask; - - Assert.Equal(TaskStatus.RanToCompletion, connection.SocketClosed.Status); - - var called = false; - - ((ISocketOutput)socketOutput).Write((buffer, state) => - { - called = true; - }, - null); - - Assert.False(called); - } - } - - private OutputProducer CreateSocketOutput(PipeOptions pipeOptions, MockConnection connection = null) + private OutputProducer CreateOutputProducer(PipeOptions pipeOptions, MockConnection connection = null) { var pipe = _pipeFactory.Create(pipeOptions); - var serviceContext = new TestServiceContext(); + + var logger = new TestApplicationErrorLogger(); + var serviceContext = new TestServiceContext() { Log = new TestKestrelTrace(logger) }; + var transportContext = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) }; var frame = new Frame(null, new FrameContext { ServiceContext = serviceContext }); - var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, serviceContext.TransportContext.Log); + var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var socketOutput = new OutputProducer(pipe.Writer, frame, "0", serviceContext.Log); - var consumer = new LibuvOutputConsumer(pipe.Reader, _libuvThread, socket, connection ?? new MockConnection(), "0", serviceContext.TransportContext.Log); + var consumer = new LibuvOutputConsumer(pipe.Reader, _libuvThread, socket, connection ?? new MockConnection(), "0", transportContext.Log); var ignore = consumer.StartWrites(); return socketOutput; diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/LibuvTransportFactoryTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportFactoryTests.cs similarity index 92% rename from test/Microsoft.AspNetCore.Server.KestrelTests/LibuvTransportFactoryTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportFactoryTests.cs index fb11478116..1e80201397 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/LibuvTransportFactoryTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportFactoryTests.cs @@ -2,14 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class LibuvTransportFactoryTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/LibuvTransportOptionsTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportOptionsTests.cs similarity index 87% rename from test/Microsoft.AspNetCore.Server.KestrelTests/LibuvTransportOptionsTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportOptionsTests.cs index df45392c07..8651bdb2ff 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/LibuvTransportOptionsTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportOptionsTests.cs @@ -2,10 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class LibuvTransportOptionsTests { diff --git a/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportTests.cs new file mode 100644 index 0000000000..33c971a4b6 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/LibuvTransportTests.cs @@ -0,0 +1,80 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; +using Microsoft.AspNetCore.Testing; +using Xunit; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests +{ + public class LibuvTransportTests + { + public static TheoryData ConnectionAdapterData => new TheoryData + { + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)), + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)) + { + ConnectionAdapters = { new PassThroughConnectionAdapter() } + } + }; + + [Fact] + public async Task TransportCanBindAndStop() + { + var transportContext = new TestLibuvTransportContext(); + var transport = new LibuvTransport(transportContext, + new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0))); + + // The transport can no longer start threads without binding to an endpoint. + await transport.BindAsync(); + await transport.StopAsync(); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task TransportCanBindUnbindAndStop(ListenOptions listenOptions) + { + var transportContext = new TestLibuvTransportContext(); + var transport = new LibuvTransport(transportContext, listenOptions); + + await transport.BindAsync(); + await transport.UnbindAsync(); + await transport.StopAsync(); + } + + [Theory] + [MemberData(nameof(ConnectionAdapterData))] + public async Task ConnectionCanReadAndWrite(ListenOptions listenOptions) + { + var transportContext = new TestLibuvTransportContext() + { + ConnectionHandler = new ConnectionHandler(listenOptions, new TestServiceContext(), new DummyApplication(TestApp.EchoApp)) + }; + var transport = new LibuvTransport(transportContext, listenOptions); + + await transport.BindAsync(); + + using (var socket = TestConnection.CreateConnectedLoopbackSocket(listenOptions.IPEndPoint.Port)) + { + var data = "Hello World"; + socket.Send(Encoding.ASCII.GetBytes($"POST / HTTP/1.0\r\nContent-Length: 11\r\n\r\n{data}")); + var buffer = new byte[data.Length]; + var read = 0; + while (read < data.Length) + { + read += socket.Receive(buffer, read, buffer.Length - read, SocketFlags.None); + } + } + + await transport.UnbindAsync(); + await transport.StopAsync(); + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/ListenerPrimaryTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/ListenerPrimaryTests.cs similarity index 80% rename from test/Microsoft.AspNetCore.Server.KestrelTests/ListenerPrimaryTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/ListenerPrimaryTests.cs index 797e7d00a4..46defb4317 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/ListenerPrimaryTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/ListenerPrimaryTests.cs @@ -9,15 +9,14 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class ListenerPrimaryTests { @@ -28,14 +27,16 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); var serviceContextPrimary = new TestServiceContext(); - serviceContextPrimary.TransportContext.ConnectionHandler = new ConnectionHandler( + var transportContextPrimary = new TestLibuvTransportContext(); + transportContextPrimary.ConnectionHandler = new ConnectionHandler( listenOptions, serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary"))); var serviceContextSecondary = new TestServiceContext(); - serviceContextSecondary.TransportContext.ConnectionHandler = new ConnectionHandler( + var transportContextSecondary = new TestLibuvTransportContext(); + transportContextSecondary.ConnectionHandler = new ConnectionHandler( listenOptions, serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary"))); - var libuvTransport = new LibuvTransport(libuv, serviceContextPrimary.TransportContext, listenOptions); + var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); @@ -43,7 +44,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests // Start primary listener var libuvThreadPrimary = new LibuvThread(libuvTransport); await libuvThreadPrimary.StartAsync(); - var listenerPrimary = new ListenerPrimary(serviceContextPrimary.TransportContext); + var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); var address = listenOptions.ToString(); @@ -54,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.KestrelTests // Add secondary listener var libuvThreadSecondary = new LibuvThread(libuvTransport); await libuvThreadSecondary.StartAsync(); - var listenerSecondary = new ListenerSecondary(serviceContextSecondary.TransportContext); + var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadSecondary); // Once a secondary listener is added, TCP connections start getting dispatched to it @@ -79,8 +80,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var libuv = new LibuvFunctions(); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var logger = new TestApplicationErrorLogger(); + var serviceContextPrimary = new TestServiceContext(); - serviceContextPrimary.TransportContext.ConnectionHandler = new ConnectionHandler( + var transportContextPrimary = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) }; + transportContextPrimary.ConnectionHandler = new ConnectionHandler( listenOptions, serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary"))); var serviceContextSecondary = new TestServiceContext @@ -90,10 +94,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests ThreadPool = serviceContextPrimary.ThreadPool, HttpParserFactory = serviceContextPrimary.HttpParserFactory, }; - serviceContextSecondary.TransportContext.ConnectionHandler = new ConnectionHandler( + var transportContextSecondary = new TestLibuvTransportContext(); + transportContextSecondary.ConnectionHandler = new ConnectionHandler( listenOptions, serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary"))); - var libuvTransport = new LibuvTransport(libuv, serviceContextPrimary.TransportContext, listenOptions); + var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); @@ -101,14 +106,14 @@ namespace Microsoft.AspNetCore.Server.KestrelTests // Start primary listener var libuvThreadPrimary = new LibuvThread(libuvTransport); await libuvThreadPrimary.StartAsync(); - var listenerPrimary = new ListenerPrimary(serviceContextPrimary.TransportContext); + var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); var address = listenOptions.ToString(); // Add secondary listener var libuvThreadSecondary = new LibuvThread(libuvTransport); await libuvThreadSecondary.StartAsync(); - var listenerSecondary = new ListenerSecondary(serviceContextSecondary.TransportContext); + var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadSecondary); // TCP Connections get round-robined @@ -155,10 +160,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests await libuvThreadPrimary.PostAsync(_ => pipe.Dispose(), (object)null); - var primaryTrace = (TestKestrelTrace)serviceContextPrimary.Log; - // Wait up to 10 seconds for error to be logged - for (var i = 0; i < 10 && primaryTrace.Logger.TotalErrorsLogged == 0; i++) + for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) { await Task.Delay(100); } @@ -174,8 +177,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(1)); - Assert.Equal(1, primaryTrace.Logger.TotalErrorsLogged); - var errorMessage = primaryTrace.Logger.Messages.First(m => m.LogLevel == LogLevel.Error); + Assert.Equal(1, logger.TotalErrorsLogged); + var errorMessage = logger.Messages.First(m => m.LogLevel == LogLevel.Error); Assert.Equal(TestConstants.EOF, Assert.IsType(errorMessage.Exception).StatusCode); } @@ -186,8 +189,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests var libuv = new LibuvFunctions(); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var logger = new TestApplicationErrorLogger(); + var serviceContextPrimary = new TestServiceContext(); - serviceContextPrimary.TransportContext.ConnectionHandler = new ConnectionHandler( + var transportContextPrimary = new TestLibuvTransportContext() { Log = new LibuvTrace(logger) }; + transportContextPrimary.ConnectionHandler = new ConnectionHandler( listenOptions, serviceContextPrimary, new DummyApplication(c => c.Response.WriteAsync("Primary"))); var serviceContextSecondary = new TestServiceContext @@ -197,10 +203,11 @@ namespace Microsoft.AspNetCore.Server.KestrelTests ThreadPool = serviceContextPrimary.ThreadPool, HttpParserFactory = serviceContextPrimary.HttpParserFactory, }; - serviceContextSecondary.TransportContext.ConnectionHandler = new ConnectionHandler( + var transportContextSecondary = new TestLibuvTransportContext(); + transportContextSecondary.ConnectionHandler = new ConnectionHandler( listenOptions, serviceContextSecondary, new DummyApplication(c => c.Response.WriteAsync("Secondary"))); - var libuvTransport = new LibuvTransport(libuv, serviceContextPrimary.TransportContext, listenOptions); + var libuvTransport = new LibuvTransport(libuv, transportContextPrimary, listenOptions); var pipeName = (libuv.IsWindows ? @"\\.\pipe\kestrel_" : "/tmp/kestrel_") + Guid.NewGuid().ToString("n"); var pipeMessage = Guid.NewGuid().ToByteArray(); @@ -208,20 +215,18 @@ namespace Microsoft.AspNetCore.Server.KestrelTests // Start primary listener var libuvThreadPrimary = new LibuvThread(libuvTransport); await libuvThreadPrimary.StartAsync(); - var listenerPrimary = new ListenerPrimary(serviceContextPrimary.TransportContext); + var listenerPrimary = new ListenerPrimary(transportContextPrimary); await listenerPrimary.StartAsync(pipeName, pipeMessage, listenOptions, libuvThreadPrimary); var address = listenOptions.ToString(); // Add secondary listener with wrong pipe message var libuvThreadSecondary = new LibuvThread(libuvTransport); await libuvThreadSecondary.StartAsync(); - var listenerSecondary = new ListenerSecondary(serviceContextSecondary.TransportContext); + var listenerSecondary = new ListenerSecondary(transportContextSecondary); await listenerSecondary.StartAsync(pipeName, Guid.NewGuid().ToByteArray(), listenOptions, libuvThreadSecondary); - var primaryTrace = (TestKestrelTrace)serviceContextPrimary.Log; - // Wait up to 10 seconds for error to be logged - for (var i = 0; i < 10 && primaryTrace.Logger.TotalErrorsLogged == 0; i++) + for (var i = 0; i < 10 && logger.TotalErrorsLogged == 0; i++) { await Task.Delay(100); } @@ -237,8 +242,8 @@ namespace Microsoft.AspNetCore.Server.KestrelTests await listenerPrimary.DisposeAsync(); await libuvThreadPrimary.StopAsync(TimeSpan.FromSeconds(1)); - Assert.Equal(1, primaryTrace.Logger.TotalErrorsLogged); - var errorMessage = primaryTrace.Logger.Messages.First(m => m.LogLevel == LogLevel.Error); + Assert.Equal(1, logger.TotalErrorsLogged); + var errorMessage = logger.Messages.First(m => m.LogLevel == LogLevel.Error); Assert.IsType(errorMessage.Exception); Assert.Contains("Bad data", errorMessage.Exception.ToString()); } diff --git a/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.csproj b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.csproj new file mode 100644 index 0000000000..ceb08babf0 --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.csproj @@ -0,0 +1,35 @@ + + + + + + netcoreapp2.0;net46 + netcoreapp2.0 + x64 + true + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/MultipleLoopTests.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/MultipleLoopTests.cs index 66a7b64fa6..59f7a8f719 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/MultipleLoopTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/MultipleLoopTests.cs @@ -6,14 +6,14 @@ using System.Net; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading; +using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; -using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class MultipleLoopTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/NetworkingTests.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/NetworkingTests.cs index 11225a2e61..fd103c299f 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/NetworkingTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/NetworkingTests.cs @@ -12,7 +12,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { /// /// Summary description for NetworkingTests diff --git a/test/shared/MockConnection.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockConnection.cs similarity index 92% rename from test/shared/MockConnection.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockConnection.cs index d37696f9c7..a40892a1d0 100644 --- a/test/shared/MockConnection.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockConnection.cs @@ -4,11 +4,10 @@ using System; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.Extensions.Internal; -namespace Microsoft.AspNetCore.Testing +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers { public class MockConnection : LibuvConnection, IDisposable { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockConnectionHandler.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockConnectionHandler.cs similarity index 96% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockConnectionHandler.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockConnectionHandler.cs index 4b784346ae..44bda00009 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockConnectionHandler.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockConnectionHandler.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Internal.System.IO.Pipelines; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers { public class MockConnectionHandler : IConnectionHandler, IDisposable { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockLibuv.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockLibuv.cs similarity index 98% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockLibuv.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockLibuv.cs index 8c0dea28e2..e18e2e8572 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockLibuv.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockLibuv.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers { public class MockLibuv : LibuvFunctions { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocket.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockSocket.cs similarity index 89% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocket.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockSocket.cs index a8ab5075f3..110f8667ba 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/MockSocket.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/MockSocket.cs @@ -5,7 +5,7 @@ using System; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers { class MockSocket : UvStreamHandle { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/TestConstants.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestConstants.cs similarity index 76% rename from test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/TestConstants.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestConstants.cs index 25f213c2a7..e9843bda85 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/TestHelpers/TestConstants.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestConstants.cs @@ -1,7 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -namespace Microsoft.AspNetCore.Server.KestrelTests.TestHelpers +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers { public class TestConstants { diff --git a/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestLibuvTransportContext.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestLibuvTransportContext.cs new file mode 100644 index 0000000000..28b1da297d --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/TestHelpers/TestLibuvTransportContext.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; +using Microsoft.AspNetCore.Testing; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers +{ + public class TestLibuvTransportContext : LibuvTransportContext + { + public TestLibuvTransportContext() + { + var logger = new TestApplicationErrorLogger(); + + AppLifetime = new LifetimeNotImplemented(); + ConnectionHandler = new MockConnectionHandler(); + Log = new LibuvTrace(logger); + Options = new LibuvTransportOptions + { + ThreadCount = 1, + ShutdownTimeout = TimeSpan.FromSeconds(5) + }; + } + } +} diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/UvStreamHandleTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/UvStreamHandleTests.cs similarity index 87% rename from test/Microsoft.AspNetCore.Server.KestrelTests/UvStreamHandleTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/UvStreamHandleTests.cs index 0754287832..d52e120add 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/UvStreamHandleTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/UvStreamHandleTests.cs @@ -3,11 +3,11 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; -using Microsoft.AspNetCore.Server.KestrelTests.TestHelpers; +using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests.TestHelpers; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class UvStreamHandleTests { diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/UvTimerHandleTests.cs b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/UvTimerHandleTests.cs similarity index 96% rename from test/Microsoft.AspNetCore.Server.KestrelTests/UvTimerHandleTests.cs rename to test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/UvTimerHandleTests.cs index 832e699c18..0eafb1a48b 100644 --- a/test/Microsoft.AspNetCore.Server.KestrelTests/UvTimerHandleTests.cs +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/UvTimerHandleTests.cs @@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking; using Microsoft.AspNetCore.Testing; using Xunit; -namespace Microsoft.AspNetCore.Server.KestrelTests +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests { public class UvTimerHandleTests { diff --git a/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/xunit.runner.json b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/xunit.runner.json new file mode 100644 index 0000000000..3a5192e57d --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.Transport.Libuv.Tests/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json.schemastore.org/xunit.runner.schema", + "appDomain": "denied", + "methodDisplay": "method", + "longRunningTestSeconds": 60 +} diff --git a/test/shared/HttpParsingData.cs b/test/shared/HttpParsingData.cs index a4d3d5521f..d3774bfd91 100644 --- a/test/shared/HttpParsingData.cs +++ b/test/shared/HttpParsingData.cs @@ -33,6 +33,8 @@ namespace Microsoft.AspNetCore.Testing Tuple.Create("/a%C3%A5a", "/a\u00E5a"), Tuple.Create("/%C3%A5/bc", "/\u00E5/bc"), Tuple.Create("/%25", "/%"), + Tuple.Create("/%25%30%30", "/%00"), + Tuple.Create("/%%2000", "/% 00"), Tuple.Create("/%2F", "/%2F"), Tuple.Create("http://host/abs/path", "/abs/path"), Tuple.Create("http://host/abs/path/", "/abs/path/"), diff --git a/test/shared/TestApplicationErrorLogger.cs b/test/shared/TestApplicationErrorLogger.cs index dcba1da052..d77a1c08ed 100644 --- a/test/shared/TestApplicationErrorLogger.cs +++ b/test/shared/TestApplicationErrorLogger.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Linq; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.Extensions.Logging; diff --git a/test/shared/TestServiceContext.cs b/test/shared/TestServiceContext.cs index ca9c28cecc..9223ad87f4 100644 --- a/test/shared/TestServiceContext.cs +++ b/test/shared/TestServiceContext.cs @@ -1,13 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv; -using Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal; namespace Microsoft.AspNetCore.Testing { @@ -26,21 +23,8 @@ namespace Microsoft.AspNetCore.Testing { AddServerHeader = false }; - - TransportContext = new LibuvTransportContext - { - AppLifetime = new LifetimeNotImplemented(), - Log = new LibuvTrace(logger), - Options = new LibuvTransportOptions - { - ThreadCount = 1, - ShutdownTimeout = TimeSpan.FromSeconds(5) - } - }; } public string DateHeaderValue { get; } - - public LibuvTransportContext TransportContext { get; } } }