diff --git a/NuGet.config b/NuGet.config index 64edb3888279..cd8902cfb5ec 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,10 +4,10 @@ - + - + @@ -30,10 +30,10 @@ - + - + diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props index f29ce0f83682..4ff4b2c148c6 100644 --- a/eng/Baseline.Designer.props +++ b/eng/Baseline.Designer.props @@ -2,117 +2,117 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - - + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 @@ -120,279 +120,279 @@ - 9.0.5 + 9.0.8 - - - + + + - - - + + + - - - + + + - 9.0.5 + 9.0.8 - - - + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - - - - - + + + + + - 9.0.5 + 9.0.8 - - - - - + + + + + - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - - - - - + + + + + + - 9.0.5 + 9.0.8 - - - + + + - 9.0.5 + 9.0.8 - - - + + + - + - - - + + + - - - + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - + - + - 9.0.5 + 9.0.8 - - - - - - + + + + + + - + - - - - - - - + + + + + + + - - - - - - + + + + + + - + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - - + + - - + + - - + + - 9.0.5 + 9.0.8 - + - + - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 @@ -401,83 +401,83 @@ - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - - - + + + - + - - - - + + + + - - - - + + + + - - - - + + + + - 9.0.5 + 9.0.8 - - + + - + - - + + - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 @@ -493,510 +493,510 @@ - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - - - + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - + + - - + + - - + + - 9.0.5 + 9.0.8 - - - - - - + + + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - 9.0.5 + 9.0.8 - - + + - + - - + + - - - + + + - 9.0.5 + 9.0.8 - + - + - + - 9.0.5 + 9.0.8 - + - + - + - 9.0.5 + 9.0.8 - + - + - + - 9.0.5 + 9.0.8 - - - - + + + + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - + + - - + + - - + + - 9.0.5 + 9.0.8 - - - + + + - - - + + + - - - + + + - - - + + + - 9.0.5 + 9.0.8 - - + + - - + + - - + + - 9.0.5 + 9.0.8 - - - - - + + + + + - - - - + + + + - - - - - + + + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - - + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - + - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - - - + + + - - - + + + - - - + + + - 9.0.5 + 9.0.8 - - - + + + - - - + + + - - - + + + - 9.0.5 + 9.0.8 - - - - + + + + - - - - + + + + - - - - + + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - - - + + + - - + + - - - + + + - 9.0.5 + 9.0.8 - 9.0.5 + 9.0.8 - + - 9.0.5 + 9.0.8 - + \ No newline at end of file diff --git a/eng/Baseline.xml b/eng/Baseline.xml index 689d369cce19..71f542dc6973 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -4,110 +4,110 @@ This file contains a list of all the packages and their versions which were rele Update this list when preparing for a new patch. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 8e86375464ef..2c8d20d3234b 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -46,6 +46,13 @@ + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 533640d10c8b..3ebbb1853f12 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,325 +9,325 @@ --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 8751e6d519fda94d5154187358765311ed4a4e84 + 78871c83aac6c38eb5476c2f34aae98ef65314f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 https://github.com/dotnet/xdt @@ -367,9 +367,9 @@ bc1c3011064a493b0ca527df6fb7215e2e5cfa96 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 @@ -380,47 +380,47 @@ - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 3875b54e7b10b10606b105340199946d0b877754 + 893c2ebbd49952ca49e93298148af2d95a61a0a4 https://github.com/dotnet/winforms 9b822fd70005bf5632d12fe76811b97b3dd044e4 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + e29823691315ed6b3acff20d5bdf3b0be7628283 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + e29823691315ed6b3acff20d5bdf3b0be7628283 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + e29823691315ed6b3acff20d5bdf3b0be7628283 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + e29823691315ed6b3acff20d5bdf3b0be7628283 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + e29823691315ed6b3acff20d5bdf3b0be7628283 - + https://github.com/dotnet/arcade - 1cfa39f82d00b3659a3d367bc344241946e10681 + e29823691315ed6b3acff20d5bdf3b0be7628283 - + https://github.com/dotnet/extensions - 90dd3fdbb6056d8ae177ab102b779e3922a88981 + ed336d147b46d36edad3e9441398de636b67cf5d - + https://github.com/dotnet/extensions - 90dd3fdbb6056d8ae177ab102b779e3922a88981 + ed336d147b46d36edad3e9441398de636b67cf5d https://github.com/nuget/nuget.client diff --git a/eng/Versions.props b/eng/Versions.props index c246614af85c..a2d7c6b770e6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 9 0 - 6 + 9 true @@ -68,92 +68,92 @@ --> - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6-servicing.25266.13 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6-servicing.25266.13 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6-servicing.25266.13 - 9.0.6-servicing.25266.13 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9-servicing.25419.16 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9-servicing.25419.16 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9-servicing.25419.16 + 9.0.9-servicing.25419.16 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 - 9.0.6-servicing.25266.13 - 9.0.6 + 9.0.9-servicing.25419.16 + 9.0.9 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 - 9.6.0-preview.1.25260.2 - 9.6.0-preview.1.25260.2 + 9.9.0-preview.1.25409.1 + 9.9.0-preview.1.25409.1 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 - 9.0.6 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 + 9.0.9 4.11.0-3.24554.2 4.11.0-3.24554.2 @@ -166,10 +166,10 @@ 6.2.4 6.2.4 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 - 9.0.0-beta.25255.5 + 9.0.0-beta.25407.2 + 9.0.0-beta.25407.2 + 9.0.0-beta.25407.2 + 9.0.0-beta.25407.2 9.0.0-alpha.1.24575.1 @@ -227,13 +227,12 @@ 6.0.0 1.1.1 - 17.4.0 + 17.8.29 1.2.0 - 17.4.0 - 17.4.0 - 17.4.0 + 17.8.29 + 17.8.29 + 17.8.29 1.2.6 - 17.4.0 (AlmaLinux.8.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:almalinux-8-helix-amd64 - (Alpine.318.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.18-helix-amd64 + (Alpine.321.Amd64.Open)azurelinux.3.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.21-helix-amd64 (Debian.12.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:debian-12-helix-amd64 (Fedora.41.Amd64.Open)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-41-helix (Mariner)Ubuntu.2204.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:cbl-mariner-2.0-helix-amd64 @@ -42,7 +42,7 @@ - + diff --git a/eng/targets/Helix.targets b/eng/targets/Helix.targets index 70e01877befa..0aab28ef20cc 100644 --- a/eng/targets/Helix.targets +++ b/eng/targets/Helix.targets @@ -17,7 +17,7 @@ $(HelixQueueAlmaLinux8); - $(HelixQueueAlpine318); + $(HelixQueueAlpine); $(HelixQueueDebian12); $(HelixQueueFedora40); $(HelixQueueMariner); diff --git a/global.json b/global.json index f50567b4c1a7..ae875b2ea090 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "9.0.106" + "version": "9.0.109" }, "tools": { - "dotnet": "9.0.106", + "dotnet": "9.0.109", "runtimes": { "dotnet/x86": [ "$(MicrosoftNETCoreBrowserDebugHostTransportVersion)" @@ -27,7 +27,7 @@ "jdk": "latest" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25255.5", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25255.5" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25407.2", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25407.2" } } diff --git a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs index 792e222f4183..53fd2addd404 100644 --- a/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs +++ b/src/Components/test/E2ETest/ServerRenderingTests/FormHandlingTests/FormWithParentBindingContextTest.cs @@ -1287,7 +1287,7 @@ public void CanBindToFormWithFiles() } [Theory] - [InlineData(true)] + // [InlineData(true)] QuarantinedTest: https://github.com/dotnet/aspnetcore/issues/61882 [InlineData(false)] public void CanUseFormWithMethodGet(bool suppressEnhancedNavigation) { diff --git a/src/Http/Headers/test/CookieHeaderValueTest.cs b/src/Http/Headers/test/CookieHeaderValueTest.cs index 6623a8ed13dd..6ad2e962d005 100644 --- a/src/Http/Headers/test/CookieHeaderValueTest.cs +++ b/src/Http/Headers/test/CookieHeaderValueTest.cs @@ -75,7 +75,7 @@ public static TheoryData InvalidCookieValues } } - public static TheoryData, string?[]> ListOfCookieHeaderDataSet + public static TheoryData, string?[]> ListOfStrictCookieHeaderDataSet { get { @@ -94,19 +94,30 @@ public static TheoryData InvalidCookieValues dataset.Add(new[] { header1 }.ToList(), new[] { string1 }); dataset.Add(new[] { header1, header1 }.ToList(), new[] { string1, string1 }); - dataset.Add(new[] { header1, header1 }.ToList(), new[] { string1, null, "", " ", ";", " , ", string1 }); dataset.Add(new[] { header2 }.ToList(), new[] { string2 }); dataset.Add(new[] { header1, header2 }.ToList(), new[] { string1, string2 }); - dataset.Add(new[] { header1, header2 }.ToList(), new[] { string1 + ", " + string2 }); dataset.Add(new[] { header2, header1 }.ToList(), new[] { string2 + "; " + string1 }); dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string1, string2, string3, string4 }); - dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(",", string1, string2, string3, string4) }); dataset.Add(new[] { header1, header2, header3, header4 }.ToList(), new[] { string.Join(";", string1, string2, string3, string4) }); return dataset; } } + public static TheoryData, string?[]> ListOfCookieHeaderDataSet + { + get + { + var header1 = new CookieHeaderValue("name1", "n1=v1&n2=v2&n3=v3"); + var string1 = "name1=n1=v1&n2=v2&n3=v3"; + + var dataset = new TheoryData, string?[]>(); + dataset.Concat(ListOfStrictCookieHeaderDataSet); + dataset.Add(new[] { header1, header1 }.ToList(), new[] { string1, null, "", " ", ";", " , ", string1 }); + return dataset; + } + } + public static TheoryData?, string?[]> ListWithInvalidCookieHeaderDataSet { get @@ -127,18 +138,19 @@ public static TheoryData InvalidCookieValues dataset.Add(new[] { header1 }.ToList(), new[] { validString1, invalidString1 }); dataset.Add(new[] { header1 }.ToList(), new[] { validString1, null, "", " ", ";", " , ", invalidString1 }); dataset.Add(new[] { header1 }.ToList(), new[] { invalidString1, null, "", " ", ";", " , ", validString1 }); - dataset.Add(new[] { header1 }.ToList(), new[] { validString1 + ", " + invalidString1 }); - dataset.Add(new[] { header2 }.ToList(), new[] { invalidString1 + ", " + validString2 }); + dataset.Add(null, new[] { validString1 + ", " }); + dataset.Add(null, new[] { invalidString1 + ", " + validString2 }); dataset.Add(new[] { header1 }.ToList(), new[] { invalidString1 + "; " + validString1 }); dataset.Add(new[] { header2 }.ToList(), new[] { validString2 + "; " + invalidString1 }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { invalidString1, validString1, validString2, validString3 }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { validString1, invalidString1, validString2, validString3 }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { validString1, validString2, invalidString1, validString3 }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { validString1, validString2, validString3, invalidString1 }); - dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", invalidString1, validString1, validString2, validString3) }); - dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", validString1, invalidString1, validString2, validString3) }); - dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", validString1, validString2, invalidString1, validString3) }); - dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(",", validString1, validString2, validString3, invalidString1) }); + dataset.Add(null, new[] { string.Join(",", invalidString1, validString1, validString2, validString3) }); + dataset.Add(null, new[] { string.Join(",", validString1, invalidString1, validString2, validString3) }); + dataset.Add(null, new[] { string.Join(",", validString1, validString2, invalidString1, validString3) }); + dataset.Add(null, new[] { string.Join(",", validString1, validString2, validString3, invalidString1) }); + dataset.Add(null, new[] { string.Join(",", validString1, validString2, validString3) }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", invalidString1, validString1, validString2, validString3) }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", validString1, invalidString1, validString2, validString3) }); dataset.Add(new[] { header1, header2, header3 }.ToList(), new[] { string.Join(";", validString1, validString2, invalidString1, validString3) }); @@ -248,7 +260,7 @@ public void CookieHeaderValue_ParseList_AcceptsValidValues(IList cookies, string[] input) { var results = CookieHeaderValue.ParseStrictList(input); @@ -267,7 +279,7 @@ public void CookieHeaderValue_TryParseList_AcceptsValidValues(IList cookies, string[] input) { var result = CookieHeaderValue.TryParseStrictList(input, out var results); diff --git a/src/Http/Http.Extensions/test/HeaderDictionaryTypeExtensionsTest.cs b/src/Http/Http.Extensions/test/HeaderDictionaryTypeExtensionsTest.cs index 33ecc3ff1ea8..b4071866534b 100644 --- a/src/Http/Http.Extensions/test/HeaderDictionaryTypeExtensionsTest.cs +++ b/src/Http/Http.Extensions/test/HeaderDictionaryTypeExtensionsTest.cs @@ -214,7 +214,7 @@ public void GetListT_StringWithQualityHeaderValidValue_Success() public void GetListT_CookieHeaderValue_Success() { var context = new DefaultHttpContext(); - context.Request.Headers.Cookie = "cookie1=a,cookie2=b"; + context.Request.Headers.Cookie = "cookie1=a;cookie2=b"; var result = context.Request.GetTypedHeaders().GetList(HeaderNames.Cookie); diff --git a/src/Http/Http/test/RequestCookiesCollectionTests.cs b/src/Http/Http/test/RequestCookiesCollectionTests.cs index e08a53f29711..d584f2db0599 100644 --- a/src/Http/Http/test/RequestCookiesCollectionTests.cs +++ b/src/Http/Http/test/RequestCookiesCollectionTests.cs @@ -33,11 +33,18 @@ public void ParseManyCookies() [Theory] [InlineData(",", null)] [InlineData(";", null)] - [InlineData("er=dd,cc,bb", new[] { "dd" })] - [InlineData("er=dd,err=cc,errr=bb", new[] { "dd", "cc", "bb" })] - [InlineData("errorcookie=dd,:(\"sa;", new[] { "dd" })] + [InlineData("er=dd,cc,bb", null)] + [InlineData("er=dd,err=cc,errr=bb", null)] + [InlineData("errorcookie=dd,:(\"sa;", null)] [InlineData("s;", null)] [InlineData("er=;,err=,errr=\\,errrr=\"", null)] + [InlineData("a@a=a;", null)] + [InlineData("a@ a=a;", null)] + [InlineData("a a=a;", null)] + [InlineData(",a=a;", null)] + [InlineData(",a=a", null)] + [InlineData("a=a;,b=b", new []{ "a" })] // valid cookie followed by invalid cookie + [InlineData(",a=a;b=b", new[] { "b" })] // invalid cookie followed by valid cookie public void ParseInvalidCookies(string cookieToParse, string[] expectedCookieValues) { var cookies = RequestCookieCollection.Parse(new StringValues(new[] { cookieToParse })); diff --git a/src/Http/Shared/CookieHeaderParserShared.cs b/src/Http/Shared/CookieHeaderParserShared.cs index e4b1d83e519a..0eb1c64d533a 100644 --- a/src/Http/Shared/CookieHeaderParserShared.cs +++ b/src/Http/Shared/CookieHeaderParserShared.cs @@ -89,6 +89,17 @@ public static bool TryParseValue(StringSegment value, ref int index, bool suppor if (!TryGetCookieLength(value, ref current, out parsedName, out parsedValue)) { + var separatorIndex = value.IndexOf(';', current); + if (separatorIndex > 0) + { + // Skip the invalid values and keep trying. + index = separatorIndex; + } + else + { + // No more separators, so we're done. + index = value.Length; + } return false; } @@ -97,6 +108,17 @@ public static bool TryParseValue(StringSegment value, ref int index, bool suppor // If we support multiple values and we've not reached the end of the string, then we must have a separator. if ((separatorFound && !supportsMultipleValues) || (!separatorFound && (current < value.Length))) { + var separatorIndex = value.IndexOf(';', current); + if (separatorIndex > 0) + { + // Skip the invalid values and keep trying. + index = separatorIndex; + } + else + { + // No more separators, so we're done. + index = value.Length; + } return false; } @@ -112,7 +134,7 @@ private static int GetNextNonEmptyOrWhitespaceIndex(StringSegment input, int sta separatorFound = false; var current = startIndex + HttpRuleParser.GetWhitespaceLength(input, startIndex); - if ((current == input.Length) || (input[current] != ',' && input[current] != ';')) + if (current == input.Length || input[current] != ';') { return current; } @@ -125,8 +147,8 @@ private static int GetNextNonEmptyOrWhitespaceIndex(StringSegment input, int sta if (skipEmptyValues) { - // Most headers only split on ',', but cookies primarily split on ';' - while ((current < input.Length) && ((input[current] == ',') || (input[current] == ';'))) + // Cookies are split on ';' + while (current < input.Length && input[current] == ';') { current++; // skip delimiter. current = current + HttpRuleParser.GetWhitespaceLength(input, current); @@ -136,6 +158,18 @@ private static int GetNextNonEmptyOrWhitespaceIndex(StringSegment input, int sta return current; } + /* + * https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1 + * cookie-pair = cookie-name "=" cookie-value + * cookie-name = token + * token = 1* + separators = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT + CTL = + */ // name=value; name="value" internal static bool TryGetCookieLength(StringSegment input, ref int offset, [NotNullWhen(true)] out StringSegment? parsedName, [NotNullWhen(true)] out StringSegment? parsedValue) { diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs index d00ccfa0a13d..72cae1507a0e 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs @@ -21,6 +21,7 @@ public class ForwardedHeadersMiddleware private readonly ForwardedHeadersOptions _options; private readonly RequestDelegate _next; private readonly ILogger _logger; + private readonly bool _ignoreUnknownProxiesWithoutFor; private bool _allowAllHosts; private IList? _allowedHosts; @@ -63,6 +64,18 @@ public ForwardedHeadersMiddleware(RequestDelegate next, ILoggerFactory loggerFac _logger = loggerFactory.CreateLogger(); _next = next; + if (AppContext.TryGetSwitch("Microsoft.AspNetCore.HttpOverrides.IgnoreUnknownProxiesWithoutFor", out var enabled) + && enabled) + { + _ignoreUnknownProxiesWithoutFor = true; + } + + if (Environment.GetEnvironmentVariable("MICROSOFT_ASPNETCORE_HTTPOVERRIDES_IGNORE_UNKNOWN_PROXIES_WITHOUT_FOR") is string env + && (env.Equals("true", StringComparison.OrdinalIgnoreCase) || env.Equals("1"))) + { + _ignoreUnknownProxiesWithoutFor = true; + } + PreProcessHosts(); } @@ -220,15 +233,20 @@ public void ApplyForwarders(HttpContext context) for (; entriesConsumed < sets.Length; entriesConsumed++) { var set = sets[entriesConsumed]; - // For the first instance, allow remoteIp to be null for servers that don't support it natively. - if (currentValues.RemoteIpAndPort != null && checkKnownIps && !CheckKnownAddress(currentValues.RemoteIpAndPort.Address)) + // Opt-out of breaking change behavior where we now always check KnownProxies and KnownNetworks + // It used to be guarded by the ForwardedHeaders.XForwardedFor flag, but now we always check it. + if (!_ignoreUnknownProxiesWithoutFor || checkFor) { - // Stop at the first unknown remote IP, but still apply changes processed so far. - if (_logger.IsEnabled(LogLevel.Debug)) + // For the first instance, allow remoteIp to be null for servers that don't support it natively. + if (currentValues.RemoteIpAndPort != null && checkKnownIps && !CheckKnownAddress(currentValues.RemoteIpAndPort.Address)) { - _logger.LogDebug(1, "Unknown proxy: {RemoteIpAndPort}", currentValues.RemoteIpAndPort); + // Stop at the first unknown remote IP, but still apply changes processed so far. + if (_logger.IsEnabled(LogLevel.Warning)) + { + _logger.LogWarning(1, "Unknown proxy: {RemoteIpAndPort}", currentValues.RemoteIpAndPort); + } + break; } - break; } if (checkFor) diff --git a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs index 4fd1341acc45..627ad96a3cd6 100644 --- a/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs +++ b/src/Middleware/HttpOverrides/test/ForwardedHeadersMiddlewareTest.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; +using Microsoft.DotNet.RemoteExecutor; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -1035,6 +1036,128 @@ public async Task IgnoreXForwardedHeadersFromUnknownProxy(ForwardedHeaders forwa } } + [Theory] + [InlineData(ForwardedHeaders.XForwardedFor)] + [InlineData(ForwardedHeaders.XForwardedHost)] + [InlineData(ForwardedHeaders.XForwardedProto)] + [InlineData(ForwardedHeaders.XForwardedPrefix)] + public void AppContextDoesNotValidateUnknownProxyWithoutForwardedFor(ForwardedHeaders forwardedHeaders) + { + RemoteExecutor.Invoke(static async (forwardedHeadersName) => + { + Assert.True(Enum.TryParse(forwardedHeadersName, out var forwardedHeaders)); + AppContext.SetSwitch("Microsoft.AspNetCore.HttpOverrides.IgnoreUnknownProxiesWithoutFor", true); + using var host = new HostBuilder() + .ConfigureWebHost(webHostBuilder => + { + webHostBuilder + .UseTestServer() + .Configure(app => + { + var options = new ForwardedHeadersOptions + { + ForwardedHeaders = forwardedHeaders + }; + app.UseForwardedHeaders(options); + }); + }).Build(); + + await host.StartAsync(); + + var server = host.GetTestServer(); + + var context = await server.SendAsync(c => + { + c.Request.Headers["X-Forwarded-For"] = "11.111.111.11"; + c.Request.Headers["X-Forwarded-Host"] = "testhost"; + c.Request.Headers["X-Forwarded-Proto"] = "Protocol"; + c.Request.Headers["X-Forwarded-Prefix"] = "/pathbase"; + c.Connection.RemoteIpAddress = IPAddress.Parse("10.0.0.1"); + c.Connection.RemotePort = 99; + }); + + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor)) + { + // X-Forwarded-For ignored since 10.0.0.1 isn't in KnownProxies + Assert.Equal("10.0.0.1", context.Connection.RemoteIpAddress.ToString()); + } + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedHost)) + { + Assert.Equal("testhost", context.Request.Host.ToString()); + } + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedProto)) + { + Assert.Equal("Protocol", context.Request.Scheme); + } + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedPrefix)) + { + Assert.Equal("/pathbase", context.Request.PathBase); + } + return RemoteExecutor.SuccessExitCode; + }, forwardedHeaders.ToString()).Dispose(); + } + + [Theory] + [InlineData(ForwardedHeaders.XForwardedFor)] + [InlineData(ForwardedHeaders.XForwardedHost)] + [InlineData(ForwardedHeaders.XForwardedProto)] + [InlineData(ForwardedHeaders.XForwardedPrefix)] + public void EnvVariableDoesNotValidateUnknownProxyWithoutForwardedFor(ForwardedHeaders forwardedHeaders) + { + RemoteExecutor.Invoke(static async (forwardedHeadersName) => + { + Assert.True(Enum.TryParse(forwardedHeadersName, out var forwardedHeaders)); + Environment.SetEnvironmentVariable("MICROSOFT_ASPNETCORE_HTTPOVERRIDES_IGNORE_UNKNOWN_PROXIES_WITHOUT_FOR", "true"); + using var host = new HostBuilder() + .ConfigureWebHost(webHostBuilder => + { + webHostBuilder + .UseTestServer() + .Configure(app => + { + var options = new ForwardedHeadersOptions + { + ForwardedHeaders = forwardedHeaders + }; + app.UseForwardedHeaders(options); + }); + }).Build(); + + await host.StartAsync(); + + var server = host.GetTestServer(); + + var context = await server.SendAsync(c => + { + c.Request.Headers["X-Forwarded-For"] = "11.111.111.11"; + c.Request.Headers["X-Forwarded-Host"] = "testhost"; + c.Request.Headers["X-Forwarded-Proto"] = "Protocol"; + c.Request.Headers["X-Forwarded-Prefix"] = "/pathbase"; + c.Connection.RemoteIpAddress = IPAddress.Parse("10.0.0.1"); + c.Connection.RemotePort = 99; + }); + + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedFor)) + { + // X-Forwarded-For ignored since 10.0.0.1 isn't in KnownProxies + Assert.Equal("10.0.0.1", context.Connection.RemoteIpAddress.ToString()); + } + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedHost)) + { + Assert.Equal("testhost", context.Request.Host.ToString()); + } + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedProto)) + { + Assert.Equal("Protocol", context.Request.Scheme); + } + if (forwardedHeaders.HasFlag(ForwardedHeaders.XForwardedPrefix)) + { + Assert.Equal("/pathbase", context.Request.PathBase); + } + return RemoteExecutor.SuccessExitCode; + }, forwardedHeaders.ToString()).Dispose(); + } + [Fact] public async Task PartiallyEnabledForwardsPartiallyChangesRequest() { diff --git a/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs b/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs index a75546b6793f..38a38069b32c 100644 --- a/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs +++ b/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Linq; using Microsoft.Extensions.Caching.Memory; namespace Microsoft.AspNetCore.OutputCaching.Memory; @@ -9,7 +10,7 @@ namespace Microsoft.AspNetCore.OutputCaching.Memory; internal sealed class MemoryOutputCacheStore : IOutputCacheStore { private readonly MemoryCache _cache; - private readonly Dictionary> _taggedEntries = new(); + private readonly Dictionary> _taggedEntries = []; private readonly object _tagsLock = new(); internal MemoryOutputCacheStore(MemoryCache cache) @@ -20,7 +21,7 @@ internal MemoryOutputCacheStore(MemoryCache cache) } // For testing - internal Dictionary> TaggedEntries => _taggedEntries; + internal Dictionary> TaggedEntries => _taggedEntries.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Select(t => t.Key).ToHashSet()); public ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken) { @@ -30,7 +31,7 @@ public ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken { if (_taggedEntries.TryGetValue(tag, out var keys)) { - if (keys != null && keys.Count > 0) + if (keys is { Count: > 0 }) { // If MemoryCache changed to run eviction callbacks inline in Remove, iterating over keys could throw // To prevent allocating a copy of the keys we check if the eviction callback ran, @@ -40,7 +41,7 @@ public ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken while (i > 0) { var oldCount = keys.Count; - foreach (var key in keys) + foreach (var (key, _) in keys) { _cache.Remove(key); i--; @@ -74,6 +75,8 @@ public ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan val ArgumentNullException.ThrowIfNull(key); ArgumentNullException.ThrowIfNull(value); + var entryId = Guid.NewGuid(); + if (tags != null) { // Lock with SetEntry() to prevent EvictByTagAsync() from trying to remove a tag whose entry hasn't been added yet. @@ -90,27 +93,27 @@ public ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan val if (!_taggedEntries.TryGetValue(tag, out var keys)) { - keys = new HashSet(); + keys = new HashSet(); _taggedEntries[tag] = keys; } Debug.Assert(keys != null); - keys.Add(key); + keys.Add(new TaggedEntry(key, entryId)); } - SetEntry(key, value, tags, validFor); + SetEntry(key, value, tags, validFor, entryId); } } else { - SetEntry(key, value, tags, validFor); + SetEntry(key, value, tags, validFor, entryId); } return ValueTask.CompletedTask; } - void SetEntry(string key, byte[] value, string[]? tags, TimeSpan validFor) + private void SetEntry(string key, byte[] value, string[]? tags, TimeSpan validFor, Guid entryId) { Debug.Assert(key != null); @@ -120,22 +123,25 @@ void SetEntry(string key, byte[] value, string[]? tags, TimeSpan validFor) Size = value.Length }; - if (tags != null && tags.Length > 0) + if (tags is { Length: > 0 }) { // Remove cache keys from tag lists when the entry is evicted - options.RegisterPostEvictionCallback(RemoveFromTags, tags); + options.RegisterPostEvictionCallback(RemoveFromTags, (tags, entryId)); } _cache.Set(key, value, options); } - void RemoveFromTags(object key, object? value, EvictionReason reason, object? state) + private void RemoveFromTags(object key, object? value, EvictionReason reason, object? state) { - var tags = state as string[]; + Debug.Assert(state != null); + + var (tags, entryId) = ((string[] Tags, Guid EntryId))state; Debug.Assert(tags != null); Debug.Assert(tags.Length > 0); Debug.Assert(key is string); + Debug.Assert(entryId != Guid.Empty); lock (_tagsLock) { @@ -143,7 +149,7 @@ void RemoveFromTags(object key, object? value, EvictionReason reason, object? st { if (_taggedEntries.TryGetValue(tag, out var tagged)) { - tagged.Remove((string)key); + tagged.Remove(new TaggedEntry((string)key, entryId)); // Remove the collection if there is no more keys in it if (tagged.Count == 0) @@ -154,4 +160,6 @@ void RemoveFromTags(object key, object? value, EvictionReason reason, object? st } } } + + private record TaggedEntry(string Key, Guid EntryId); } diff --git a/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs b/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs index e8c809911add..c1ad1d708f4b 100644 --- a/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs +++ b/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs @@ -197,6 +197,43 @@ public async Task ExpiredEntries_AreRemovedFromTags() Assert.Single(tag2s); } + [Fact] + public async Task ReplacedEntries_AreNotRemovedFromTags() + { + var testClock = new TestMemoryOptionsClock { UtcNow = DateTimeOffset.UtcNow }; + var cache = new MemoryCache(new MemoryCacheOptions { SizeLimit = 1000, Clock = testClock, ExpirationScanFrequency = TimeSpan.FromMilliseconds(1) }); + var store = new MemoryOutputCacheStore(cache); + var value = "abc"u8.ToArray(); + + await store.SetAsync("a", value, new[] { "tag1", "tag2" }, TimeSpan.FromMilliseconds(5), default); + await store.SetAsync("a", value, new[] { "tag1" }, TimeSpan.FromMilliseconds(20), default); + + testClock.Advance(TimeSpan.FromMilliseconds(10)); + + // Trigger background expiration by accessing the cache. + _ = cache.Get("a"); + + var resulta = await store.GetAsync("a", default); + + Assert.NotNull(resulta); + + HashSet tag1s, tag2s; + + // Wait for the tag2 HashSet to be removed by the background expiration thread. + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + + while (store.TaggedEntries.TryGetValue("tag2", out tag2s) && !cts.IsCancellationRequested) + { + await Task.Yield(); + } + + store.TaggedEntries.TryGetValue("tag1", out tag1s); + + Assert.Null(tag2s); + Assert.Single(tag1s); + } + [Theory] [InlineData(null)] public async Task Store_Throws_OnInvalidTag(string tag) diff --git a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs index 830b0375d396..f05c595b5c2b 100644 --- a/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs +++ b/src/OpenApi/src/Schemas/OpenApiJsonSchema.Helpers.cs @@ -136,6 +136,8 @@ internal sealed partial class OpenApiJsonSchema { type = "array"; var array = new OpenApiArray(); + // Read to process JsonTokenType.StartArray before advancing + reader.Read(); while (reader.TokenType != JsonTokenType.EndArray) { array.Add(ReadOpenApiAny(ref reader)); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs index 1c7cb1ba5746..6f87374b1c92 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs @@ -722,4 +722,93 @@ public static bool TryParse(string value, out Student result) return true; } } + + // Regression test for https://github.com/dotnet/aspnetcore/issues/62023 + // Testing that the array parsing in our OpenApiJsonSchema works + [Fact] + public async Task CustomConverterThatOutputsArrayWithDefaultValue() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.ConfigureHttpJsonOptions(options => + { + options.SerializerOptions.Converters.Add(new EnumArrayTypeConverter()); + }); + var builder = CreateBuilder(serviceCollection); + + // Act + builder.MapPost("/api", (EnumArrayType e = EnumArrayType.None) => { }); + + // Assert + await VerifyOpenApiDocument(builder, document => + { + var operation = document.Paths["/api"].Operations[OperationType.Post]; + var param = Assert.Single(operation.Parameters); + Assert.NotNull(param.Schema); + Assert.IsType(param.Schema.Default); + // Type is null, it's up to the user to configure this via a custom schema + // transformer for types with a converter. + Assert.Null(param.Schema.Type); + }); + } + + [Fact] + public async Task CustomConverterThatOutputsObjectWithDefaultValue() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.ConfigureHttpJsonOptions(options => + { + options.SerializerOptions.Converters.Add(new EnumObjectTypeConverter()); + }); + var builder = CreateBuilder(serviceCollection); + + // Act + builder.MapPost("/api", (EnumArrayType e = EnumArrayType.None) => { }); + + // Assert + await VerifyOpenApiDocument(builder, document => + { + var operation = document.Paths["/api"].Operations[OperationType.Post]; + var param = Assert.Single(operation.Parameters); + Assert.NotNull(param.Schema); + Assert.IsType(param.Schema.Default); + // Type is null, it's up to the user to configure this via a custom schema + // transformer for types with a converter. + Assert.Null(param.Schema.Type); + }); + } + + public enum EnumArrayType + { + None = 1 + } + + public class EnumArrayTypeConverter : JsonConverter + { + public override EnumArrayType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new EnumArrayType(); + } + + public override void Write(Utf8JsonWriter writer, EnumArrayType value, JsonSerializerOptions options) + { + writer.WriteStartArray(); + writer.WriteEndArray(); + } + } + + public class EnumObjectTypeConverter : JsonConverter + { + public override EnumArrayType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return new EnumArrayType(); + } + + public override void Write(Utf8JsonWriter writer, EnumArrayType value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteEndObject(); + } + } } diff --git a/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs b/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs index 8f8873057027..de74d6e5bf7c 100644 --- a/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs +++ b/src/Security/Authentication/Certificate/src/CertificateAuthenticationHandler.cs @@ -135,21 +135,35 @@ private async Task ValidateCertificateAsync(X509Certificate2 } var chainPolicy = BuildChainPolicy(clientCertificate, isCertificateSelfSigned); - using var chain = new X509Chain + var chain = new X509Chain { ChainPolicy = chainPolicy }; - var certificateIsValid = chain.Build(clientCertificate); - if (!certificateIsValid) + try + { + var certificateIsValid = chain.Build(clientCertificate); + if (!certificateIsValid) + { + var chainErrors = new List(chain.ChainStatus.Length); + foreach (var validationFailure in chain.ChainStatus) + { + chainErrors.Add($"{validationFailure.Status} {validationFailure.StatusInformation}"); + } + Logger.CertificateFailedValidation(clientCertificate.Subject, chainErrors); + return AuthenticateResults.InvalidClientCertificate; + } + } + finally { - var chainErrors = new List(chain.ChainStatus.Length); - foreach (var validationFailure in chain.ChainStatus) + // Disposing the chain does not dispose the elements we potentially built. + // Do the full walk manually to dispose. + for (var i = 0; i < chain.ChainElements.Count; i++) { - chainErrors.Add($"{validationFailure.Status} {validationFailure.StatusInformation}"); + chain.ChainElements[i].Certificate.Dispose(); } - Logger.CertificateFailedValidation(clientCertificate.Subject, chainErrors); - return AuthenticateResults.InvalidClientCertificate; + + chain.Dispose(); } var certificateValidatedContext = new CertificateValidatedContext(Context, Scheme, Options) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp index 9b12cd0132b4..8fb960261590 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp @@ -197,7 +197,7 @@ HostFxrResolver::TryGetHostFxrPath( size_t size = MAX_PATH * 2; hostfxrPath.resize(size); - get_hostfxr_parameters params; + get_hostfxr_parameters params{}; params.size = sizeof(get_hostfxr_parameters); params.assembly_path = applicationPath.c_str(); params.dotnet_root = dotnetRoot.c_str(); @@ -393,7 +393,7 @@ HostFxrResolver::GetAbsolutePathToDotnetFromHostfxr(const fs::path& hostfxrPath) // Tries to call where.exe to find the location of dotnet.exe. // Will check that the bitness of dotnet matches the current // worker process bitness. -// Returns true if a valid dotnet was found, else false.R +// Returns true if a valid dotnet was found, else false. // std::optional HostFxrResolver::InvokeWhereToFindDotnet() @@ -415,8 +415,7 @@ HostFxrResolver::InvokeWhereToFindDotnet() DWORD dwExitCode; STRU struDotnetSubstring; STRU struDotnetLocationsString; - DWORD dwNumBytesRead; - DWORD dwBinaryType; + DWORD dwNumBytesRead = 0; INT index = 0; INT prevIndex = 0; std::optional result; @@ -521,14 +520,7 @@ HostFxrResolver::InvokeWhereToFindDotnet() LOG_INFOF(L"Processing entry '%ls'", struDotnetSubstring.QueryStr()); - if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType))) - { - continue; - } - - LOG_INFOF(L"Binary type %d", dwBinaryType); - - if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) + if (fIsCurrentProcess64Bit == IsX64(struDotnetSubstring.QueryStr())) { // The bitness of dotnet matched with the current worker process bitness. return std::make_optional(struDotnetSubstring.QueryStr()); @@ -539,6 +531,62 @@ HostFxrResolver::InvokeWhereToFindDotnet() return result; } +BOOL HostFxrResolver::IsX64(const WCHAR* dotnetPath) +{ + // Errors while reading from the file shouldn't throw unless + // file.exception(bits) is set + std::ifstream file(dotnetPath, std::ios::binary); + if (!file.is_open()) + { + LOG_TRACEF(L"Failed to open file %ls", dotnetPath); + return false; + } + + // Read the DOS header + IMAGE_DOS_HEADER dosHeader{}; + file.read(reinterpret_cast(&dosHeader), sizeof(dosHeader)); + if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) // 'MZ' + { + LOG_TRACEF(L"%ls is not a valid executable file (missing MZ header).", dotnetPath); + return false; + } + + // Seek to the PE header + file.seekg(dosHeader.e_lfanew, std::ios::beg); + + // Read the PE header + DWORD peSignature{}; + file.read(reinterpret_cast(&peSignature), sizeof(peSignature)); + if (peSignature != IMAGE_NT_SIGNATURE) // 'PE\0\0' + { + LOG_TRACEF(L"%ls is not a valid PE file (missing PE header).", dotnetPath); + return false; + } + + // Read the file header + IMAGE_FILE_HEADER fileHeader{}; + file.read(reinterpret_cast(&fileHeader), sizeof(fileHeader)); + + // Read the optional header magic field + WORD magic{}; + file.read(reinterpret_cast(&magic), sizeof(magic)); + + // Determine the architecture based on the magic value + if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + LOG_INFOF(L"%ls is 32-bit", dotnetPath); + return false; + } + else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + LOG_INFOF(L"%ls is 64-bit", dotnetPath); + return true; + } + + LOG_INFOF(L"%ls is unknown architecture %i", dotnetPath, fileHeader.Machine); + return false; +} + std::optional HostFxrResolver::GetAbsolutePathToDotnetFromProgramFiles() { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h index 519f6df52c97..08ec650aec54 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h @@ -74,6 +74,8 @@ class HostFxrResolver const std::filesystem::path & requestedPath ); + static BOOL IsX64(const WCHAR* dotnetPath); + struct LocalFreeDeleter { void operator ()(_In_ LPWSTR* ptr) const diff --git a/src/Servers/IIS/build/Build.Lib.Settings b/src/Servers/IIS/build/Build.Lib.Settings index 0dcba8c2011a..9327eb363771 100644 --- a/src/Servers/IIS/build/Build.Lib.Settings +++ b/src/Servers/IIS/build/Build.Lib.Settings @@ -9,7 +9,7 @@ - false + true _LIB;%(PreprocessorDefinitions) true diff --git a/src/Shared/CertificateGeneration/UnixCertificateManager.cs b/src/Shared/CertificateGeneration/UnixCertificateManager.cs index c583c4d370ed..9212fc475cf7 100644 --- a/src/Shared/CertificateGeneration/UnixCertificateManager.cs +++ b/src/Shared/CertificateGeneration/UnixCertificateManager.cs @@ -62,18 +62,32 @@ public override TrustLevel GetTrustLevel(X509Certificate2 certificate) // Building the chain will check whether dotnet trusts the cert. We could, instead, // enumerate the Root store and/or look for the file in the OpenSSL directory, but // this tests the real-world behavior. - using var chain = new X509Chain(); - // This is just a heuristic for whether or not we should prompt the user to re-run with `--trust` - // so we don't need to check revocation (which doesn't really make sense for dev certs anyway) - chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; - if (chain.Build(certificate)) + var chain = new X509Chain(); + try { - sawTrustSuccess = true; + // This is just a heuristic for whether or not we should prompt the user to re-run with `--trust` + // so we don't need to check revocation (which doesn't really make sense for dev certs anyway) + chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; + if (chain.Build(certificate)) + { + sawTrustSuccess = true; + } + else + { + sawTrustFailure = true; + Log.UnixNotTrustedByDotnet(); + } } - else + finally { - sawTrustFailure = true; - Log.UnixNotTrustedByDotnet(); + // Disposing the chain does not dispose the elements we potentially built. + // Do the full walk manually to dispose. + for (var i = 0; i < chain.ChainElements.Count; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } + + chain.Dispose(); } // Will become the name of the file on disk and the nickname in the NSS DBs @@ -94,7 +108,7 @@ public override TrustLevel GetTrustLevel(X509Certificate2 certificate) var certPath = Path.Combine(sslCertDir, certificateNickname + ".pem"); if (File.Exists(certPath)) { - var candidate = X509CertificateLoader.LoadCertificateFromFile(certPath); + using var candidate = X509CertificateLoader.LoadCertificateFromFile(certPath); if (AreCertificatesEqual(certificate, candidate)) { foundCert = true; diff --git a/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/GsonHubProtocol.java b/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/GsonHubProtocol.java index 042ca484806f..4b0c8848e816 100644 --- a/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/GsonHubProtocol.java +++ b/src/SignalR/clients/java/signalr/core/src/main/java/com/microsoft/signalr/GsonHubProtocol.java @@ -126,7 +126,14 @@ public List parseMessages(ByteBuffer payload, InvocationBinder binde } break; case "headers": - throw new RuntimeException("Headers not implemented yet."); + // Parse headers as Map but don't store for now as it's unused + reader.beginObject(); + while (reader.hasNext()) { + reader.nextName(); // Read the key + reader.nextString(); // Read the value + } + reader.endObject(); + break; default: // Skip unknown property, allows new clients to still work with old protocols reader.skipValue(); diff --git a/src/SignalR/clients/java/signalr/test/src/main/java/com/microsoft/signalr/GsonHubProtocolTest.java b/src/SignalR/clients/java/signalr/test/src/main/java/com/microsoft/signalr/GsonHubProtocolTest.java index d696a74850eb..2ecfd483c7f5 100644 --- a/src/SignalR/clients/java/signalr/test/src/main/java/com/microsoft/signalr/GsonHubProtocolTest.java +++ b/src/SignalR/clients/java/signalr/test/src/main/java/com/microsoft/signalr/GsonHubProtocolTest.java @@ -527,4 +527,98 @@ public void canRegisterTypeAdaptorWithoutAffectingJsonProtocol() { assertEquals(3, (int) invocationMessage.getArguments()[0]); assertEquals("four", invocationMessage.getArguments()[1]); } + + @Test + public void canParseInvocationMessageWithHeaders() { + String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42],\"headers\":{\"a\":\"b\",\"c\":\"d\"}}\u001E"; + ByteBuffer message = TestUtils.stringToByteBuffer(stringifiedMessage); + TestBinder binder = new TestBinder(new Type[] { int.class }, null); + + List messages = hubProtocol.parseMessages(message, binder); + + assertNotNull(messages); + assertEquals(1, messages.size()); + + assertEquals(HubMessageType.INVOCATION, messages.get(0).getMessageType()); + InvocationMessage invocationMessage = (InvocationMessage) messages.get(0); + + assertEquals("test", invocationMessage.getTarget()); + assertEquals(null, invocationMessage.getInvocationId()); + int messageResult = (int)invocationMessage.getArguments()[0]; + assertEquals(42, messageResult); + // Headers are parsed but not stored, so we just verify the message was processed successfully + } + + @Test + public void canParseInvocationMessageWithEmptyHeaders() { + String stringifiedMessage = "{\"type\":1,\"target\":\"test\",\"arguments\":[42],\"headers\":{}}\u001E"; + ByteBuffer message = TestUtils.stringToByteBuffer(stringifiedMessage); + TestBinder binder = new TestBinder(new Type[] { int.class }, null); + + List messages = hubProtocol.parseMessages(message, binder); + + assertNotNull(messages); + assertEquals(1, messages.size()); + + assertEquals(HubMessageType.INVOCATION, messages.get(0).getMessageType()); + InvocationMessage invocationMessage = (InvocationMessage) messages.get(0); + + assertEquals("test", invocationMessage.getTarget()); + int messageResult = (int)invocationMessage.getArguments()[0]; + assertEquals(42, messageResult); + } + + @Test + public void canParseCompletionMessageWithHeaders() { + String stringifiedMessage = "{\"type\":3,\"invocationId\":\"1\",\"result\":42,\"headers\":{\"a\":\"b\",\"c\":\"d\"}}\u001E"; + ByteBuffer message = TestUtils.stringToByteBuffer(stringifiedMessage); + TestBinder binder = new TestBinder(null, int.class); + + List messages = hubProtocol.parseMessages(message, binder); + + assertNotNull(messages); + assertEquals(1, messages.size()); + + assertEquals(HubMessageType.COMPLETION, messages.get(0).getMessageType()); + CompletionMessage completionMessage = (CompletionMessage) messages.get(0); + assertEquals("1", completionMessage.getInvocationId()); + assertEquals(42, completionMessage.getResult()); + assertEquals(null, completionMessage.getError()); + } + + @Test + public void canParseStreamItemMessageWithHeaders() { + String stringifiedMessage = "{\"type\":2,\"invocationId\":\"1\",\"item\":\"test-item\",\"headers\":{\"a\":\"b\"}}\u001E"; + ByteBuffer message = TestUtils.stringToByteBuffer(stringifiedMessage); + TestBinder binder = new TestBinder(null, String.class); + + List messages = hubProtocol.parseMessages(message, binder); + + assertNotNull(messages); + assertEquals(1, messages.size()); + + assertEquals(HubMessageType.STREAM_ITEM, messages.get(0).getMessageType()); + StreamItem streamItem = (StreamItem) messages.get(0); + assertEquals("1", streamItem.getInvocationId()); + assertEquals("test-item", streamItem.getItem()); + } + + @Test + public void canParseMessageWithHeadersInDifferentOrder() { + String stringifiedMessage = "{\"headers\":{\"First\":\"value1\",\"Second\":\"value2\"},\"type\":1,\"target\":\"test\",\"arguments\":[42]}\u001E"; + ByteBuffer message = TestUtils.stringToByteBuffer(stringifiedMessage); + TestBinder binder = new TestBinder(new Type[] { int.class }, null); + + List messages = hubProtocol.parseMessages(message, binder); + + assertNotNull(messages); + assertEquals(1, messages.size()); + + assertEquals(HubMessageType.INVOCATION, messages.get(0).getMessageType()); + InvocationMessage invocationMessage = (InvocationMessage) messages.get(0); + + assertEquals("test", invocationMessage.getTarget()); + int messageResult = (int)invocationMessage.getArguments()[0]; + assertEquals(42, messageResult); + } } diff --git a/src/SignalR/server/StackExchangeRedis/test/Docker.cs b/src/SignalR/server/StackExchangeRedis/test/Docker.cs index 76fa6440e672..41315734daea 100644 --- a/src/SignalR/server/StackExchangeRedis/test/Docker.cs +++ b/src/SignalR/server/StackExchangeRedis/test/Docker.cs @@ -16,7 +16,8 @@ public class Docker { private static readonly string _exeSuffix = OperatingSystem.IsWindows() ? ".exe" : string.Empty; - private static readonly string _dockerContainerName = "redisTestContainer"; + private static readonly string _redisImageName = "dotnetdhmirror-f8bzbjakh8cga6ab.azurecr.io/library/redis:7.4"; + private static readonly string _dockerContainerName = "redisTestContainer74"; private static readonly string _dockerMonitorContainerName = _dockerContainerName + "Monitor"; private static readonly Lazy _instance = new Lazy(Create); @@ -112,7 +113,7 @@ void Run() // use static name 'redisTestContainer' so if the container doesn't get removed we don't keep adding more // use redis base docker image // 30 second timeout to allow redis image to be downloaded, should be a rare occurrence, only happening when a new version is released - RunProcessAndThrowIfFailed(_path, $"run --rm -p 6379:6379 --name {_dockerContainerName} -d redis", "redis", logger, TimeSpan.FromMinutes(1)); + RunProcessAndThrowIfFailed(_path, $"run --rm -p 6379:6379 --name {_dockerContainerName} -d {_redisImageName}", "redis", logger, TimeSpan.FromMinutes(1)); } } diff --git a/src/SignalR/server/StackExchangeRedis/test/RedisEndToEnd.cs b/src/SignalR/server/StackExchangeRedis/test/RedisEndToEnd.cs index 46ac0ded803b..f3501fb69c1e 100644 --- a/src/SignalR/server/StackExchangeRedis/test/RedisEndToEnd.cs +++ b/src/SignalR/server/StackExchangeRedis/test/RedisEndToEnd.cs @@ -35,6 +35,7 @@ public RedisEndToEndTests(RedisServerFixture serverFixture) [ConditionalTheory] [SkipIfDockerNotPresent] [MemberData(nameof(TransportTypesAndProtocolTypes))] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/62435")] public async Task HubConnectionCanSendAndReceiveMessages(HttpTransportType transportType, string protocolName) { using (StartVerifiableLog()) @@ -55,6 +56,7 @@ public async Task HubConnectionCanSendAndReceiveMessages(HttpTransportType trans [ConditionalTheory] [SkipIfDockerNotPresent] [MemberData(nameof(TransportTypesAndProtocolTypes))] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/62435")] public async Task HubConnectionCanSendAndReceiveGroupMessages(HttpTransportType transportType, string protocolName) { using (StartVerifiableLog()) @@ -116,6 +118,7 @@ public async Task CanSendAndReceiveUserMessagesFromMultipleConnectionsWithSameUs [ConditionalTheory] [SkipIfDockerNotPresent] [MemberData(nameof(TransportTypesAndProtocolTypes))] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/62435")] public async Task CanSendAndReceiveUserMessagesWhenOneConnectionWithUserDisconnects(HttpTransportType transportType, string protocolName) { // Regression test: @@ -145,6 +148,7 @@ public async Task CanSendAndReceiveUserMessagesWhenOneConnectionWithUserDisconne [ConditionalTheory] [SkipIfDockerNotPresent] [MemberData(nameof(TransportTypesAndProtocolTypes))] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/62435")] public async Task HubConnectionCanSendAndReceiveGroupMessagesGroupNameWithPatternIsTreatedAsLiteral(HttpTransportType transportType, string protocolName) { using (StartVerifiableLog()) @@ -213,6 +217,7 @@ public async Task CanSendAndReceiveUserMessagesUserNameWithPatternIsTreatedAsLit [SkipIfDockerNotPresent] [InlineData("messagepack")] [InlineData("json")] + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/62435")] public async Task StatefulReconnectPreservesMessageFromOtherServer(string protocolName) { using (StartVerifiableLog()) diff --git a/src/submodules/googletest b/src/submodules/googletest index 04ee1b4f2aef..373af2e3df71 160000 --- a/src/submodules/googletest +++ b/src/submodules/googletest @@ -1 +1 @@ -Subproject commit 04ee1b4f2aefdffb0135d7cf2a2c519fe50dabe4 +Subproject commit 373af2e3df71599b87a40ce0e37164523849166b