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