diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index eb38671015..6070086773 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -254,6 +254,38 @@ stages: - name: Windows_arm_Packages path: artifacts/packages/ + # Build Windows ARM64 + - template: jobs/default-build.yml + parameters: + codeSign: true + jobName: Windows_64_build + jobDisplayName: "Build: Windows ARM64" + agentOs: Windows + buildArgs: + -arch arm64 + -sign + -pack + -noBuildNodeJS + -noBuildJava + /bl:artifacts/log/build.win-arm64.binlog + /p:DotNetSignType=$(_SignType) + /p:OnlyPackPlatformSpecificPackages=true + /p:AssetManifestFileName=aspnetcore-win-arm64.xml + $(_BuildArgs) + $(_PublishArgs) + $(_InternalRuntimeDownloadArgs) + installNodeJs: false + installJdk: false + artifacts: + - name: Windows_arm64_Logs + path: artifacts/log/ + publishOnError: true + includeForks: true + - name: Windows_arm64_Packages + path: artifacts/packages/ + - name: Windows_arm64_Installers + path: artifacts/installers/ + # Build MacOS - template: jobs/default-build.yml parameters: @@ -683,7 +715,7 @@ stages: # Helix ARM64 - template: jobs/default-build.yml parameters: - condition: ne(variables['Build.Reason'], 'PullRequest') + condition: and(eq(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) jobName: Helix_arm64_daily jobDisplayName: "Tests: Helix ARM64 Daily" agentOs: Linux diff --git a/.azure/pipelines/quarantined-tests.yml b/.azure/pipelines/quarantined-tests.yml index bb9224b38b..415853ed8e 100644 --- a/.azure/pipelines/quarantined-tests.yml +++ b/.azure/pipelines/quarantined-tests.yml @@ -32,10 +32,10 @@ jobs: steps: # Build the shared framework - script: ./build.cmd -ci -all -pack -arch x64 -buildNative /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log /bl:artifacts/log/helix.build.x64.binlog - displayName: Build shared fx + displayName: Build shared fx - script: .\restore.cmd -ci displayName: Restore - - script: .\build.cmd -ci -NoRestore -test -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl + - script: .\build.cmd -ci -NoRestore -test -noBuildJava -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl displayName: Run build.cmd helix target env: HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues diff --git a/Directory.Build.props b/Directory.Build.props index a2765ff560..c40d203441 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -123,6 +123,7 @@ win-x64; win-x86; win-arm; + win-arm64; osx-x64; linux-musl-x64; linux-musl-arm64; diff --git a/build.ps1 b/build.ps1 index 7ce1d87f58..f371d26673 100644 --- a/build.ps1 +++ b/build.ps1 @@ -118,7 +118,7 @@ param( [ValidateSet('Debug', 'Release')] $Configuration, - [ValidateSet('x64', 'x86', 'arm')] + [ValidateSet('x64', 'x86', 'arm', 'arm64')] $Architecture = 'x64', # A list of projects which should be built. @@ -157,7 +157,7 @@ param( # Other lifecycle targets [switch]$Help, # Show help - + # Optional arguments that enable downloading an internal # runtime or runtime from a non-default location [string]$DotNetRuntimeSourceFeed, diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 0603add6c3..6e8e7b9a8b 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -94,6 +94,7 @@ and are generated based on the last package release. + diff --git a/eng/Signing.props b/eng/Signing.props index 3b61e9205f..024a5c8a0a 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -93,9 +93,11 @@ <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x64\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x86\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> + <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm64\shared\Microsoft.NETCore.App\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x64\host\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-x86\host\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm\host\**\*.dll" CertificateName="None" /> + <_DotNetFilesToExclude Include="$(BaseRedistNetCorePath)win-arm64\host\**\*.dll" CertificateName="None" /> <_DotNetFilesToExclude Include="$(RedistNetCorePath)dotnet.exe" CertificateName="None" /> diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 778c65dab1..4cb798c0aa 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -13,328 +13,328 @@ https://github.com/dotnet/blazor dd7fb4d3931d556458f62642c2edfc59f6295bfb - + https://github.com/dotnet/aspnetcore-tooling - 33a9f3ca20e57e7fb69fd90b9aa9b2278f377a54 + cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/aspnetcore-tooling - 33a9f3ca20e57e7fb69fd90b9aa9b2278f377a54 + cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/aspnetcore-tooling - 33a9f3ca20e57e7fb69fd90b9aa9b2278f377a54 + cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/aspnetcore-tooling - 33a9f3ca20e57e7fb69fd90b9aa9b2278f377a54 + cc921d78edc850e3214917274d117bcc1450884b - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/efcore - 39633487003879903958dc09be5bb4ec6d6034df + f1a6840fbc5c72a6cf2756dbdbcc65622434ebd7 - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/runtime - 9bcf3b12f4e7ad2667ca00afc804877d78b81a46 + 2f6e072c97738a56eeeb25f1fdb5dcc955649db0 - + https://github.com/dotnet/extensions - a1c36119ce1b3ff9a7a69f7b088b9601962e58e2 + 540b4e8f129a132749a60174464b8c8274bfed7d https://github.com/dotnet/arcade diff --git a/eng/Versions.props b/eng/Versions.props index 32a5384560..c3b185ac09 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -9,7 +9,7 @@ 5 0 0 - 2 + 3 @@ -66,92 +66,92 @@ 3.5.0-beta4-20153-05 - 5.0.0-preview.2-runtime.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 2.1.0-preview.2.20154.1 + 5.0.0-preview.2-runtime.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 2.1.0-preview.2.20155.3 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 + 5.0.0-preview.2.20155.3 - 5.0.0-preview.2.20154.1 + 5.0.0-preview.2.20155.3 3.2.0-preview1.20067.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 - 5.0.0-preview.2.20154.4 - 5.0.0-preview.2.20154.4 - 5.0.0-preview.2.20154.4 - 5.0.0-preview.2.20154.4 - 5.0.0-preview.2.20154.4 - 5.0.0-preview.2.20154.4 - 5.0.0-preview.2.20154.4 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 + 5.0.0-preview.3.20156.3 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 - 5.0.0-preview.2.20154.1 + 5.0.0-preview.3.20157.1 + 5.0.0-preview.3.20157.1 + 5.0.0-preview.3.20157.1 + 5.0.0-preview.3.20157.1 diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/InputFlowControl.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/InputFlowControl.cs index 7231f882b7..3ee7ca5848 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/InputFlowControl.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/InputFlowControl.cs @@ -26,6 +26,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl public bool IsAvailabilityLow => _flow.Available < _minWindowSizeIncrement; + public void Reset() + { + _flow = new FlowControl((uint)_initialWindowSize); + _pendingUpdateSize = 0; + _windowUpdatesDisabled = false; + } + public bool TryAdvance(int bytes) { lock (_flowLock) diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/OutputFlowControl.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/OutputFlowControl.cs index 609398d3d7..c5ccd92b1c 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/OutputFlowControl.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/OutputFlowControl.cs @@ -37,6 +37,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl } } + public void Reset(uint initialWindowSize) + { + // When output flow control is reused the client window size needs to be reset. + // The client might have changed the window size before the stream is reused. + _flow = new FlowControl(initialWindowSize); + Debug.Assert((_awaitableQueue?.Count ?? 0) == 0, "Queue should have been emptied by the previous stream."); + } + public void Advance(int bytes) { _flow.Advance(bytes); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamInputFlowControl.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamInputFlowControl.cs index afcd4219dc..7e7c8434e8 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamInputFlowControl.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamInputFlowControl.cs @@ -10,11 +10,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl private readonly InputFlowControl _connectionLevelFlowControl; private readonly InputFlowControl _streamLevelFlowControl; - private readonly int _streamId; + private int StreamId => _stream.StreamId; + private readonly Http2Stream _stream; private readonly Http2FrameWriter _frameWriter; public StreamInputFlowControl( - int streamId, + Http2Stream stream, Http2FrameWriter frameWriter, InputFlowControl connectionLevelFlowControl, uint initialWindowSize, @@ -22,11 +23,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl { _connectionLevelFlowControl = connectionLevelFlowControl; _streamLevelFlowControl = new InputFlowControl(initialWindowSize, minWindowSizeIncrement); - - _streamId = streamId; + _stream = stream; _frameWriter = frameWriter; } + public void Reset() + { + _streamLevelFlowControl.Reset(); + } + public void Advance(int bytes) { var connectionSuccess = _connectionLevelFlowControl.TryAdvance(bytes); @@ -52,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl if (streamWindowUpdateSize > 0) { // Writing with the FrameWriter should only fail if given a canceled token, so just fire and forget. - _ = _frameWriter.WriteWindowUpdateAsync(_streamId, streamWindowUpdateSize); + _ = _frameWriter.WriteWindowUpdateAsync(StreamId, streamWindowUpdateSize); } UpdateConnectionWindow(bytes); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs index 35dccd92fc..8781f163c6 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/FlowControl/StreamOutputFlowControl.cs @@ -24,6 +24,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl public bool IsAborted => _connectionLevelFlowControl.IsAborted || _streamLevelFlowControl.IsAborted; + public void Reset(uint initialWindowSize) + { + _streamLevelFlowControl.Reset(initialWindowSize); + if (_currentConnectionLevelAwaitable != null) + { + Debug.Assert(_currentConnectionLevelAwaitable.IsCompleted, "Should have been completed by the previous stream."); + _currentConnectionLevelAwaitable = null; + } + } + public void Advance(int bytes) { _connectionLevelFlowControl.Advance(bytes); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs index c3dbfb5d02..41d09b162a 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Connection.cs @@ -607,6 +607,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 private void ReturnStream(Http2Stream stream) { + // We're conservative about what streams we can reuse. + // If there is a chance the stream is still in use then don't attempt to reuse it. + Debug.Assert(stream.CanReuse); + if (StreamPool.Count < MaxStreamPoolSize) { StreamPool.Push(stream); @@ -1066,7 +1070,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } _streams.Remove(stream.StreamId); - ReturnStream(stream); + if (stream.CanReuse) + { + ReturnStream(stream); + } } else { @@ -1093,7 +1100,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } _streams.Remove(stream.StreamId); - ReturnStream(stream); + if (stream.CanReuse) + { + ReturnStream(stream); + } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs index 3bd9794e47..d129540342 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { internal class Http2OutputProducer : IHttpOutputProducer, IHttpOutputAborter { - private readonly int _streamId; + private int StreamId => _stream.StreamId; private readonly Http2FrameWriter _frameWriter; private readonly TimingPipeFlusher _flusher; private readonly IKestrelTrace _log; @@ -30,53 +30,65 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 private readonly MemoryPool _memoryPool; private readonly Http2Stream _stream; private readonly object _dataWriterLock = new object(); - private readonly PipeWriter _pipeWriter; + private readonly Pipe _pipe; + private readonly ConcurrentPipeWriter _pipeWriter; private readonly PipeReader _pipeReader; - private readonly ValueTask _dataWriteProcessingTask; + private ValueTask _dataWriteProcessingTask; private bool _startedWritingDataFrames; - private bool _completed; + private bool _streamCompleted; private bool _suffixSent; private bool _streamEnded; - private bool _disposed; + private bool _writerComplete; private IMemoryOwner _fakeMemoryOwner; - public Http2OutputProducer( - int streamId, - Http2FrameWriter frameWriter, - StreamOutputFlowControl flowControl, - MemoryPool pool, - Http2Stream stream, - IKestrelTrace log) + public Http2OutputProducer(Http2Stream stream, Http2StreamContext context, StreamOutputFlowControl flowControl) { - _streamId = streamId; - _frameWriter = frameWriter; - _flowControl = flowControl; - _memoryPool = pool; _stream = stream; - _log = log; + _frameWriter = context.FrameWriter; + _flowControl = flowControl; + _memoryPool = context.MemoryPool; + _log = context.ServiceContext.Log; - var pipe = CreateDataPipe(pool); + _pipe = CreateDataPipe(_memoryPool); - _pipeWriter = new ConcurrentPipeWriter(pipe.Writer, pool, _dataWriterLock); - _pipeReader = pipe.Reader; + _pipeWriter = new ConcurrentPipeWriter(_pipe.Writer, _memoryPool, _dataWriterLock); + _pipeReader = _pipe.Reader; // No need to pass in timeoutControl here, since no minDataRates are passed to the TimingPipeFlusher. // The minimum output data rate is enforced at the connection level by Http2FrameWriter. - _flusher = new TimingPipeFlusher(_pipeWriter, timeoutControl: null, log); + _flusher = new TimingPipeFlusher(_pipeWriter, timeoutControl: null, _log); + _dataWriteProcessingTask = ProcessDataWrites(); } - public void Dispose() + public void StreamReset() + { + Debug.Assert(_dataWriteProcessingTask.IsCompletedSuccessfully); + + _streamEnded = false; + _suffixSent = false; + _suffixSent = false; + _startedWritingDataFrames = false; + _streamCompleted = false; + _writerComplete = false; + + _pipe.Reset(); + _pipeWriter.Reset(); + + _dataWriteProcessingTask = ProcessDataWrites(); + } + + public void Complete() { lock (_dataWriterLock) { - if (_disposed) + if (_writerComplete) { return; } - _disposed = true; + _writerComplete = true; Stop(); @@ -107,9 +119,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); - if (_completed) + if (_streamCompleted) { return default; } @@ -133,14 +145,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); - if (_completed) + if (_streamCompleted) { return default; } - return _frameWriter.Write100ContinueAsync(_streamId); + return _frameWriter.Write100ContinueAsync(StreamId); } } @@ -150,7 +162,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { // The HPACK header compressor is stateful, if we compress headers for an aborted stream we must send them. // Optimize for not compressing or sending them. - if (_completed) + if (_streamCompleted) { return; } @@ -175,7 +187,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 http2HeadersFrame = Http2HeadersFrameFlags.NONE; } - _frameWriter.WriteResponseHeaders(_streamId, statusCode, http2HeadersFrame, responseHeaders); + _frameWriter.WriteResponseHeaders(StreamId, statusCode, http2HeadersFrame, responseHeaders); } } @@ -188,11 +200,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); // This length check is important because we don't want to set _startedWritingDataFrames unless a data // frame will actually be written causing the headers to be flushed. - if (_completed || data.Length == 0) + if (_streamCompleted || data.Length == 0) { return Task.CompletedTask; } @@ -208,12 +220,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - if (_completed) + if (_streamCompleted) { return _dataWriteProcessingTask; } - _completed = true; + _streamCompleted = true; _suffixSent = true; _pipeWriter.Complete(); @@ -228,7 +240,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 // Always send the reset even if the response body is _completed. The request body may not have completed yet. Stop(); - return _frameWriter.WriteRstStreamAsync(_streamId, error); + return _frameWriter.WriteRstStreamAsync(StreamId, error); } } @@ -236,9 +248,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); - if (_completed) + if (_streamCompleted) { return; } @@ -253,9 +265,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); - if (_completed) + if (_streamCompleted) { return GetFakeMemory(sizeHint).Span; } @@ -268,9 +280,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); - if (_completed) + if (_streamCompleted) { return GetFakeMemory(sizeHint); } @@ -283,7 +295,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - if (_completed) + if (_streamCompleted) { return; } @@ -301,11 +313,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 lock (_dataWriterLock) { - ThrowIfSuffixSentOrDisposed(); + ThrowIfSuffixSentOrCompleted(); // This length check is important because we don't want to set _startedWritingDataFrames unless a data // frame will actually be written causing the headers to be flushed. - if (_completed || data.Length == 0) + if (_streamCompleted || data.Length == 0) { return default; } @@ -341,12 +353,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { lock (_dataWriterLock) { - if (_completed) + if (_streamCompleted) { return; } - _completed = true; + _streamCompleted = true; _pipeReader.CancelPendingRead(); @@ -382,12 +394,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { // Only flush if required (i.e. content length exceeds flow control availability) // Writing remaining content without flushing allows content and trailers to be sent in the same packet - await _frameWriter.WriteDataAsync(_streamId, _flowControl, readResult.Buffer, endStream: false, forceFlush: false); + await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream: false, forceFlush: false); } _stream.ResponseTrailers.SetReadOnly(); _stream.DecrementActiveClientStreamCount(); - flushResult = await _frameWriter.WriteResponseTrailers(_streamId, _stream.ResponseTrailers); + flushResult = await _frameWriter.WriteResponseTrailers(StreamId, _stream.ResponseTrailers); } else if (readResult.IsCompleted && _streamEnded) { @@ -406,7 +418,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { _stream.DecrementActiveClientStreamCount(); } - flushResult = await _frameWriter.WriteDataAsync(_streamId, _flowControl, readResult.Buffer, endStream, forceFlush: true); + flushResult = await _frameWriter.WriteDataAsync(StreamId, _flowControl, readResult.Buffer, endStream, forceFlush: true); } _pipeReader.AdvanceTo(readResult.Buffer.End); @@ -438,16 +450,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } [StackTraceHidden] - private void ThrowIfSuffixSentOrDisposed() + private void ThrowIfSuffixSentOrCompleted() { if (_suffixSent) { ThrowSuffixSent(); } - if (_disposed) + if (_writerComplete) { - ThrowDisposed(); + ThrowWriterComplete(); } } @@ -458,7 +470,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } [StackTraceHidden] - private static void ThrowDisposed() + private static void ThrowWriterComplete() { throw new InvalidOperationException("Cannot write to response after the request has completed."); } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index b3dccb8a23..ee97bec12f 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -37,6 +37,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { base.Initialize(context); + CanReuse = false; _decrementCalled = false; _completionState = StreamCompletionFlags.None; InputRemaining = null; @@ -45,27 +46,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 _context = context; - _inputFlowControl = new StreamInputFlowControl( - context.StreamId, - context.FrameWriter, - context.ConnectionInputFlowControl, - context.ServerPeerSettings.InitialWindowSize, - context.ServerPeerSettings.InitialWindowSize / 2); + // First time the stream is used we need to create flow control, producer and pipes. + // When a stream is reused these types will be reset and reused. + if (_inputFlowControl == null) + { + _inputFlowControl = new StreamInputFlowControl( + this, + context.FrameWriter, + context.ConnectionInputFlowControl, + context.ServerPeerSettings.InitialWindowSize, + context.ServerPeerSettings.InitialWindowSize / 2); - _outputFlowControl = new StreamOutputFlowControl( - context.ConnectionOutputFlowControl, - context.ClientPeerSettings.InitialWindowSize); + _outputFlowControl = new StreamOutputFlowControl( + context.ConnectionOutputFlowControl, + context.ClientPeerSettings.InitialWindowSize); - _http2Output = new Http2OutputProducer( - context.StreamId, - context.FrameWriter, - _outputFlowControl, - context.MemoryPool, - this, - context.ServiceContext.Log); + _http2Output = new Http2OutputProducer(this, context, _outputFlowControl); - RequestBodyPipe = CreateRequestBodyPipe(context.ServerPeerSettings.InitialWindowSize); - Output = _http2Output; + RequestBodyPipe = CreateRequestBodyPipe(); + + Output = _http2Output; + } + else + { + _inputFlowControl.Reset(); + _outputFlowControl.Reset(context.ClientPeerSettings.InitialWindowSize); + _http2Output.StreamReset(); + RequestBodyPipe.Reset(); + } } public void InitializeWithExistingContext(int streamId) @@ -95,6 +103,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } } + public bool CanReuse { get; private set; } + protected override void OnReset() { _keepAlive = true; @@ -137,13 +147,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } } - _http2Output.Dispose(); + _http2Output.Complete(); RequestBodyPipe.Reader.Complete(); // The app can no longer read any more of the request body, so return any bytes that weren't read to the // connection's flow-control window. _inputFlowControl.Abort(); + + // We only want to reuse a stream that has completely finished writing. + // This is to prevent the situation where Http2OutputProducer.ProcessDataWrites + // is still running in the background. + CanReuse = !_keepAlive && HasResponseCompleted; } finally { @@ -543,7 +558,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 _context.StreamLifetimeHandler.DecrementActiveClientStreamCount(); } - private Pipe CreateRequestBodyPipe(uint windowSize) + private Pipe CreateRequestBodyPipe() => new Pipe(new PipeOptions ( pool: _context.MemoryPool, @@ -551,8 +566,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 writerScheduler: PipeScheduler.Inline, // Never pause within the window range. Flow control will prevent more data from being added. // See the assert in OnDataAsync. - pauseWriterThreshold: windowSize + 1, - resumeWriterThreshold: windowSize + 1, + pauseWriterThreshold: _context.ServerPeerSettings.InitialWindowSize + 1, + resumeWriterThreshold: _context.ServerPeerSettings.InitialWindowSize + 1, useSynchronizationContext: false, minimumSegmentSize: _context.MemoryPool.GetMinimumSegmentSize() )); diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/ConcurrentPipeWriter.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/ConcurrentPipeWriter.cs index 4e09b0a8a4..5e871e9dad 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/ConcurrentPipeWriter.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/PipeWriterHelpers/ConcurrentPipeWriter.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Diagnostics; using System.IO.Pipelines; using System.Runtime.CompilerServices; using System.Threading; @@ -59,6 +60,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.PipeW _sync = sync; } + public void Reset() + { + Debug.Assert(_currentFlushTcs == null, "There should not be a pending flush."); + + _aborted = false; + _completeException = null; + } + public override Memory GetMemory(int sizeHint = 0) { if (_currentFlushTcs == null && _head == null) diff --git a/src/Servers/Kestrel/Core/test/Http2HeadersEnumeratorTests.cs b/src/Servers/Kestrel/Core/test/Http2HeadersEnumeratorTests.cs new file mode 100644 index 0000000000..8aed0c4498 --- /dev/null +++ b/src/Servers/Kestrel/Core/test/Http2HeadersEnumeratorTests.cs @@ -0,0 +1,88 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests +{ + public class Http2HeadersEnumeratorTests + { + [Fact] + public void CanIterateOverResponseHeaders() + { + var responseHeaders = new HttpResponseHeaders + { + ContentLength = 9, + HeaderAcceptRanges = "AcceptRanges!", + HeaderAge = new StringValues(new[] { "1", "2" }), + HeaderDate = "Date!" + }; + responseHeaders.Append("Name1", "Value1"); + responseHeaders.Append("Name2", "Value2-1"); + responseHeaders.Append("Name2", "Value2-2"); + responseHeaders.Append("Name3", "Value3"); + + var e = new Http2HeadersEnumerator(); + e.Initialize(responseHeaders); + + var headers = GetNormalizedHeaders(e); + + Assert.Equal(new[] + { + new KeyValuePair("Date", "Date!"), + new KeyValuePair("Accept-Ranges", "AcceptRanges!"), + new KeyValuePair("Age", "1"), + new KeyValuePair("Age", "2"), + new KeyValuePair("Content-Length", "9"), + new KeyValuePair("Name1", "Value1"), + new KeyValuePair("Name2", "Value2-1"), + new KeyValuePair("Name2", "Value2-2"), + new KeyValuePair("Name3", "Value3"), + }, headers); + } + + [Fact] + public void CanIterateOverResponseTrailers() + { + var responseHeaders = new HttpResponseTrailers + { + ContentLength = 9, + HeaderETag = "ETag!" + }; + responseHeaders.Append("Name1", "Value1"); + responseHeaders.Append("Name2", "Value2-1"); + responseHeaders.Append("Name2", "Value2-2"); + responseHeaders.Append("Name3", "Value3"); + + var e = new Http2HeadersEnumerator(); + e.Initialize(responseHeaders); + + var headers = GetNormalizedHeaders(e); + + Assert.Equal(new[] + { + new KeyValuePair("ETag", "ETag!"), + new KeyValuePair("Name1", "Value1"), + new KeyValuePair("Name2", "Value2-1"), + new KeyValuePair("Name2", "Value2-2"), + new KeyValuePair("Name3", "Value3"), + }, headers); + } + + private KeyValuePair[] GetNormalizedHeaders(Http2HeadersEnumerator enumerator) + { + var headers = new List>(); + while (enumerator.MoveNext()) + { + headers.Add(enumerator.Current); + } + return headers.ToArray(); + } + } +} diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs index 310398d00e..b0595d4545 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs @@ -12,6 +12,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; @@ -212,7 +213,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests } [Fact] - public async Task StreamPool_EndedStreamErrorsOnStart_ReturnedToPool() + [QuarantinedTest] + public async Task StreamPool_StreamIsInvalidState_DontReturnedToPool() + { + await InitializeConnectionAsync(async context => + { + await context.Response.WriteAsync("Content"); + throw new InvalidOperationException("Put the stream into an invalid state by throwing after writing to response."); + }); + + await StartStreamAsync(1, _browserRequestHeaders, endStream: true); + + await ExpectAsync(Http2FrameType.HEADERS, + withLength: 33, + withFlags: (byte)Http2HeadersFrameFlags.END_HEADERS, + withStreamId: 1); + await ExpectAsync(Http2FrameType.DATA, + withLength: 7, + withFlags: (byte)Http2DataFrameFlags.NONE, + withStreamId: 1); + await WaitForStreamErrorAsync(1, Http2ErrorCode.INTERNAL_ERROR, null); + + // Ping will trigger the stream to be returned to the pool so we can assert it + await SendPingAsync(Http2PingFrameFlags.NONE); + await ExpectAsync(Http2FrameType.PING, + withLength: 8, + withFlags: (byte)Http2PingFrameFlags.ACK, + withStreamId: 0); + + // Stream is not returned to the pool + Assert.Equal(0, _connection.StreamPool.Count); + + await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false); + } + + [Fact] + public async Task StreamPool_EndedStreamErrorsOnStart_NotReturnedToPool() { await InitializeConnectionAsync(_echoApplication); @@ -231,14 +267,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2PingFrameFlags.ACK, withStreamId: 0); - // Stream returned to the pool - Assert.Equal(1, _connection.StreamPool.Count); + // Stream not returned to the pool + Assert.Equal(0, _connection.StreamPool.Count); await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false); } [Fact] - public async Task StreamPool_UnendedStreamErrorsOnStart_ReturnedToPool() + public async Task StreamPool_UnendedStreamErrorsOnStart_NotReturnedToPool() { _serviceContext.ServerOptions.Limits.MinRequestBodyDataRate = null; @@ -268,8 +304,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests withFlags: (byte)Http2PingFrameFlags.ACK, withStreamId: 0); - // Stream was returned to the pool because of the drain timeout - Assert.Equal(1, _connection.StreamPool.Count); + // Drain timeout has past but the stream was not returned because it is unfinished + Assert.Equal(0, _connection.StreamPool.Count); await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false); diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs index 04c77b6d9c..c9ef2f32e8 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs @@ -143,6 +143,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests } [Fact] + [QuarantinedTest] public async Task ClientHandshakeFailureLoggedAsDebug() { var loggerProvider = new HandshakeErrorLoggerProvider(); diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestTests.cs index 2e63e458ac..dafbd6eca7 100644 --- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestTests.cs +++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/RequestTests.cs @@ -1684,6 +1684,61 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests } } + [Fact] + public async Task ReuseRequestHeaderStrings() + { + var testContext = new TestServiceContext(LoggerFactory); + string customHeaderValue = null; + string contentTypeHeaderValue = null; + + await using (var server = new TestServer(context => + { + customHeaderValue = context.Request.Headers["X-CustomHeader"]; + contentTypeHeaderValue = context.Request.ContentType; + return Task.CompletedTask; + }, testContext)) + { + using (var connection = server.CreateConnection()) + { + // First request + await connection.Send( + "GET / HTTP/1.1", + "Host:", + "Content-Type: application/test", + "X-CustomHeader: customvalue", + "", + ""); + await connection.Receive( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + + var initialCustomHeaderValue = customHeaderValue; + var initialContentTypeValue = contentTypeHeaderValue; + + // Second request + await connection.Send( + "GET / HTTP/1.1", + "Host:", + "Content-Type: application/test", + "X-CustomHeader: customvalue", + "", + ""); + await connection.Receive( + "HTTP/1.1 200 OK", + $"Date: {testContext.DateHeaderValue}", + "Content-Length: 0", + "", + ""); + + Assert.NotSame(initialCustomHeaderValue, customHeaderValue); + Assert.Same(initialContentTypeValue, contentTypeHeaderValue); + } + } + } + [Fact] public async Task Latin1HeaderValueAcceptedWhenLatin1OptionIsConfigured() { diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj b/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj index e5c19de1eb..ca861ff26b 100644 --- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj +++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Interop.FunctionalTests.csproj @@ -6,8 +6,6 @@ Interop.FunctionalTests false - - false diff --git a/src/Shared/E2ETesting/E2ETesting.props b/src/Shared/E2ETesting/E2ETesting.props index 64e74c095e..0e6552e7ec 100644 --- a/src/Shared/E2ETesting/E2ETesting.props +++ b/src/Shared/E2ETesting/E2ETesting.props @@ -4,7 +4,7 @@ $(DefaultItemExcludes);node_modules\** $([MSBuild]::NormalizeDirectory('$(ArtifactsTestResultsDir)','$(MSBuildProjectName)')) $([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))artifacts\tmp\selenium\ - true + true $([MSBuild]::NormalizePath($(MSBuildThisFileDirectory)selenium-config.json)) diff --git a/src/Shared/runtime/ReadMe.SharedCode.md b/src/Shared/runtime/ReadMe.SharedCode.md index b9f4803617..f24980b8a2 100644 --- a/src/Shared/runtime/ReadMe.SharedCode.md +++ b/src/Shared/runtime/ReadMe.SharedCode.md @@ -14,22 +14,22 @@ dotnet/AspNetCore code paths: ## Building dotnet/runtime code: - https://github.com/dotnet/runtime/tree/master/docs/workflow -- Run *build.cmd* from the root once: `PS D:\github\runtime> .\build.cmd -subsetCategory libraries` +- Run *build.cmd* from the root once: `PS D:\github\runtime> .\build.cmd -runtimeConfiguration Release -subsetCategory coreclr-libraries` - Build the individual projects: -- `PS D:\github\dotnet\src\libraries\Common\tests> dotnet msbuild /t:rebuild` -- `PS D:\github\dotnet\src\libraries\System.Net.Http\src> dotnet msbuild /t:rebuild` +- `PS D:\github\dotnet\src\libraries\Common\tests> dotnet build` +- `PS D:\github\dotnet\src\libraries\System.Net.Http\src> dotnet build` ### Running dotnet/runtime tests: -- `PS D:\github\runtime\src\libraries\Common\tests> dotnet msbuild /t:rebuildandtest` -- `PS D:\github\runtime\src\libraries\System.Net.Http\tests\UnitTests> dotnet msbuild /t:rebuildandtest` +- `PS D:\github\runtime\src\libraries\Common\tests> dotnet build /t:test` +- `PS D:\github\runtime\src\libraries\System.Net.Http\tests\UnitTests> dotnet build /t:test` ## Building dotnet/AspNetCore code: - https://github.com/dotnet/AspNetCore/blob/master/docs/BuildFromSource.md - Run restore in the root once: `PS D:\github\AspNetCore> .\restore.cmd` - Activate to use the repo local runtime: `PS D:\github\AspNetCore> . .\activate.ps1` - Build the individual projects: -- `(AspNetCore) PS D:\github\AspNetCore\src\Shared\test\Shared.Tests> dotnet msbuild` -- `(AspNetCore) PS D:\github\AspNetCore\src\servers\Kestrel\core\src> dotnet msbuild` +- `(AspNetCore) PS D:\github\AspNetCore\src\Shared\test\Shared.Tests> dotnet build` +- `(AspNetCore) PS D:\github\AspNetCore\src\servers\Kestrel\core\src> dotnet build` ### Running dotnet/AspNetCore tests: - `(AspNetCore) PS D:\github\AspNetCore\src\Shared\test\Shared.Tests> dotnet test` diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs index acd21ef2c4..4d975d5d7e 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs @@ -1214,11 +1214,13 @@ namespace Microsoft.AspNetCore.SignalR.Client finally { invocationMessageChannel.Writer.TryComplete(); - await invocationMessageReceiveTask; timer.Stop(); await timerTask; uploadStreamSource.Cancel(); await HandleConnectionClose(connectionState); + + // await after the connection has been closed, otherwise could deadlock on a user's .On callback(s) + await invocationMessageReceiveTask; } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs index b303e71ddd..e2b18c9857 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs @@ -345,7 +345,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var complete = await connection.ReadSentJsonAsync().OrTimeout(); Assert.Equal(HubProtocolConstants.CompletionMessageType, complete["type"]); Assert.EndsWith("canceled by client.", ((string)complete["error"])); - } + } } [Fact] @@ -414,6 +414,59 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests } } + [Fact] + public async Task CanAwaitInvokeFromOnHandlerWithServerClosingConnection() + { + using (StartVerifiableLog()) + { + var connection = new TestConnection(); + var hubConnection = CreateHubConnection(connection, loggerFactory: LoggerFactory); + await hubConnection.StartAsync().OrTimeout(); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + hubConnection.On("Echo", async msg => + { + try + { + // This should be canceled when the connection is closed + await hubConnection.InvokeAsync("Echo", msg).OrTimeout(); + } + catch (Exception ex) + { + tcs.SetException(ex); + return; + } + + tcs.SetResult(null); + }); + + var closedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + hubConnection.Closed += _ => + { + closedTcs.SetResult(null); + + return Task.CompletedTask; + }; + + await connection.ReceiveJsonMessage(new { type = HubProtocolConstants.InvocationMessageType, target = "Echo", arguments = new object[] { "42" } }); + + // Read sent message first to make sure invoke has been processed and is waiting for a response + await connection.ReadSentJsonAsync().OrTimeout(); + await connection.ReceiveJsonMessage(new { type = HubProtocolConstants.CloseMessageType }); + + await closedTcs.Task.OrTimeout(); + + try + { + await tcs.Task.OrTimeout(); + Assert.True(false); + } + catch (TaskCanceledException) + { + } + } + } + [Fact] public async Task CanAwaitUsingHubConnection() { diff --git a/src/Tools/Shared/TestHelpers/TestConsole.cs b/src/Tools/Shared/TestHelpers/TestConsole.cs index 834767faaa..89439706e2 100644 --- a/src/Tools/Shared/TestHelpers/TestConsole.cs +++ b/src/Tools/Shared/TestHelpers/TestConsole.cs @@ -91,6 +91,8 @@ namespace Microsoft.Extensions.Tools.Internal _output.WriteLine(_sb.ToString()); _sb.Clear(); } + + _currentOutput.Append(value); } else { diff --git a/src/Tools/dotnet-user-secrets/src/Internal/ProjectIdResolver.cs b/src/Tools/dotnet-user-secrets/src/Internal/ProjectIdResolver.cs index 1dafb27ca2..1c00b4b7b0 100644 --- a/src/Tools/dotnet-user-secrets/src/Internal/ProjectIdResolver.cs +++ b/src/Tools/dotnet-user-secrets/src/Internal/ProjectIdResolver.cs @@ -5,7 +5,7 @@ using System; using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; +using System.Text; using Microsoft.Extensions.CommandLineUtils; using Microsoft.Extensions.Tools.Internal; @@ -53,26 +53,51 @@ namespace Microsoft.Extensions.SecretManager.Tools.Internal "/p:Configuration=" + configuration, "/p:CustomAfterMicrosoftCommonTargets=" + _targetsFile, "/p:CustomAfterMicrosoftCommonCrossTargetingTargets=" + _targetsFile, + "-verbosity:detailed", }; var psi = new ProcessStartInfo { FileName = DotNetMuxer.MuxerPathOrDefault(), Arguments = ArgumentEscaper.EscapeAndConcatenate(args), RedirectStandardOutput = true, - RedirectStandardError = true + RedirectStandardError = true, + UseShellExecute = false, }; #if DEBUG _reporter.Verbose($"Invoking '{psi.FileName} {psi.Arguments}'"); #endif - using var process = Process.Start(psi); + using var process = new Process() + { + StartInfo = psi, + }; + + var outputBuilder = new StringBuilder(); + var errorBuilder = new StringBuilder(); + process.OutputDataReceived += (_, d) => + { + if (!string.IsNullOrEmpty(d.Data)) + { + outputBuilder.AppendLine(d.Data); + } + }; + process.ErrorDataReceived += (_, d) => + { + if (!string.IsNullOrEmpty(d.Data)) + { + errorBuilder.AppendLine(d.Data); + } + }; + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); process.WaitForExit(); if (process.ExitCode != 0) { - _reporter.Verbose(process.StandardOutput.ReadToEnd()); - _reporter.Verbose(process.StandardError.ReadToEnd()); + _reporter.Verbose(outputBuilder.ToString()); + _reporter.Verbose(errorBuilder.ToString()); throw new InvalidOperationException(Resources.FormatError_ProjectFailedToLoad(projectFile)); }