diff --git a/.github/labeler.yml b/.github/labeler.yml index 83509a9fd2..5e44e2f2e8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -51,3 +51,6 @@ area-signalr: - src/SignalR/**/* #area-websockets: + +api-suggestion: + - src/**/ref/**/* diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index dcafb97dc9..4652757b25 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -7,5 +7,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/labeler@v2 + if: github.repository == 'aspnet/AspNetCore' with: repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/NuGet.config b/NuGet.config index 90058d74d5..2aa0b156cb 100644 --- a/NuGet.config +++ b/NuGet.config @@ -2,6 +2,9 @@ + + + diff --git a/THIRD-PARTY-NOTICES.txt b/THIRD-PARTY-NOTICES.txt index 81fadeae22..99d1e37721 100644 --- a/THIRD-PARTY-NOTICES.txt +++ b/THIRD-PARTY-NOTICES.txt @@ -189,3 +189,31 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License notice for corefx + +------------------------------------------------ + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 59a61d4631..d08ca7ebf2 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,408 +9,408 @@ --> - + https://github.com/aspnet/Blazor - adc2dc6547bc542aa687c712e0e3c0e5de3500fe + af9bec917415a00716551608d734ddb1b729eb2b - + https://github.com/aspnet/AspNetCore-Tooling - debe2d2e911020ffee5d6e95eb4dc6d0754bd8ef + 5d3c4074fe2df12afefab2292e4644b4f2686b29 - + https://github.com/aspnet/AspNetCore-Tooling - debe2d2e911020ffee5d6e95eb4dc6d0754bd8ef + 5d3c4074fe2df12afefab2292e4644b4f2686b29 - + https://github.com/aspnet/AspNetCore-Tooling - debe2d2e911020ffee5d6e95eb4dc6d0754bd8ef + 5d3c4074fe2df12afefab2292e4644b4f2686b29 - + https://github.com/aspnet/AspNetCore-Tooling - debe2d2e911020ffee5d6e95eb4dc6d0754bd8ef + 5d3c4074fe2df12afefab2292e4644b4f2686b29 - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/EntityFrameworkCore - 2da52082db1fb33f785822c560f2373e691bab0c + 717c0693868642f1dae589f765d775dbc881cf7d - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + 4ac4c0367003fe3973a3648eb0715ddb0e3bbcea - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/dotnet/core-setup - 4ace84dbf94128b4825c76cdd09b46dba7473478 + a484aa78e137262669266591696fe188ca2b5d63 - + https://github.com/dotnet/core-setup - 4ace84dbf94128b4825c76cdd09b46dba7473478 + a484aa78e137262669266591696fe188ca2b5d63 - + https://github.com/dotnet/core-setup - 4ace84dbf94128b4825c76cdd09b46dba7473478 + a484aa78e137262669266591696fe188ca2b5d63 - + https://github.com/dotnet/core-setup - 4ace84dbf94128b4825c76cdd09b46dba7473478 + a484aa78e137262669266591696fe188ca2b5d63 - + https://github.com/dotnet/corefx - a434a52ae3f1175bc1cad8b1a02b0921ef3b1f55 + b9186cfa3566ee24e5f16e45c542a3078e128dc6 - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e https://github.com/dotnet/arcade @@ -424,9 +424,9 @@ https://github.com/dotnet/arcade f70d1fca3d5d4045be75694006f1bee0e0aec572 - + https://github.com/aspnet/Extensions - 5fb4eb52033b15dea5a4165fb7ce15f6bee83cf0 + f15f3278c73c72a6546d7cb2c1bd54a541ffc83e https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index b010036154..5ae95d3858 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -66,112 +66,112 @@ 3.4.0-beta1-19456-03 - 5.0.0-alpha1.19514.1 - 5.0.0-alpha1.19514.1 - 5.0.0-alpha1.19514.1 - 2.1.0-alpha1.19514.1 + 5.0.0-alpha1.19517.2 + 5.0.0-alpha1.19517.2 + 5.0.0-alpha1.19517.2 + 2.1.0-alpha1.19517.2 - 1.2.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 1.9.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 - 5.0.0-alpha1.19504.7 + 1.0.0 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 + 5.0.0-alpha1.19516.16 - 5.0.0-alpha1.19504.7 + 5.0.0-alpha1.19516.16 - 5.0.0-alpha1.19514.2 + 5.0.0-alpha1.19516.2 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 - 5.0.0-alpha1.19514.8 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-alpha1.19517.7 + 5.0.0-preview2.19517.7 - 5.0.0-alpha1.19515.8 - 5.0.0-alpha1.19515.8 - 5.0.0-alpha1.19515.8 - 5.0.0-alpha1.19515.8 - 5.0.0-alpha1.19515.8 - 5.0.0-alpha1.19515.8 - 5.0.0-alpha1.19515.8 + 5.0.0-alpha1.19518.8 + 5.0.0-alpha1.19518.8 + 5.0.0-alpha1.19518.8 + 5.0.0-alpha1.19518.8 + 5.0.0-alpha1.19518.8 + 5.0.0-alpha1.19518.8 + 5.0.0-alpha1.19518.8 - 5.0.0-alpha1.19513.1 - 5.0.0-alpha1.19513.1 - 5.0.0-alpha1.19513.1 - 5.0.0-alpha1.19513.1 + 5.0.0-alpha1.19517.12 + 5.0.0-alpha1.19517.12 + 5.0.0-alpha1.19517.12 + 5.0.0-alpha1.19517.12 + diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Shared/_Layout.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Shared/_Layout.cshtml index ccc07b30f6..835d806c2e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Shared/_Layout.cshtml +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/RazorPagesWeb-CSharp/Pages/Shared/_Layout.cshtml @@ -50,6 +50,6 @@ - @RenderSection("Scripts", required: false) + @await RenderSectionAsync("Scripts", required: false) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Views/Shared/_Layout.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Views/Shared/_Layout.cshtml index 892d6b9b5d..5234625f8d 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Views/Shared/_Layout.cshtml +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-CSharp/Views/Shared/_Layout.cshtml @@ -48,6 +48,6 @@ - @RenderSection("Scripts", required: false) + @await RenderSectionAsync("Scripts", required: false) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Views/Shared/_Layout.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Views/Shared/_Layout.cshtml index 6dbe6964a7..b326cdff07 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Views/Shared/_Layout.cshtml +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/StarterWeb-FSharp/Views/Shared/_Layout.cshtml @@ -47,6 +47,6 @@ - @RenderSection("Scripts", required: false) + @await RenderSectionAsync("Scripts", required: false) diff --git a/src/Security/CookiePolicy/src/CookiePolicyOptions.cs b/src/Security/CookiePolicy/src/CookiePolicyOptions.cs index 098bd33483..56e5998808 100644 --- a/src/Security/CookiePolicy/src/CookiePolicyOptions.cs +++ b/src/Security/CookiePolicy/src/CookiePolicyOptions.cs @@ -12,22 +12,10 @@ namespace Microsoft.AspNetCore.Builder /// public class CookiePolicyOptions { - // True (old): https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-3.1 - // False (new): https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.1 - internal static bool SuppressSameSiteNone; - - static CookiePolicyOptions() - { - if (AppContext.TryGetSwitch("Microsoft.AspNetCore.SuppressSameSiteNone", out var enabled)) - { - SuppressSameSiteNone = enabled; - } - } - /// /// Affects the cookie's same site attribute. /// - public SameSiteMode MinimumSameSitePolicy { get; set; } = SuppressSameSiteNone ? SameSiteMode.None : SameSiteMode.Unspecified; + public SameSiteMode MinimumSameSitePolicy { get; set; } = SameSiteMode.Unspecified; /// /// Affects whether cookies must be HttpOnly. diff --git a/src/Security/CookiePolicy/src/ResponseCookiesWrapper.cs b/src/Security/CookiePolicy/src/ResponseCookiesWrapper.cs index 2d03c65c3d..2c1b46264e 100644 --- a/src/Security/CookiePolicy/src/ResponseCookiesWrapper.cs +++ b/src/Security/CookiePolicy/src/ResponseCookiesWrapper.cs @@ -115,8 +115,7 @@ namespace Microsoft.AspNetCore.CookiePolicy private bool CheckPolicyRequired() { return !CanTrack - || (CookiePolicyOptions.SuppressSameSiteNone && Options.MinimumSameSitePolicy != SameSiteMode.None) - || (!CookiePolicyOptions.SuppressSameSiteNone && Options.MinimumSameSitePolicy != SameSiteMode.Unspecified) + || Options.MinimumSameSitePolicy != SameSiteMode.Unspecified || Options.HttpOnly != HttpOnlyPolicy.None || Options.Secure != CookieSecurePolicy.None; } diff --git a/src/Servers/HttpSys/ref/Microsoft.AspNetCore.Server.HttpSys.netcoreapp.cs b/src/Servers/HttpSys/ref/Microsoft.AspNetCore.Server.HttpSys.netcoreapp.cs index 31f097495b..65c4e2b728 100644 --- a/src/Servers/HttpSys/ref/Microsoft.AspNetCore.Server.HttpSys.netcoreapp.cs +++ b/src/Servers/HttpSys/ref/Microsoft.AspNetCore.Server.HttpSys.netcoreapp.cs @@ -15,6 +15,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys { internal AuthenticationManager() { } public bool AllowAnonymous { get { throw null; } set { } } + public bool AutomaticAuthentication { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes Schemes { get { throw null; } set { } } } [System.FlagsAttribute] diff --git a/src/Servers/HttpSys/src/AuthenticationManager.cs b/src/Servers/HttpSys/src/AuthenticationManager.cs index 29f5a3495a..9b54f0ab0f 100644 --- a/src/Servers/HttpSys/src/AuthenticationManager.cs +++ b/src/Servers/HttpSys/src/AuthenticationManager.cs @@ -45,12 +45,22 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } + /// + /// Indicates if anonymous requests will be surfaced to the application or challenged by the server. + /// The default value is true. + /// public bool AllowAnonymous { get { return _allowAnonymous; } set { _allowAnonymous = value; } } + /// + /// If true the server should set HttpContext.User. If false the server will only provide an + /// identity when explicitly requested by the AuthenticationScheme. The default is true. + /// + public bool AutomaticAuthentication { get; set; } = true; + internal void SetUrlGroupSecurity(UrlGroup urlGroup) { Debug.Assert(_urlGroup == null, "SetUrlGroupSecurity called more than once."); diff --git a/src/Servers/HttpSys/src/FeatureContext.cs b/src/Servers/HttpSys/src/FeatureContext.cs index 32014853e7..482db9f5b2 100644 --- a/src/Servers/HttpSys/src/FeatureContext.cs +++ b/src/Servers/HttpSys/src/FeatureContext.cs @@ -85,7 +85,11 @@ namespace Microsoft.AspNetCore.Server.HttpSys _query = Request.QueryString; _rawTarget = Request.RawUrl; _scheme = Request.Scheme; - _user = _requestContext.User; + + if (requestContext.Server.Options.Authentication.AutomaticAuthentication) + { + _user = _requestContext.User; + } _responseStream = new ResponseStream(requestContext.Response.Body, OnResponseStart); _responseHeaders = Response.Headers; diff --git a/src/Servers/HttpSys/test/FunctionalTests/AuthenticationTests.cs b/src/Servers/HttpSys/test/FunctionalTests/AuthenticationTests.cs index be66db9655..2886b2d8d1 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/AuthenticationTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/AuthenticationTests.cs @@ -368,6 +368,38 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } + [ConditionalTheory] + [InlineData(AuthenticationSchemes.Negotiate)] + [InlineData(AuthenticationSchemes.NTLM)] + // [InlineData(AuthenticationSchemes.Digest)] // TODO: Not implemented + // [InlineData(AuthenticationSchemes.Basic)] // Doesn't work with default creds + [InlineData(AuthenticationSchemes.Negotiate | AuthenticationSchemes.NTLM | /* AuthenticationSchemes.Digest |*/ AuthenticationSchemes.Basic)] + public async Task AuthTypes_DisableAutomaticAuthentication(AuthenticationSchemes authType) + { + using (var server = Utilities.CreateDynamicHost(out var address, options => + { + options.Authentication.AutomaticAuthentication = false; + options.Authentication.Schemes = authType; + options.Authentication.AllowAnonymous = DenyAnoymous; + }, + async httpContext => + { + Assert.NotNull(httpContext.User); + Assert.NotNull(httpContext.User.Identity); + Assert.False(httpContext.User.Identity.IsAuthenticated); + + var authenticateResult = await httpContext.AuthenticateAsync(HttpSysDefaults.AuthenticationScheme); + + Assert.NotNull(authenticateResult.Principal); + Assert.NotNull(authenticateResult.Principal.Identity); + Assert.True(authenticateResult.Principal.Identity.IsAuthenticated); + })) + { + var response = await SendRequestAsync(address, useDefaultCredentials: true); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + } + private async Task SendRequestAsync(string uri, bool useDefaultCredentials = false) { HttpClientHandler handler = new HttpClientHandler(); diff --git a/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs b/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs index 1816fdcc28..04228c6da6 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/ServerTests.cs @@ -345,10 +345,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode); } } - - // A connection has been closed, try again. - string responseText = await SendRequestAsync(address); - Assert.Equal(string.Empty, responseText); } } } @@ -366,31 +362,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys } } - [ConditionalFact] - public async Task Server_SetConnectionLimit_Success() - { - using (Utilities.CreateDynamicHost(out var address, options => - { - Assert.Null(options.MaxConnections); - options.MaxConnections = 3; - }, httpContext => Task.FromResult(0))) - { - using (var client1 = await SendHungRequestAsync("GET", address)) - using (var client2 = await SendHungRequestAsync("GET", address)) - { - using (var client3 = await SendHungRequestAsync("GET", address)) - { - // Maxed out, refuses connection and throws - await Assert.ThrowsAsync(() => SendRequestAsync(address)); - } - - // A connection has been closed, try again. - string responseText = await SendRequestAsync(address); - Assert.Equal(string.Empty, responseText); - } - } - } - [ConditionalFact] public async Task Server_SetConnectionLimitChangeAfterStarted_Success() { diff --git a/src/Servers/IIS/IIS/src/Core/DuplexStream.cs b/src/Servers/IIS/IIS/src/Core/DuplexStream.cs index 8ff01c778f..73dbd4cbb9 100644 --- a/src/Servers/IIS/IIS/src/Core/DuplexStream.cs +++ b/src/Servers/IIS/IIS/src/Core/DuplexStream.cs @@ -60,9 +60,29 @@ namespace Microsoft.AspNetCore.Server.IIS.Core return _requestBody.ReadAsync(buffer, offset, count, cancellationToken); } + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return _requestBody.ReadAsync(buffer, cancellationToken); + } + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { return _responseBody.WriteAsync(buffer, offset, count, cancellationToken); } + + public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + return _responseBody.WriteAsync(buffer, cancellationToken); + } + + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + return _requestBody.CopyToAsync(destination, bufferSize, cancellationToken); + } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + return _responseBody.FlushAsync(cancellationToken); + } } } diff --git a/src/Servers/IIS/IIS/src/Core/HttpRequestStream.cs b/src/Servers/IIS/IIS/src/Core/HttpRequestStream.cs index 9fa5d7405e..8d90fd69a5 100644 --- a/src/Servers/IIS/IIS/src/Core/HttpRequestStream.cs +++ b/src/Servers/IIS/IIS/src/Core/HttpRequestStream.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; @@ -35,40 +36,12 @@ namespace Microsoft.AspNetCore.Server.IIS.Core public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = ReadAsync(buffer, offset, count, default(CancellationToken), state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); } public override int EndRead(IAsyncResult asyncResult) { - return ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = ReadAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(task2.Result); - } - }, tcs, cancellationToken); - return tcs.Task; + return TaskToApm.End(asyncResult); } public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -97,6 +70,18 @@ namespace Microsoft.AspNetCore.Server.IIS.Core } } + public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) + { + try + { + await _body.CopyToAsync(destination, cancellationToken); + } + catch (ConnectionAbortedException ex) + { + throw new TaskCanceledException("The request was aborted", ex); + } + } + public void StartAcceptingReads(IISHttpContext body) { // Only start if not aborted diff --git a/src/Servers/IIS/IIS/src/Core/HttpResponseStream.cs b/src/Servers/IIS/IIS/src/Core/HttpResponseStream.cs index 739b6b16f5..b1d0c1f22a 100644 --- a/src/Servers/IIS/IIS/src/Core/HttpResponseStream.cs +++ b/src/Servers/IIS/IIS/src/Core/HttpResponseStream.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core public override void Flush() { - FlushAsync(default(CancellationToken)).GetAwaiter().GetResult(); + FlushAsync(default).GetAwaiter().GetResult(); } public override Task FlushAsync(CancellationToken cancellationToken) @@ -41,45 +41,17 @@ namespace Microsoft.AspNetCore.Server.IIS.Core throw new InvalidOperationException(CoreStrings.SynchronousWritesDisallowed); } - WriteAsync(buffer, offset, count, default(CancellationToken)).GetAwaiter().GetResult(); + WriteAsync(buffer, offset, count, default).GetAwaiter().GetResult(); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = WriteAsync(buffer, offset, count, default(CancellationToken), state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); } public override void EndWrite(IAsyncResult asyncResult) { - ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = WriteAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(null); - } - }, tcs, cancellationToken); - return tcs.Task; + TaskToApm.End(asyncResult); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) diff --git a/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs b/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs index 68141058c5..5f831eb01a 100644 --- a/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs +++ b/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs @@ -3,11 +3,10 @@ using System; using System.Buffers; -using System.Net.Http; +using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Server.IIS.Core @@ -54,6 +53,16 @@ namespace Microsoft.AspNetCore.Server.IIS.Core } } + internal Task CopyToAsync(Stream destination, CancellationToken cancellationToken) + { + if (!HasStartedConsumingRequestBody) + { + InitializeRequestIO(); + } + + return _bodyInputPipe.Reader.CopyToAsync(destination, cancellationToken); + } + /// /// Writes data to the output pipe. /// diff --git a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj index 06fe1aa203..a95e4ec56a 100644 --- a/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj +++ b/src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs index d041705a19..e1175fcde9 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs @@ -27,6 +27,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http get { return _contentLength; } set { + if (_isReadOnly) + { + ThrowHeadersReadOnlyException(); + } if (value.HasValue && value.Value < 0) { ThrowInvalidContentLengthException(value.Value); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs index 4d5513293e..a1b30fb940 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestStream.cs @@ -90,41 +90,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = ReadAsync(buffer, offset, count, default, state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); } /// public override int EndRead(IAsyncResult asyncResult) { - return ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = ReadAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(task2.Result); - } - }, tcs, cancellationToken); - return tcs.Task; + return TaskToApm.End(asyncResult); } private ValueTask ReadAsyncWrapper(Memory destination, CancellationToken cancellationToken) @@ -139,7 +111,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } } - private async ValueTask ReadAsyncInternal(Memory buffer, CancellationToken cancellationToken) + private async ValueTask ReadAsyncInternal(Memory destination, CancellationToken cancellationToken) { while (true) { @@ -150,19 +122,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http throw new OperationCanceledException("The read was canceled"); } - var readableBuffer = result.Buffer; - var readableBufferLength = readableBuffer.Length; + var buffer = result.Buffer; + var length = buffer.Length; - var consumed = readableBuffer.End; + var consumed = buffer.End; try { - if (readableBufferLength != 0) + if (length != 0) { - var actual = (int)Math.Min(readableBufferLength, buffer.Length); + var actual = (int)Math.Min(length, destination.Length); - var slice = actual == readableBufferLength ? readableBuffer : readableBuffer.Slice(0, actual); + var slice = actual == length ? buffer : buffer.Slice(0, actual); consumed = slice.End; - slice.CopyTo(buffer.Span); + slice.CopyTo(destination.Span); return actual; } @@ -193,37 +165,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http throw new ArgumentOutOfRangeException(nameof(bufferSize)); } - return CopyToAsyncInternal(destination, cancellationToken); - } - - private async Task CopyToAsyncInternal(Stream destination, CancellationToken cancellationToken) - { - while (true) - { - var result = await _pipeReader.ReadAsync(cancellationToken); - var readableBuffer = result.Buffer; - var readableBufferLength = readableBuffer.Length; - - try - { - if (readableBufferLength != 0) - { - foreach (var memory in readableBuffer) - { - await destination.WriteAsync(memory, cancellationToken); - } - } - - if (result.IsCompleted) - { - return; - } - } - finally - { - _pipeReader.AdvanceTo(readableBuffer.End); - } - } + return _pipeReader.CopyToAsync(destination, cancellationToken); } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs index 2bf5017278..86ddb9b157 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseStream.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Features; @@ -87,40 +86,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = WriteAsync(buffer, offset, count, default, state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); } public override void EndWrite(IAsyncResult asyncResult) { - ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = WriteAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(null); - } - }, tcs, cancellationToken); - return tcs.Task; + TaskToApm.End(asyncResult); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) diff --git a/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj b/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj index ea3a87bad7..058df2190c 100644 --- a/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj +++ b/src/Servers/Kestrel/Core/src/Microsoft.AspNetCore.Server.Kestrel.Core.csproj @@ -15,6 +15,7 @@ + diff --git a/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStream.cs b/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStream.cs index b81a5e8869..7a2dfdf362 100644 --- a/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStream.cs +++ b/src/Servers/Kestrel/Core/src/Middleware/Internal/DuplexPipeStream.cs @@ -152,78 +152,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = ReadAsync(buffer, offset, count, default, state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); } public override int EndRead(IAsyncResult asyncResult) { - return ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = ReadAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(task2.Result); - } - }, tcs, cancellationToken); - return tcs.Task; + return TaskToApm.End(asyncResult); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = WriteAsync(buffer, offset, count, default, state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); } public override void EndWrite(IAsyncResult asyncResult) { - ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = WriteAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(null); - } - }, tcs, cancellationToken); - return tcs.Task; + TaskToApm.End(asyncResult); } } } diff --git a/src/Servers/Kestrel/Core/src/Middleware/Internal/LoggingStream.cs b/src/Servers/Kestrel/Core/src/Middleware/Internal/LoggingStream.cs index e3fdec3f81..15758f64f4 100644 --- a/src/Servers/Kestrel/Core/src/Middleware/Internal/LoggingStream.cs +++ b/src/Servers/Kestrel/Core/src/Middleware/Internal/LoggingStream.cs @@ -176,78 +176,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal // The below APM methods call the underlying Read/WriteAsync methods which will still be logged. public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = ReadAsync(buffer, offset, count, default(CancellationToken), state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(ReadAsync(buffer, offset, count), callback, state); } public override int EndRead(IAsyncResult asyncResult) { - return ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = ReadAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(task2.Result); - } - }, tcs, cancellationToken); - return tcs.Task; + return TaskToApm.End(asyncResult); } public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) { - var task = WriteAsync(buffer, offset, count, default(CancellationToken), state); - if (callback != null) - { - task.ContinueWith(t => callback.Invoke(t)); - } - return task; + return TaskToApm.Begin(WriteAsync(buffer, offset, count), callback, state); } public override void EndWrite(IAsyncResult asyncResult) { - ((Task)asyncResult).GetAwaiter().GetResult(); - } - - private Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken, object state) - { - var tcs = new TaskCompletionSource(state); - var task = WriteAsync(buffer, offset, count, cancellationToken); - task.ContinueWith((task2, state2) => - { - var tcs2 = (TaskCompletionSource)state2; - if (task2.IsCanceled) - { - tcs2.SetCanceled(); - } - else if (task2.IsFaulted) - { - tcs2.SetException(task2.Exception); - } - else - { - tcs2.SetResult(null); - } - }, tcs, cancellationToken); - return tcs.Task; + TaskToApm.End(asyncResult); } } } diff --git a/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs b/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs index ed3c492e3c..1bbe06e99c 100644 --- a/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs +++ b/src/Servers/Kestrel/Core/test/HttpResponseHeadersTests.cs @@ -138,6 +138,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests Assert.Throws(() => ((IDictionary)headers).Add("my-header", new[] { "value" })); } + [Fact] + public void ThrowsWhenSettingContentLengthPropertyAfterReadOnlyIsSet() + { + var headers = new HttpResponseHeaders(); + headers.SetReadOnly(); + + Assert.Throws(() => headers.ContentLength = null); + } + [Fact] public void ThrowsWhenChangingHeaderAfterReadOnlyIsSet() { diff --git a/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp.cs b/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp.cs index f24337e79d..e6fa605b47 100644 --- a/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp.cs +++ b/src/Servers/Kestrel/Transport.Libuv/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.netcoreapp.cs @@ -14,6 +14,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv public partial class LibuvTransportOptions { public LibuvTransportOptions() { } + public int Backlog { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public long? MaxReadBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public long? MaxWriteBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public bool NoDelay { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConstants.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConstants.cs index 0e07f1a69c..1e5b7bb75b 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConstants.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/LibuvConstants.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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.Runtime.CompilerServices; @@ -8,8 +8,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal { internal static class LibuvConstants { - public const int ListenBacklog = 128; - public const int EOF = -4095; public static readonly int? ECONNRESET = GetECONNRESET(); public static readonly int? EADDRINUSE = GetEADDRINUSE(); diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs index 9dc11a31a4..6279764101 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/Listener.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal return Thread.PostAsync(listener => { listener.ListenSocket = listener.CreateListenSocket(); - listener.ListenSocket.Listen(LibuvConstants.ListenBacklog, ConnectionCallback, listener); + listener.ListenSocket.Listen(TransportContext.Options.Backlog, ConnectionCallback, listener); }, this); } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs index acbc356294..add394b5e3 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/Internal/ListenerPrimary.cs @@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal ListenPipe = new UvPipeHandle(Log); ListenPipe.Init(Thread.Loop, Thread.QueueCloseHandle, false); ListenPipe.Bind(_pipeName); - ListenPipe.Listen(LibuvConstants.ListenBacklog, + ListenPipe.Listen(TransportContext.Options.Backlog, (pipe, status, error, state) => ((ListenerPrimary)state).OnListenPipe(pipe, status, error), this); } diff --git a/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs b/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs index 06c8aec796..0aa477f3af 100644 --- a/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs +++ b/src/Servers/Kestrel/Transport.Libuv/src/LibuvTransportOptions.cs @@ -27,6 +27,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv /// public bool NoDelay { get; set; } = true; + /// + /// The maximum length of the pending connection queue. + /// + /// + /// Defaults to 128. + /// + public int Backlog { get; set; } = 128; + public long? MaxReadBufferSize { get; set; } = 1024 * 1024; public long? MaxWriteBufferSize { get; set; } = 64 * 1024; diff --git a/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp.cs b/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp.cs index c1e32cc39d..e54b0a2d21 100644 --- a/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp.cs +++ b/src/Servers/Kestrel/Transport.Sockets/ref/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.netcoreapp.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets public partial class SocketTransportOptions { public SocketTransportOptions() { } + public int Backlog { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public int IOQueueCount { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public long? MaxReadBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public long? MaxWriteBufferSize { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs index 600d674d98..f829ae5591 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketConnectionListener.cs @@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets EndPoint = listenSocket.LocalEndPoint; - listenSocket.Listen(512); + listenSocket.Listen(_options.Backlog); _listenSocket = listenSocket; } diff --git a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs index 4adb16ebfb..424a4375ae 100644 --- a/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs +++ b/src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs @@ -24,6 +24,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets /// public bool NoDelay { get; set; } = true; + /// + /// The maximum length of the pending connection queue. + /// + /// + /// Defaults to 512. + /// + public int Backlog { get; set; } = 512; + public long? MaxReadBufferSize { get; set; } = 1024 * 1024; public long? MaxWriteBufferSize { get; set; } = 64 * 1024; diff --git a/src/Shared/TaskToApm.cs b/src/Shared/TaskToApm.cs new file mode 100644 index 0000000000..3a05eb6b42 --- /dev/null +++ b/src/Shared/TaskToApm.cs @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// Helper methods for using Tasks to implement the APM pattern. +// +// Example usage, wrapping a Task-returning FooAsync method with Begin/EndFoo methods: +// +// public IAsyncResult BeginFoo(..., AsyncCallback callback, object state) => +// TaskToApm.Begin(FooAsync(...), callback, state); +// +// public int EndFoo(IAsyncResult asyncResult) => +// TaskToApm.End(asyncResult); + +#nullable enable +using System.Diagnostics; + +namespace System.Threading.Tasks +{ + /// + /// Provides support for efficiently using Tasks to implement the APM (Begin/End) pattern. + /// + internal static class TaskToApm + { + /// + /// Marshals the Task as an IAsyncResult, using the supplied callback and state + /// to implement the APM pattern. + /// + /// The Task to be marshaled. + /// The callback to be invoked upon completion. + /// The state to be stored in the IAsyncResult. + /// An IAsyncResult to represent the task's asynchronous operation. + public static IAsyncResult Begin(Task task, AsyncCallback? callback, object? state) => + new TaskAsyncResult(task, state, callback); + + /// Processes an IAsyncResult returned by Begin. + /// The IAsyncResult to unwrap. + public static void End(IAsyncResult asyncResult) + { + if (asyncResult is TaskAsyncResult twar) + { + twar._task.GetAwaiter().GetResult(); + return; + } + + throw new ArgumentNullException(); + } + + /// Processes an IAsyncResult returned by Begin. + /// The IAsyncResult to unwrap. + public static TResult End(IAsyncResult asyncResult) + { + if (asyncResult is TaskAsyncResult twar && twar._task is Task task) + { + return task.GetAwaiter().GetResult(); + } + + throw new ArgumentNullException(); + } + + /// Provides a simple IAsyncResult that wraps a Task. + /// + /// We could use the Task as the IAsyncResult if the Task's AsyncState is the same as the object state, + /// but that's very rare, in particular in a situation where someone cares about allocation, and always + /// using TaskAsyncResult simplifies things and enables additional optimizations. + /// + internal sealed class TaskAsyncResult : IAsyncResult + { + /// The wrapped Task. + internal readonly Task _task; + /// Callback to invoke when the wrapped task completes. + private readonly AsyncCallback? _callback; + + /// Initializes the IAsyncResult with the Task to wrap and the associated object state. + /// The Task to wrap. + /// The new AsyncState value. + /// Callback to invoke when the wrapped task completes. + internal TaskAsyncResult(Task task, object? state, AsyncCallback? callback) + { + Debug.Assert(task != null); + _task = task; + AsyncState = state; + + if (task.IsCompleted) + { + // Synchronous completion. Invoke the callback. No need to store it. + CompletedSynchronously = true; + callback?.Invoke(this); + } + else if (callback != null) + { + // Asynchronous completion, and we have a callback; schedule it. We use OnCompleted rather than ContinueWith in + // order to avoid running synchronously if the task has already completed by the time we get here but still run + // synchronously as part of the task's completion if the task completes after (the more common case). + _callback = callback; + _task.ConfigureAwait(continueOnCapturedContext: false) + .GetAwaiter() + .OnCompleted(InvokeCallback); // allocates a delegate, but avoids a closure + } + } + + /// Invokes the callback. + private void InvokeCallback() + { + Debug.Assert(!CompletedSynchronously); + Debug.Assert(_callback != null); + _callback.Invoke(this); + } + + /// Gets a user-defined object that qualifies or contains information about an asynchronous operation. + public object? AsyncState { get; } + /// Gets a value that indicates whether the asynchronous operation completed synchronously. + /// This is set lazily based on whether the has completed by the time this object is created. + public bool CompletedSynchronously { get; } + /// Gets a value that indicates whether the asynchronous operation has completed. + public bool IsCompleted => _task.IsCompleted; + /// Gets a that is used to wait for an asynchronous operation to complete. + public WaitHandle AsyncWaitHandle => ((IAsyncResult)_task).AsyncWaitHandle; + } + } +} \ No newline at end of file diff --git a/src/SignalR/clients/ts/signalr/src/HttpConnection.ts b/src/SignalR/clients/ts/signalr/src/HttpConnection.ts index 3359f253a9..27bb40b786 100644 --- a/src/SignalR/clients/ts/signalr/src/HttpConnection.ts +++ b/src/SignalR/clients/ts/signalr/src/HttpConnection.ts @@ -322,7 +322,7 @@ export class HttpConnection implements IConnection { }); if (response.statusCode !== 200) { - return Promise.reject(new Error(`Unexpected status code returned from negotiate ${response.statusCode}`)); + return Promise.reject(new Error(`Unexpected status code returned from negotiate '${response.statusCode}'`)); } const negotiateResponse = JSON.parse(response.content as string) as INegotiateResponse; @@ -475,8 +475,8 @@ export class HttpConnection implements IConnection { } if (this.connectionState === ConnectionState.Connecting) { - this.logger.log(LogLevel.Warning, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection hasn't yet left the in the connecting state.`); - return; + this.logger.log(LogLevel.Warning, `Call to HttpConnection.stopConnection(${error}) was ignored because the connection is still in the connecting state.`); + throw new Error(`HttpConnection.stopConnection(${error}) was called while the connection is still in the connecting state.`); } if (this.connectionState === ConnectionState.Disconnecting) { @@ -626,7 +626,7 @@ export class TransportSendQueue { offset += item.byteLength; } - return result; + return result.buffer; } } diff --git a/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts b/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts index cee7eafdc5..d2b65d2039 100644 --- a/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts +++ b/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts @@ -98,7 +98,12 @@ export class WebSocketTransport implements ITransport { webSocket.onmessage = (message: MessageEvent) => { this.logger.log(LogLevel.Trace, `(WebSockets transport) data received. ${getDataDetail(message.data, this.logMessageContent)}.`); if (this.onreceive) { - this.onreceive(message.data); + try { + this.onreceive(message.data); + } catch (error) { + this.close(error); + return; + } } }; @@ -149,15 +154,21 @@ export class WebSocketTransport implements ITransport { return Promise.resolve(); } - private close(event?: CloseEvent): void { + private close(event?: CloseEvent | Error): void { // webSocket will be null if the transport did not start successfully this.logger.log(LogLevel.Trace, "(WebSockets transport) socket closed."); if (this.onclose) { - if (event && (event.wasClean === false || event.code !== 1000)) { + if (this.isCloseEvent(event) && (event.wasClean === false || event.code !== 1000)) { this.onclose(new Error(`WebSocket closed with status code: ${event.code} (${event.reason}).`)); + } else if (event instanceof Error) { + this.onclose(event); } else { this.onclose(); } } } + + private isCloseEvent(event?: any): event is CloseEvent { + return event && typeof event.wasClean === "boolean" && typeof event.code === "number"; + } } diff --git a/src/SignalR/clients/ts/signalr/tests/HttpConnection.test.ts b/src/SignalR/clients/ts/signalr/tests/HttpConnection.test.ts index 6ed2a6e78c..3e0b6a81eb 100644 --- a/src/SignalR/clients/ts/signalr/tests/HttpConnection.test.ts +++ b/src/SignalR/clients/ts/signalr/tests/HttpConnection.test.ts @@ -8,6 +8,7 @@ import { HttpTransportType, ITransport, TransferFormat } from "../src/ITransport import { getUserAgentHeader } from "../src/Utils"; import { HttpError } from "../src/Errors"; +import { ILogger, LogLevel } from "../src/ILogger"; import { NullLogger } from "../src/Loggers"; import { EventSourceConstructor, WebSocketConstructor } from "../src/Polyfills"; @@ -192,9 +193,9 @@ describe("HttpConnection", () => { const connection = new HttpConnection("http://tempuri.org", options); await expect(connection.start(TransferFormat.Text)) .rejects - .toThrow("Unexpected status code returned from negotiate 999"); + .toThrow("Unexpected status code returned from negotiate '999'"); }, - "Failed to start the connection: Error: Unexpected status code returned from negotiate 999"); + "Failed to start the connection: Error: Unexpected status code returned from negotiate '999'"); }); it("all transport failure errors get aggregated", async () => { @@ -1151,6 +1152,53 @@ describe("HttpConnection", () => { }, "Failed to start the connection: Error: nope"); }); + it("logMessageContent displays correctly with binary data", async () => { + await VerifyLogger.run(async (logger) => { + const availableTransport = { transport: "LongPolling", transferFormats: ["Text", "Binary"] }; + + let sentMessage = ""; + const captureLogger: ILogger = { + log: (logLevel: LogLevel, message: string) => { + if (logLevel === LogLevel.Trace && message.search("data of length") > 0) { + sentMessage = message; + } + + logger.log(logLevel, message); + }, + }; + + let httpClientGetCount = 0; + const options: IHttpConnectionOptions = { + ...commonOptions, + httpClient: new TestHttpClient() + .on("POST", () => ({ connectionId: "42", availableTransports: [availableTransport] })) + .on("GET", () => { + httpClientGetCount++; + if (httpClientGetCount === 1) { + // First long polling request must succeed so start completes + return ""; + } + return Promise.resolve(); + }) + .on("DELETE", () => new HttpResponse(202)), + logMessageContent: true, + logger: captureLogger, + transport: HttpTransportType.LongPolling, + } as IHttpConnectionOptions; + + const connection = new HttpConnection("http://tempuri.org", options); + connection.onreceive = () => null; + try { + await connection.start(TransferFormat.Binary); + await connection.send(new Uint8Array([0x68, 0x69, 0x20, 0x3a, 0x29])); + } finally { + await connection.stop(); + } + + expect(sentMessage).toBe("(LongPolling transport) sending data. Binary data of length 5. Content: '0x68 0x69 0x20 0x3a 0x29'."); + }); + }); + describe(".constructor", () => { it("throws if no Url is provided", async () => { // Force TypeScript to let us call the constructor incorrectly :) @@ -1413,7 +1461,7 @@ describe("TransportSendQueue", () => { const queue = new TransportSendQueue(transport); - const first = queue.send(new Uint8Array([4, 5, 6])); + const first = queue.send(new Uint8Array([4, 5, 6]).buffer); // This should allow first to enter transport.send promiseSource1.resolve(); // Wait until we're inside transport.send @@ -1428,8 +1476,8 @@ describe("TransportSendQueue", () => { await Promise.all([first, second, third]); expect(sendMock.mock.calls.length).toBe(2); - expect(sendMock.mock.calls[0][0]).toEqual(new Uint8Array([4, 5, 6])); - expect(sendMock.mock.calls[1][0]).toEqual(new Uint8Array([7, 8, 10, 12, 14])); + expect(sendMock.mock.calls[0][0]).toEqual(new Uint8Array([4, 5, 6]).buffer); + expect(sendMock.mock.calls[1][0]).toEqual(new Uint8Array([7, 8, 10, 12, 14]).buffer); await queue.stop(); }); diff --git a/src/SignalR/clients/ts/signalr/tests/WebSocketTransport.test.ts b/src/SignalR/clients/ts/signalr/tests/WebSocketTransport.test.ts index 3126258ee1..cb4455a517 100644 --- a/src/SignalR/clients/ts/signalr/tests/WebSocketTransport.test.ts +++ b/src/SignalR/clients/ts/signalr/tests/WebSocketTransport.test.ts @@ -256,6 +256,66 @@ describe("WebSocketTransport", () => { .toBe("WebSocket is not in the OPEN state"); }); }); + + it("is closed from 'onreceive' callback throwing", async () => { + await VerifyLogger.run(async (logger) => { + (global as any).ErrorEvent = TestEvent; + const webSocket = await createAndStartWebSocket(logger); + + let closeCalled: boolean = false; + let error: Error; + webSocket.onclose = (e) => { + closeCalled = true; + error = e!; + }; + + const receiveError = new Error("callback error"); + webSocket.onreceive = (data) => { + throw receiveError; + }; + + const message = new TestMessageEvent(); + message.data = "receive data"; + TestWebSocket.webSocket.onmessage(message); + + expect(closeCalled).toBe(true); + expect(error!).toBe(receiveError); + + await expect(webSocket.send("")) + .rejects + .toBe("WebSocket is not in the OPEN state"); + }); + }); + + it("does not run onclose callback if Transport does not fully connect and exits", async () => { + await VerifyLogger.run(async (logger) => { + (global as any).ErrorEvent = TestErrorEvent; + const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket); + + const connectPromise = webSocket.connect("http://example.com", TransferFormat.Text); + + await TestWebSocket.webSocket.closeSet; + + let closeCalled: boolean = false; + let error: Error; + webSocket.onclose = (e) => { + closeCalled = true; + error = e!; + }; + + const message = new TestCloseEvent(); + message.wasClean = false; + message.code = 1; + message.reason = "just cause"; + TestWebSocket.webSocket.onclose(message); + + expect(closeCalled).toBe(false); + expect(error!).toBeUndefined(); + + TestWebSocket.webSocket.onerror(new TestEvent()); + await expect(connectPromise).rejects.toThrow("There was an error with the transport."); + }); + }); }); async function createAndStartWebSocket(logger: ILogger, url?: string, accessTokenFactory?: (() => string | Promise), format?: TransferFormat): Promise { diff --git a/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp.cs b/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp.cs index 5309dae32c..f266b11a88 100644 --- a/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp.cs +++ b/src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp.cs @@ -176,9 +176,11 @@ namespace Microsoft.AspNetCore.SignalR public partial class HubInvocationContext { public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, string hubMethodName, object[] hubMethodArguments) { } + public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, System.Type hubType, string hubMethodName, object[] hubMethodArguments) { } public Microsoft.AspNetCore.SignalR.HubCallerContext Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public System.Collections.Generic.IReadOnlyList HubMethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public string HubMethodName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public System.Type HubType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } public abstract partial class HubLifetimeManager where THub : Microsoft.AspNetCore.SignalR.Hub { diff --git a/src/SignalR/server/Core/src/HubInvocationContext.cs b/src/SignalR/server/Core/src/HubInvocationContext.cs index a62706d6a8..967db6ebb5 100644 --- a/src/SignalR/server/Core/src/HubInvocationContext.cs +++ b/src/SignalR/server/Core/src/HubInvocationContext.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; @@ -11,6 +12,18 @@ namespace Microsoft.AspNetCore.SignalR /// public class HubInvocationContext { + /// + /// Instantiates a new instance of the class. + /// + /// Context for the active Hub connection and caller. + /// The type of the Hub. + /// The name of the Hub method being invoked. + /// The arguments provided by the client. + public HubInvocationContext(HubCallerContext context, Type hubType, string hubMethodName, object[] hubMethodArguments): this(context, hubMethodName, hubMethodArguments) + { + HubType = hubType; + } + /// /// Instantiates a new instance of the class. /// @@ -29,6 +42,11 @@ namespace Microsoft.AspNetCore.SignalR /// public HubCallerContext Context { get; } + /// + /// Gets the Hub type. + /// + public Type HubType { get; } + /// /// Gets the name of the Hub method being invoked. /// diff --git a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs index bc0f8c9fc5..8d54b6d0ae 100644 --- a/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs +++ b/src/SignalR/server/Core/src/Internal/DefaultHubDispatcher.cs @@ -495,7 +495,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal return TaskCache.True; } - return IsHubMethodAuthorizedSlow(provider, hubConnectionContext.User, policies, new HubInvocationContext(hubConnectionContext.HubCallerContext, hubMethodName, hubMethodArguments)); + return IsHubMethodAuthorizedSlow(provider, hubConnectionContext.User, policies, new HubInvocationContext(hubConnectionContext.HubCallerContext, typeof(THub), hubMethodName, hubMethodArguments)); } private static async Task IsHubMethodAuthorizedSlow(IServiceProvider provider, ClaimsPrincipal principal, IList policies, HubInvocationContext resource) diff --git a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs index c88f593db2..5c6f9d9def 100644 --- a/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs +++ b/src/SignalR/server/SignalR/test/HubConnectionHandlerTests.cs @@ -2215,6 +2215,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests { Assert.NotNull(context.Resource); var resource = Assert.IsType(context.Resource); + Assert.Equal(typeof(MethodHub), resource.HubType); Assert.Equal(nameof(MethodHub.MultiParamAuthMethod), resource.HubMethodName); Assert.Equal(2, resource.HubMethodArguments?.Count); Assert.Equal("Hello", resource.HubMethodArguments[0]);