diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index 0395418fe330..886ccb870b45 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -138,6 +138,10 @@ extends: os: windows spotBugs: enabled: false + policheck: + enabled: true + tsa: + enabled: true containers: alpine319WithNode: image: mcr.microsoft.com/dotnet-buildtools/prereqs:alpine-3.19-WithNode diff --git a/NuGet.config b/NuGet.config index 1ccd39949f02..bad334e8c9b3 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,10 +6,11 @@ - + - + + @@ -36,7 +37,7 @@ - + @@ -45,7 +46,8 @@ - + + diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props index fdced6323d23..d0138a65e8de 100644 --- a/eng/Baseline.Designer.props +++ b/eng/Baseline.Designer.props @@ -2,117 +2,117 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 @@ -120,281 +120,281 @@ - 8.0.8 + 8.0.10 - - + + - - + + - - + + - 8.0.8 + 8.0.10 - + - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - - - - + + + + - 8.0.8 + 8.0.10 - - + + - - + + - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - + + - + - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - + - + - + - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 - - - - - + + + + + - + - - - - - + + + + + - + - - - - - + + + + + - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - - + + - - + + - - + + - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 @@ -403,79 +403,79 @@ - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - - + + - - + + - - + + - - + + - 8.0.8 + 8.0.10 - - + + - + - - + + - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 - - + + - 8.0.8 + 8.0.10 @@ -491,192 +491,192 @@ - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - - - + + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - + + - - + + - - + + - 8.0.8 + 8.0.10 - - + + - - + + - - - - + + + + - - + + - - + + - - + + - - + + - 8.0.8 + 8.0.10 - + - + - + - + - + - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 - - - - + + + + - 8.0.8 + 8.0.10 @@ -685,64 +685,64 @@ - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 @@ -764,29 +764,29 @@ - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 @@ -802,48 +802,48 @@ - 8.0.8 + 8.0.10 - + - - + + - - - + + + - + - - + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - - + + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 @@ -853,144 +853,144 @@ - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 - - + + - - + + - - + + - 8.0.8 + 8.0.10 - + - + - + - + - + - + - 8.0.8 + 8.0.10 - - - + + + - - - + + + - - - + + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - - - - + + + + - - - - + + + + - - - - + + + + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - + - + - 8.0.8 + 8.0.10 - 8.0.8 + 8.0.10 - + - 8.0.8 + 8.0.10 diff --git a/eng/Baseline.xml b/eng/Baseline.xml index 199a62d5c74a..b69677446aba 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. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 830dd83228e5..08f471a66db1 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,37 +9,37 @@ --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 4315fa43c9573671f8f5be21497d59f9c99cd829 + 5d020e763f00511c102f94fdf5bf525512d7daaf https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -121,9 +121,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -185,13 +185,13 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 - + https://github.com/dotnet/source-build-externals - fb970eccb0a9cae3092464e29cbabda0d4115049 + d4feb7e49067fc9bbf7dfb9fa76a326c33fa0595 @@ -275,17 +275,17 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-runtime 81cabf2857a01351e5ab578947c7403a5b128ad1 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 https://dev.azure.com/dnceng/internal/_git/dotnet-runtime @@ -316,22 +316,22 @@ Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime. All Runtime.$rid packages should have the same version. --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 https://github.com/dotnet/xdt @@ -368,34 +368,34 @@ - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 81cabf2857a01351e5ab578947c7403a5b128ad1 + 9cb3b725e3ad2b57ddc9fb2dd48d2d170563a8f5 https://github.com/dotnet/winforms abda8e3bfa78319363526b5a5f86863ec979940e - + https://github.com/dotnet/arcade - 80264e60280e2815e7d65871081ccac04a32445c + f7fb1fec01b91be69e4dcc5290a0bff3f28e214f - + https://github.com/dotnet/arcade - 80264e60280e2815e7d65871081ccac04a32445c + f7fb1fec01b91be69e4dcc5290a0bff3f28e214f - + https://github.com/dotnet/arcade - 80264e60280e2815e7d65871081ccac04a32445c + f7fb1fec01b91be69e4dcc5290a0bff3f28e214f - + https://github.com/dotnet/arcade - 80264e60280e2815e7d65871081ccac04a32445c + f7fb1fec01b91be69e4dcc5290a0bff3f28e214f - + https://github.com/dotnet/arcade - 80264e60280e2815e7d65871081ccac04a32445c + f7fb1fec01b91be69e4dcc5290a0bff3f28e214f https://github.com/dotnet/extensions diff --git a/eng/Versions.props b/eng/Versions.props index 02c0d42b1a5d..f89dc61bace1 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,10 +8,10 @@ 8 0 - 10 + 11 - false + true 7.1.2 7.* 8.0.2 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10-servicing.24466.10 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11-servicing.24517.7 8.0.0 8.0.1 8.0.0 @@ -93,7 +93,7 @@ 8.0.0 8.0.0 8.0.0 - 8.0.10-servicing.24466.10 + 8.0.11-servicing.24517.7 8.0.1 8.0.1 8.0.1 @@ -109,7 +109,7 @@ 8.0.0 8.0.2 8.0.0 - 8.0.10-servicing.24466.10 + 8.0.11-servicing.24517.7 8.0.1 8.0.1 8.0.1 @@ -129,9 +129,9 @@ 8.0.0 8.0.0 8.0.0 - 8.0.10-servicing.24466.10 + 8.0.11-servicing.24517.7 - 8.0.10-servicing.24466.10 + 8.0.11-servicing.24517.7 8.0.0 8.0.1 @@ -143,14 +143,14 @@ 8.1.0-preview.23604.1 8.1.0-preview.23604.1 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 - 8.0.10 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 + 8.0.11 4.8.0-3.23518.7 4.8.0-3.23518.7 @@ -162,11 +162,11 @@ 6.2.4 6.2.4 - 8.0.0-beta.24426.2 - 8.0.0-beta.24426.2 - 8.0.0-beta.24426.2 + 8.0.0-beta.24516.1 + 8.0.0-beta.24516.1 + 8.0.0-beta.24516.1 - 8.0.0-alpha.1.24379.1 + 8.0.0-alpha.1.24510.2 8.0.0-alpha.1.24415.1 @@ -300,10 +300,10 @@ 2.57.0 2.57.0 2.5.108 - 2.15.2 - 2.15.2 - 2.15.2 - 2.15.2 + 3.2.0 + 3.2.0 + 3.2.0 + 3.2.0 6.0.1 $(MessagePackVersion) 4.10.0 diff --git a/eng/common/templates-official/steps/get-delegation-sas.yml b/eng/common/templates-official/steps/get-delegation-sas.yml index c0e8f91317f0..c690cc0a070c 100644 --- a/eng/common/templates-official/steps/get-delegation-sas.yml +++ b/eng/common/templates-official/steps/get-delegation-sas.yml @@ -28,7 +28,16 @@ steps: # Calculate the expiration of the SAS token and convert to UTC $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads + # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484 + $sas = "" + do { + $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to generate SAS token." + exit 1 + } + } while($sas.IndexOf('/') -ne -1) if ($LASTEXITCODE -ne 0) { Write-Error "Failed to generate SAS token." diff --git a/eng/common/templates/steps/get-delegation-sas.yml b/eng/common/templates/steps/get-delegation-sas.yml index c0e8f91317f0..c690cc0a070c 100644 --- a/eng/common/templates/steps/get-delegation-sas.yml +++ b/eng/common/templates/steps/get-delegation-sas.yml @@ -28,7 +28,16 @@ steps: # Calculate the expiration of the SAS token and convert to UTC $expiry = (Get-Date).AddHours(${{ parameters.expiryInHours }}).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") - $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + # Temporarily work around a helix issue where SAS tokens with / in them will cause incorrect downloads + # of correlation payloads. https://github.com/dotnet/dnceng/issues/3484 + $sas = "" + do { + $sas = az storage container generate-sas --account-name ${{ parameters.storageAccount }} --name ${{ parameters.container }} --permissions ${{ parameters.permissions }} --expiry $expiry --auth-mode login --as-user -o tsv + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to generate SAS token." + exit 1 + } + } while($sas.IndexOf('/') -ne -1) if ($LASTEXITCODE -ne 0) { Write-Error "Failed to generate SAS token." diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index eb188cfda415..a2dedaa5297a 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -892,7 +892,7 @@ function IsWindowsPlatform() { } function Get-Darc($version) { - $darcPath = "$TempDir\darc\$(New-Guid)" + $darcPath = "$TempDir\darc\$([guid]::NewGuid())" if ($version -ne $null) { & $PSScriptRoot\darc-init.ps1 -toolpath $darcPath -darcVersion $version | Out-Host } else { diff --git a/eng/targets/Helix.Common.props b/eng/targets/Helix.Common.props index 426386e98ebf..452702d612b0 100644 --- a/eng/targets/Helix.Common.props +++ b/eng/targets/Helix.Common.props @@ -49,7 +49,7 @@ - + diff --git a/global.json b/global.json index a9a6de59b508..cbc2adc9d22b 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "8.0.108" + "version": "8.0.110" }, "tools": { - "dotnet": "8.0.108", + "dotnet": "8.0.110", "runtimes": { "dotnet/x86": [ "$(MicrosoftNETCoreBrowserDebugHostTransportVersion)" @@ -27,7 +27,7 @@ }, "msbuild-sdks": { "Yarn.MSBuild": "1.22.19", - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24426.2", - "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24426.2" + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24516.1", + "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24516.1" } } diff --git a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs index 82a882f7025a..8d928403297c 100644 --- a/src/Http/Http.Extensions/src/RequestDelegateFactory.cs +++ b/src/Http/Http.Extensions/src/RequestDelegateFactory.cs @@ -533,6 +533,11 @@ private static Expression MapHandlerReturnTypeToValueTask(Expression methodCall, } else { + if (returnType.IsValueType) + { + return Expression.Call(WrapObjectAsValueTaskMethod, Expression.Convert(methodCall, typeof(object))); + } + return Expression.Call(WrapObjectAsValueTaskMethod, methodCall); } } diff --git a/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.EndpointFilters.cs b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.EndpointFilters.cs new file mode 100644 index 000000000000..b2a223b345e3 --- /dev/null +++ b/src/Http/Http.Extensions/test/RequestDelegateFactoryTests.EndpointFilters.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Testing; + +namespace Microsoft.AspNetCore.Routing.Internal; + +public partial class RequestDelegateFactoryTests : LoggedTest +{ + public static object[][] ValueTypeReturningDelegates => + [ + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 'b')], + [(Func)((HttpContext httpContext) => true)], + [(Func)((HttpContext httpContext) => 4.2f)], + [(Func)((HttpContext httpContext) => 4.2)], + [(Func)((HttpContext httpContext) => 4.2m)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)] + ]; + + [Theory] + [MemberData(nameof(ValueTypeReturningDelegates))] + public void Create_WithEndpointFilterOnBuiltInValueTypeReturningDelegate_Works(Delegate @delegate) + { + var invokeCount = 0; + + RequestDelegateFactoryOptions options = new() + { + EndpointBuilder = CreateEndpointBuilderFromFilterFactories( + [ + (routeHandlerContext, next) => + { + invokeCount++; + return next; + }, + (routeHandlerContext, next) => + { + invokeCount++; + return next; + }, + ]), + }; + + var result = RequestDelegateFactory.Create(@delegate, options); + Assert.Equal(2, invokeCount); + } + + public static object[][] NullableValueTypeReturningDelegates => + [ + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 'b')], + [(Func)((HttpContext httpContext) => true)], + [(Func)((HttpContext httpContext) => 4.2f)], + [(Func)((HttpContext httpContext) => 4.2)], + [(Func)((HttpContext httpContext) => 4.2m)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)], + [(Func)((HttpContext httpContext) => 42)] + ]; + + [Theory] + [MemberData(nameof(NullableValueTypeReturningDelegates))] + public void Create_WithEndpointFilterOnNullableBuiltInValueTypeReturningDelegate_Works(Delegate @delegate) + { + var invokeCount = 0; + + RequestDelegateFactoryOptions options = new() + { + EndpointBuilder = CreateEndpointBuilderFromFilterFactories( + [ + (routeHandlerContext, next) => + { + invokeCount++; + return next; + }, + (routeHandlerContext, next) => + { + invokeCount++; + return next; + }, + ]), + }; + + var result = RequestDelegateFactory.Create(@delegate, options); + Assert.Equal(2, invokeCount); + } +} diff --git a/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs b/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs index f0508bc3b611..52e85b9a96ce 100644 --- a/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs +++ b/src/Installers/Windows/SharedFrameworkBundle/Bundle.wxs @@ -1,5 +1,6 @@ - + + + + + diff --git a/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj b/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj index e7c93b5ecba8..5f47072f1fee 100644 --- a/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj +++ b/src/Installers/Windows/SharedFrameworkBundle/SharedFrameworkBundle.wixproj @@ -13,6 +13,10 @@ + + $(WixExtDir)\WixUtilExtension.dll + WixUtilExtension + $(WixExtDir)\WixDependencyExtension.dll WixDependencyExtension @@ -25,6 +29,7 @@ + diff --git a/src/Installers/Windows/WindowsHostingBundle/Bundle.wxs b/src/Installers/Windows/WindowsHostingBundle/Bundle.wxs index 429c66c241a3..dd23ad4ee5d4 100644 --- a/src/Installers/Windows/WindowsHostingBundle/Bundle.wxs +++ b/src/Installers/Windows/WindowsHostingBundle/Bundle.wxs @@ -1,8 +1,7 @@ - + + + + + + diff --git a/src/Installers/Windows/WindowsHostingBundle/WindowsHostingBundle.wixproj b/src/Installers/Windows/WindowsHostingBundle/WindowsHostingBundle.wixproj index cfb32de2dc5d..2e5c010ed0e7 100644 --- a/src/Installers/Windows/WindowsHostingBundle/WindowsHostingBundle.wixproj +++ b/src/Installers/Windows/WindowsHostingBundle/WindowsHostingBundle.wixproj @@ -36,6 +36,7 @@ + diff --git a/src/Installers/Windows/Wix.targets b/src/Installers/Windows/Wix.targets index 0eff14c4f883..6cab47f1059a 100644 --- a/src/Installers/Windows/Wix.targets +++ b/src/Installers/Windows/Wix.targets @@ -110,7 +110,7 @@ NoLogo="true" Cultures="en-us" InstallerFile="%(WixInstallerFilesToProcess.Identity)" - AdditionalBasePaths="$(MSBuildProjectDirectory)" + AdditionalBasePaths="$(MSBuildProjectDirectory);$(PkgMicrosoft_DotNet_Build_Tasks_Installers)\build\wix\bundle" WixExtensions="@(WixExtension)" Loc="@(EmbeddedResource)" Sice="$(SuppressIces)" diff --git a/src/Servers/HttpSys/src/LoggerEventIds.cs b/src/Servers/HttpSys/src/LoggerEventIds.cs index 87f8ea56ee9f..5bc0b6b65ed6 100644 --- a/src/Servers/HttpSys/src/LoggerEventIds.cs +++ b/src/Servers/HttpSys/src/LoggerEventIds.cs @@ -58,4 +58,5 @@ internal static class LoggerEventIds public const int AcceptSetExpectationMismatch = 51; public const int AcceptCancelExpectationMismatch = 52; public const int AcceptObserveExpectationMismatch = 53; + public const int RequestParsingError = 54; } diff --git a/src/Servers/HttpSys/src/RequestProcessing/Request.cs b/src/Servers/HttpSys/src/RequestProcessing/Request.cs index 0b9418cbcac6..6029f8269f53 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/Request.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/Request.cs @@ -37,145 +37,152 @@ internal Request(RequestContext requestContext) { // TODO: Verbose log RequestContext = requestContext; - _contentBoundaryType = BoundaryType.None; - RequestId = requestContext.RequestId; - // For HTTP/2 Http.Sys assigns each request a unique connection id for use with API calls, but the RawConnectionId represents the real connection. - UConnectionId = requestContext.ConnectionId; - RawConnectionId = requestContext.RawConnectionId; - SslStatus = requestContext.SslStatus; + try + { + _contentBoundaryType = BoundaryType.None; - KnownMethod = requestContext.VerbId; - Method = requestContext.GetVerb()!; + RequestId = requestContext.RequestId; + // For HTTP/2 Http.Sys assigns each request a unique connection id for use with API calls, but the RawConnectionId represents the real connection. + UConnectionId = requestContext.ConnectionId; + RawConnectionId = requestContext.RawConnectionId; + SslStatus = requestContext.SslStatus; - RawUrl = requestContext.GetRawUrl()!; + KnownMethod = requestContext.VerbId; + Method = requestContext.GetVerb()!; - var cookedUrl = requestContext.GetCookedUrl(); - QueryString = cookedUrl.GetQueryString() ?? string.Empty; + RawUrl = requestContext.GetRawUrl()!; - var rawUrlInBytes = requestContext.GetRawUrlInBytes(); - var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes); + var cookedUrl = requestContext.GetCookedUrl(); + QueryString = cookedUrl.GetQueryString() ?? string.Empty; - PathBase = string.Empty; - Path = originalPath; - var prefix = requestContext.Server.Options.UrlPrefixes.GetPrefix((int)requestContext.UrlContext); + var rawUrlInBytes = requestContext.GetRawUrlInBytes(); + var originalPath = RequestUriBuilder.DecodeAndUnescapePath(rawUrlInBytes); - // 'OPTIONS * HTTP/1.1' - if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) - { PathBase = string.Empty; - Path = string.Empty; - } - // Prefix may be null if the requested has been transfered to our queue - else if (prefix is not null) - { - var pathBase = prefix.PathWithoutTrailingSlash; + Path = originalPath; + var prefix = requestContext.Server.Options.UrlPrefixes.GetPrefix((int)requestContext.UrlContext); - // url: /base/path, prefix: /base/, base: /base, path: /path - // url: /, prefix: /, base: , path: / - if (originalPath.Equals(pathBase, StringComparison.Ordinal)) - { - // Exact match, no need to preserve the casing - PathBase = pathBase; - Path = string.Empty; - } - else if (originalPath.Equals(pathBase, StringComparison.OrdinalIgnoreCase)) + // 'OPTIONS * HTTP/1.1' + if (KnownMethod == HttpApiTypes.HTTP_VERB.HttpVerbOPTIONS && string.Equals(RawUrl, "*", StringComparison.Ordinal)) { - // Preserve the user input casing - PathBase = originalPath; + PathBase = string.Empty; Path = string.Empty; } - else if (originalPath.StartsWith(prefix.Path, StringComparison.Ordinal)) - { - // Exact match, no need to preserve the casing - PathBase = pathBase; - Path = originalPath[pathBase.Length..]; - } - else if (originalPath.StartsWith(prefix.Path, StringComparison.OrdinalIgnoreCase)) + // Prefix may be null if the requested has been transferred to our queue + else if (prefix is not null) { - // Preserve the user input casing - PathBase = originalPath[..pathBase.Length]; - Path = originalPath[pathBase.Length..]; - } - else - { - // Http.Sys path base matching is based on the cooked url which applies some non-standard normalizations that we don't use - // like collapsing duplicate slashes "//", converting '\' to '/', and un-escaping "%2F" to '/'. Find the right split and - // ignore the normalizations. - var originalOffset = 0; - var baseOffset = 0; - while (originalOffset < originalPath.Length && baseOffset < pathBase.Length) + var pathBase = prefix.PathWithoutTrailingSlash; + + // url: /base/path, prefix: /base/, base: /base, path: /path + // url: /, prefix: /, base: , path: / + if (originalPath.Equals(pathBase, StringComparison.Ordinal)) { - var baseValue = pathBase[baseOffset]; - var offsetValue = originalPath[originalOffset]; - if (baseValue == offsetValue - || char.ToUpperInvariant(baseValue) == char.ToUpperInvariant(offsetValue)) - { - // case-insensitive match, continue - originalOffset++; - baseOffset++; - } - else if (baseValue == '/' && offsetValue == '\\') - { - // Http.Sys considers these equivalent - originalOffset++; - baseOffset++; - } - else if (baseValue == '/' && originalPath.AsSpan(originalOffset).StartsWith("%2F", StringComparison.OrdinalIgnoreCase)) - { - // Http.Sys un-escapes this - originalOffset += 3; - baseOffset++; - } - else if (baseOffset > 0 && pathBase[baseOffset - 1] == '/' - && (offsetValue == '/' || offsetValue == '\\')) - { - // Duplicate slash, skip - originalOffset++; - } - else if (baseOffset > 0 && pathBase[baseOffset - 1] == '/' - && originalPath.AsSpan(originalOffset).StartsWith("%2F", StringComparison.OrdinalIgnoreCase)) - { - // Duplicate slash equivalent, skip - originalOffset += 3; - } - else + // Exact match, no need to preserve the casing + PathBase = pathBase; + Path = string.Empty; + } + else if (originalPath.Equals(pathBase, StringComparison.OrdinalIgnoreCase)) + { + // Preserve the user input casing + PathBase = originalPath; + Path = string.Empty; + } + else if (originalPath.StartsWith(prefix.Path, StringComparison.Ordinal)) + { + // Exact match, no need to preserve the casing + PathBase = pathBase; + Path = originalPath[pathBase.Length..]; + } + else if (originalPath.StartsWith(prefix.Path, StringComparison.OrdinalIgnoreCase)) + { + // Preserve the user input casing + PathBase = originalPath[..pathBase.Length]; + Path = originalPath[pathBase.Length..]; + } + else + { + // Http.Sys path base matching is based on the cooked url which applies some non-standard normalizations that we don't use + // like collapsing duplicate slashes "//", converting '\' to '/', and un-escaping "%2F" to '/'. Find the right split and + // ignore the normalizations. + var originalOffset = 0; + var baseOffset = 0; + while (originalOffset < originalPath.Length && baseOffset < pathBase.Length) { - // Mismatch, fall back - // The failing test case here is "/base/call//../bat//path1//path2", reduced to "/base/call/bat//path1//path2", - // where http.sys collapses "//" before "../", but we do "../" first. We've lost the context that there were dot segments, - // or duplicate slashes, how do we figure out that "call/" can be eliminated? - originalOffset = 0; - break; + var baseValue = pathBase[baseOffset]; + var offsetValue = originalPath[originalOffset]; + if (baseValue == offsetValue + || char.ToUpperInvariant(baseValue) == char.ToUpperInvariant(offsetValue)) + { + // case-insensitive match, continue + originalOffset++; + baseOffset++; + } + else if (baseValue == '/' && offsetValue == '\\') + { + // Http.Sys considers these equivalent + originalOffset++; + baseOffset++; + } + else if (baseValue == '/' && originalPath.AsSpan(originalOffset).StartsWith("%2F", StringComparison.OrdinalIgnoreCase)) + { + // Http.Sys un-escapes this + originalOffset += 3; + baseOffset++; + } + else if (baseOffset > 0 && pathBase[baseOffset - 1] == '/' + && (offsetValue == '/' || offsetValue == '\\')) + { + // Duplicate slash, skip + originalOffset++; + } + else if (baseOffset > 0 && pathBase[baseOffset - 1] == '/' + && originalPath.AsSpan(originalOffset).StartsWith("%2F", StringComparison.OrdinalIgnoreCase)) + { + // Duplicate slash equivalent, skip + originalOffset += 3; + } + else + { + // Mismatch, fall back + // The failing test case here is "/base/call//../bat//path1//path2", reduced to "/base/call/bat//path1//path2", + // where http.sys collapses "//" before "../", but we do "../" first. We've lost the context that there were dot segments, + // or duplicate slashes, how do we figure out that "call/" can be eliminated? + originalOffset = 0; + break; + } } + PathBase = originalPath[..originalOffset]; + Path = originalPath[originalOffset..]; } - PathBase = originalPath[..originalOffset]; - Path = originalPath[originalOffset..]; } - } - else if (requestContext.Server.Options.UrlPrefixes.TryMatchLongestPrefix(IsHttps, cookedUrl.GetHost()!, originalPath, out var pathBase, out var path)) - { - PathBase = pathBase; - Path = path; - } + else if (requestContext.Server.Options.UrlPrefixes.TryMatchLongestPrefix(IsHttps, cookedUrl.GetHost()!, originalPath, out var pathBase, out var path)) + { + PathBase = pathBase; + Path = path; + } - ProtocolVersion = RequestContext.GetVersion(); + ProtocolVersion = RequestContext.GetVersion(); - Headers = new RequestHeaders(RequestContext); + Headers = new RequestHeaders(RequestContext); - User = RequestContext.GetUser(); + User = RequestContext.GetUser(); - SniHostName = string.Empty; - if (IsHttps) - { - GetTlsHandshakeResults(); - } + SniHostName = string.Empty; + if (IsHttps) + { + GetTlsHandshakeResults(); + } - // GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231 + // GetTlsTokenBindingInfo(); TODO: https://github.com/aspnet/HttpSysServer/issues/231 - // Finished directly accessing the HTTP_REQUEST structure. - RequestContext.ReleasePins(); - // TODO: Verbose log parameters + } + finally + { + // Finished directly accessing the HTTP_REQUEST structure. + RequestContext.ReleasePins(); + // TODO: Verbose log parameters + } RemoveContentLengthIfTransferEncodingContainsChunked(); } diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs index 2671bc71c8bb..e1931dc0fc6b 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs @@ -64,6 +64,7 @@ internal partial class RequestContext : private bool _bodyCompleted; private IHeaderDictionary _responseHeaders = default!; private IHeaderDictionary? _responseTrailers; + private ulong? _requestId; private Fields _initializedFields; @@ -98,11 +99,26 @@ private enum Fields TraceIdentifier = 0x200, } - protected internal void InitializeFeatures() + protected internal bool InitializeFeatures() { _initialized = true; - Request = new Request(this); + // Get the ID before creating the Request object as the Request ctor releases the native memory + // We want the ID in case request processing fails and we need the ID to cancel the native request + _requestId = RequestId; + try + { + Request = new Request(this); + } + catch (Exception ex) + { + Log.RequestParsingError(Logger, ex); + // Synchronously calls Http.Sys and tells it to send an http response + // No one has written to the response yet (haven't even created the response object below) + Server.SendError(_requestId.Value, StatusCodes.Status400BadRequest, authChallenges: null); + return false; + } + Response = new Response(this); _features = new FeatureCollection(new StandardFeatureCollection(this)); @@ -124,6 +140,7 @@ protected internal void InitializeFeatures() _responseStream = new ResponseStream(Response.Body, OnResponseStart); _responseHeaders = Response.Headers; + return true; } private bool IsNotInitialized(Fields field) diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.Log.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.Log.cs index 2c587b98a401..41b1cf480d5a 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.Log.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.Log.cs @@ -17,5 +17,8 @@ private static partial class Log [LoggerMessage(LoggerEventIds.ChannelBindingRetrieved, LogLevel.Debug, "Channel binding retrieved.", EventName = "ChannelBindingRetrieved")] public static partial void ChannelBindingRetrieved(ILogger logger); + + [LoggerMessage(LoggerEventIds.RequestParsingError, LogLevel.Debug, "Failed to parse request.", EventName = "RequestParsingError")] + public static partial void RequestParsingError(ILogger logger, Exception exception); } } diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs index 8da0c7a33e37..e9f277b6a990 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs @@ -187,9 +187,11 @@ public void Abort() _disconnectToken = new CancellationToken(canceled: true); } ForceCancelRequest(); - Request.Dispose(); + // Request and/or Response can be null (even though the property doesn't say it can) + // if the constructor throws (can happen for invalid path format) + Request?.Dispose(); // Only Abort, Response.Dispose() tries a graceful flush - Response.Abort(); + Response?.Abort(); } private static void Abort(object? state) @@ -208,15 +210,22 @@ internal void ForceCancelRequest() { try { + // Shouldn't be able to get here when this is null, but just in case we'll noop + if (_requestId is null) + { + return; + } + var statusCode = HttpApi.HttpCancelHttpRequest(Server.RequestQueue.Handle, - Request.RequestId, IntPtr.Zero); + _requestId.Value, default); // Either the connection has already dropped, or the last write is in progress. // The requestId becomes invalid as soon as the last Content-Length write starts. // The only way to cancel now is with CancelIoEx. if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_CONNECTION_INVALID) { - Response.CancelLastWrite(); + // Can be null if processing the request threw and the response object was never created. + Response?.CancelLastWrite(); } } catch (ObjectDisposedException) diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs index f661bf1fd018..2a1d06a06d26 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestContextOfT.cs @@ -27,7 +27,11 @@ public override async Task ExecuteAsync() try { - InitializeFeatures(); + if (!InitializeFeatures()) + { + Abort(); + return; + } if (messagePump.Stopping) { diff --git a/src/Servers/HttpSys/test/FunctionalTests/Listener/RequestTests.cs b/src/Servers/HttpSys/test/FunctionalTests/Listener/RequestTests.cs index 79e1c361380d..0cd4b08086a6 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/Listener/RequestTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/Listener/RequestTests.cs @@ -1,15 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.IO; -using System.Net.Http; using System.Net.Sockets; using System.Text; -using System.Threading.Tasks; using Microsoft.AspNetCore.Testing; -using Microsoft.Extensions.Logging; -using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys.Listener; @@ -142,6 +136,7 @@ public async Task Request_OverlongUTF8Path(string requestPath, string expectedPa [InlineData("/", "/", "", "/")] [InlineData("/base", "/base", "/base", "")] [InlineData("/base", "/baSe", "/baSe", "")] + [InlineData("/base", "/baSe/", "/baSe", "/")] [InlineData("/base", "/base/path", "/base", "/path")] [InlineData("/base", "///base/path1/path2", "///base", "/path1/path2")] [InlineData("/base/ball", @"/baSe\ball//path1//path2", @"/baSe\ball", "//path1//path2")] diff --git a/src/Servers/HttpSys/test/FunctionalTests/RequestTests.cs b/src/Servers/HttpSys/test/FunctionalTests/RequestTests.cs index 51eed9b46948..e3f86544e89f 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/RequestTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/RequestTests.cs @@ -1,24 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Globalization; -using System.IO; using System.Net; using System.Net.Http; using System.Net.Sockets; using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.HttpSys.Internal; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Xunit; namespace Microsoft.AspNetCore.Server.HttpSys; @@ -368,6 +361,43 @@ public async Task Request_UrlUnescaping() } } + [Fact] + public async Task Latin1UrlIsRejected() + { + string root; + using (var server = Utilities.CreateHttpServerReturnRoot("/", out root, httpContext => + { + Assert.Fail("Request should not reach here"); + return Task.FromResult(0); + }, LoggerFactory)) + { + var uri = new Uri(root); + StringBuilder builder = new StringBuilder(); + builder.AppendLine(FormattableString.Invariant($"GET /a HTTP/1.1")); + builder.AppendLine("Connection: close"); + builder.Append("HOST: "); + builder.AppendLine(uri.Authority); + builder.AppendLine(); + byte[] request = Encoding.ASCII.GetBytes(builder.ToString()); + // Replace the 'a' in the path with a Latin1 value + request[5] = 0xe1; + + using (var socket = new Socket(SocketType.Stream, ProtocolType.Tcp)) + { + socket.Connect(uri.Host, uri.Port); + socket.Send(request); + var response = new byte[12]; + await Task.Run(() => socket.Receive(response)); + + var statusCode = Encoding.UTF8.GetString(response).Substring(9); + Assert.Equal("400", statusCode); + } + } + + var errorLogs = TestSink.Writes.Where(w => w.LogLevel >= LogLevel.Error).Select(w => w.Exception); + Assert.False(errorLogs.Any(), string.Join(" ", errorLogs)); + } + [ConditionalFact] public async Task Request_WithDoubleSlashes_LeftAlone() { diff --git a/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs b/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs index e0947d2099e6..821d1e86ef18 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/ResponseCachingTests.cs @@ -22,8 +22,23 @@ public class ResponseCachingTests : LoggedTest public ResponseCachingTests() { - _absoluteFilePath = Path.Combine(Directory.GetCurrentDirectory(), "Microsoft.AspNetCore.Server.HttpSys.dll"); - _fileLength = new FileInfo(_absoluteFilePath).Length; + _absoluteFilePath = Path.Combine(Directory.GetCurrentDirectory(), Path.GetRandomFileName()); + using var file = File.Create(_absoluteFilePath); + // HttpSys will cache responses up to ~260k, keep this value below that + // 30k is an arbitrary choice + file.Write(new byte[30000]); + _fileLength = 30000; + } + + public override void Dispose() + { + try + { + File.Delete(_absoluteFilePath); + } + catch { } + + base.Dispose(); } [ConditionalFact] diff --git a/src/Servers/HttpSys/test/FunctionalTests/ResponseSendFileTests.cs b/src/Servers/HttpSys/test/FunctionalTests/ResponseSendFileTests.cs index 7e0c03f9c37f..ce82eddfc5bb 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/ResponseSendFileTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/ResponseSendFileTests.cs @@ -30,6 +30,7 @@ public ResponseSendFileTests() AbsoluteFilePath = Directory.GetFiles(Directory.GetCurrentDirectory()).First(); RelativeFilePath = Path.GetFileName(AbsoluteFilePath); FileLength = new FileInfo(AbsoluteFilePath).Length; + Assert.True(FileLength > 0, "FileLength is 0"); } [ConditionalFact] diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs index 96e5630c43f1..491eb5adb974 100644 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ b/src/Shared/CertificateGeneration/CertificateManager.cs @@ -328,6 +328,7 @@ public EnsureCertificateResult EnsureAspNetCoreHttpsDevelopmentCertificate( var exportDir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(exportDir) && !Directory.Exists(exportDir)) { + result = EnsureCertificateResult.ErrorExportingTheCertificateToNonExistentDirectory; throw new InvalidOperationException($"The directory '{exportDir}' does not exist. Choose permissions carefully when creating it."); } diff --git a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs index cb6b7e145428..5c28eaca3063 100644 --- a/src/Shared/CertificateGeneration/EnsureCertificateResult.cs +++ b/src/Shared/CertificateGeneration/EnsureCertificateResult.cs @@ -10,6 +10,7 @@ internal enum EnsureCertificateResult ErrorCreatingTheCertificate, ErrorSavingTheCertificateIntoTheCurrentUserPersonalStore, ErrorExportingTheCertificate, + ErrorExportingTheCertificateToNonExistentDirectory, FailedToTrustTheCertificate, PartiallyFailedToTrustTheCertificate, UserCancelledTrustStep, diff --git a/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs b/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs index 4edd3897b652..f637ecbdc61a 100644 --- a/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs +++ b/src/Tools/FirstRunCertGenerator/test/CertificateManagerTests.cs @@ -373,7 +373,7 @@ public void EnsureCreateHttpsCertificate_CannotExportToNonExistentDirectory() .EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), Path.Combine("NoSuchDirectory", CertificateName)); // Assert - Assert.Equal(EnsureCertificateResult.ErrorExportingTheCertificate, result); + Assert.Equal(EnsureCertificateResult.ErrorExportingTheCertificateToNonExistentDirectory, result); } [Fact] diff --git a/src/Tools/dotnet-dev-certs/src/Program.cs b/src/Tools/dotnet-dev-certs/src/Program.cs index 9f5407d9d3e4..9b195dbcd9fa 100644 --- a/src/Tools/dotnet-dev-certs/src/Program.cs +++ b/src/Tools/dotnet-dev-certs/src/Program.cs @@ -425,6 +425,10 @@ private static int EnsureHttpsCertificate(CommandOption exportPath, CommandOptio case EnsureCertificateResult.ErrorExportingTheCertificate: reporter.Warn("There was an error exporting the HTTPS developer certificate to a file."); return ErrorExportingTheCertificate; + case EnsureCertificateResult.ErrorExportingTheCertificateToNonExistentDirectory: + // A distinct warning is useful, but a distinct error code is probably not. + reporter.Warn("There was an error exporting the HTTPS developer certificate to a file. Please create the target directory before exporting. Choose permissions carefully when creating it."); + return ErrorExportingTheCertificate; case EnsureCertificateResult.PartiallyFailedToTrustTheCertificate: // A distinct warning is useful, but a distinct error code is probably not. reporter.Warn("There was an error trusting the HTTPS developer certificate. It will be trusted by some clients but not by others."); diff --git a/src/submodules/MessagePack-CSharp b/src/submodules/MessagePack-CSharp index ecc4e18ad7a0..95119056ee8f 160000 --- a/src/submodules/MessagePack-CSharp +++ b/src/submodules/MessagePack-CSharp @@ -1 +1 @@ -Subproject commit ecc4e18ad7a0c7db51cd7e3d2997a291ed01444d +Subproject commit 95119056ee8f4da1714b055a4f16893afaa73af7 diff --git a/src/submodules/googletest b/src/submodules/googletest index ff233bdd4cac..6dae7eb4a5c3 160000 --- a/src/submodules/googletest +++ b/src/submodules/googletest @@ -1 +1 @@ -Subproject commit ff233bdd4cac0a0bf6e5cd45bda3406814cb2796 +Subproject commit 6dae7eb4a5c3a169f3e298392bff4680224aa94a