From 0f2ffdc03edcd832e6f83a9deb73e2d57c0b8dbf Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 9 Oct 2024 10:01:33 +0800 Subject: [PATCH 01/15] Fix UpdateBalancingState not called when address attributes are modified (#2553) --- examples/Container/Backend/Dockerfile | 4 +- examples/Container/Frontend/Dockerfile | 4 +- global.json | 2 +- .../Balancer/SubchannelsLoadBalancer.cs | 5 +- .../Balancer/SubchannelsLoadBalancerTests.cs | 199 ++++++++++++++++++ .../InteropTestsGrpcWebWebsite/Dockerfile | 4 +- testassets/InteropTestsWebsite/Dockerfile | 4 +- 7 files changed, 212 insertions(+), 10 deletions(-) create mode 100644 test/Grpc.Net.Client.Tests/Balancer/SubchannelsLoadBalancerTests.cs diff --git a/examples/Container/Backend/Dockerfile b/examples/Container/Backend/Dockerfile index 78b8007d5..7c0429870 100644 --- a/examples/Container/Backend/Dockerfile +++ b/examples/Container/Backend/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/nightly/sdk:9.0-preview AS build-env +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /app # Copy everything @@ -8,7 +8,7 @@ RUN dotnet restore examples/Container/Backend RUN dotnet publish examples/Container/Backend -c Release -o out # Build runtime image -FROM mcr.microsoft.com/dotnet/nightly/aspnet:9.0-preview +FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "Backend.dll"] \ No newline at end of file diff --git a/examples/Container/Frontend/Dockerfile b/examples/Container/Frontend/Dockerfile index 23403110c..017712b23 100644 --- a/examples/Container/Frontend/Dockerfile +++ b/examples/Container/Frontend/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/nightly/sdk:9.0-preview AS build-env +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /app # Copy everything @@ -8,7 +8,7 @@ RUN dotnet restore examples/Container/Frontend RUN dotnet publish examples/Container/Frontend -c Release -o out # Build runtime image -FROM mcr.microsoft.com/dotnet/nightly/aspnet:9.0-preview +FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "Frontend.dll"] \ No newline at end of file diff --git a/global.json b/global.json index 67d91a4f9..0344c423c 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100-preview.7.24407.12", + "version": "9.0.100-rc.2.24474.11", "rollForward": "latestFeature" } } diff --git a/src/Grpc.Net.Client/Balancer/SubchannelsLoadBalancer.cs b/src/Grpc.Net.Client/Balancer/SubchannelsLoadBalancer.cs index 5f27c347b..73b88f7e1 100644 --- a/src/Grpc.Net.Client/Balancer/SubchannelsLoadBalancer.cs +++ b/src/Grpc.Net.Client/Balancer/SubchannelsLoadBalancer.cs @@ -122,6 +122,7 @@ public override void UpdateChannelState(ChannelState state) var allUpdatedSubchannels = new List(); var newSubchannels = new List(); + var hasModifiedSubchannels = false; var currentSubchannels = _addressSubchannels.ToList(); // The state's addresses is the new authoritative list of addresses. @@ -150,6 +151,8 @@ public override void UpdateChannelState(ChannelState state) address, newOrCurrentSubchannel.LastKnownState); newOrCurrentSubchannel.Subchannel.UpdateAddresses(new[] { address }); + + hasModifiedSubchannels = true; } SubchannelLog.SubchannelPreserved(_logger, newOrCurrentSubchannel.Subchannel.Id, address); @@ -171,7 +174,7 @@ public override void UpdateChannelState(ChannelState state) // This can all be removed. var removedSubConnections = currentSubchannels; - if (removedSubConnections.Count == 0 && newSubchannels.Count == 0) + if (removedSubConnections.Count == 0 && newSubchannels.Count == 0 && !hasModifiedSubchannels) { SubchannelsLoadBalancerLog.ConnectionsUnchanged(_logger); return; diff --git a/test/Grpc.Net.Client.Tests/Balancer/SubchannelsLoadBalancerTests.cs b/test/Grpc.Net.Client.Tests/Balancer/SubchannelsLoadBalancerTests.cs new file mode 100644 index 000000000..533ce0e3f --- /dev/null +++ b/test/Grpc.Net.Client.Tests/Balancer/SubchannelsLoadBalancerTests.cs @@ -0,0 +1,199 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +#if SUPPORT_LOAD_BALANCING +using System.Net; +using Grpc.Core; +using Grpc.Net.Client.Balancer; +using Grpc.Net.Client.Balancer.Internal; +using Grpc.Tests.Shared; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Grpc.Net.Client.Tests.Infrastructure.Balancer; + +[TestFixture] +public class SubchannelsLoadBalancerTests +{ + [Test] + public void UpdateChannelState_AddressMatchAndAttributesDifferent_UpdateState() + { + // Arrange + const string host1 = "127.0.0.1"; + const string host2 = "127.0.0.2"; + const int port = 80; + + const string attributeKey = "key1"; + + var controller = new CustomChannelControlHelper(); + var balancer = new CustomBalancer(controller, NullLoggerFactory.Instance); + + // create 2 addresses with some attributes + var address1 = new BalancerAddress(host1, port); + address1.Attributes.TryAdd(attributeKey, 20); // <-- difference + + var address2 = new BalancerAddress(host2, port); + address2.Attributes.TryAdd(attributeKey, 80); // <-- difference + + var state1 = new ChannelState( + status: new Status(), + addresses: [address1, address2], + loadBalancingConfig: null, + attributes: new BalancerAttributes()); + + // create 2 addresses with the same hosts and ports as previous but with other attribute values + var address3 = new BalancerAddress(host1, port); + address3.Attributes.TryAdd(attributeKey, 40); // <-- difference + + var address4 = new BalancerAddress(host2, port); + address4.Attributes.TryAdd(attributeKey, 60); // <-- difference + + var state2 = new ChannelState( + status: new Status(), + addresses: [address3, address4], + loadBalancingConfig: null, + attributes: new BalancerAttributes()); + + // Act + // first update with `address1` and `address2` + balancer.UpdateChannelState(state1); + + // remember count of `IChannelControlHelper.UpdateState()` calls + var updateStateCallsCount1 = controller.UpdateStateCallsCount; + + // second update with `address3` and `address4` + // which differs from `address1` and `address2` _only_ in attributes values + balancer.UpdateChannelState(state2); + + // get count of `IChannelControlHelper.UpdateState()` calls after second update + var updateStateCallsCount2 = controller.UpdateStateCallsCount; + + // Assert + Assert.True( + updateStateCallsCount2 > updateStateCallsCount1, + "`IChannelControlHelper.UpdateState()` was not called from `SubchannelsLoadBalancer.UpdateChannelState()`"); + } +} + +file class CustomBalancer( + IChannelControlHelper controller, + ILoggerFactory loggerFactory) + : SubchannelsLoadBalancer(controller, loggerFactory) +{ + protected override SubchannelPicker CreatePicker(IReadOnlyList readySubchannels) + { + return new CustomPicker(readySubchannels); + } +} + +file class CustomPicker : SubchannelPicker +{ + private IReadOnlyList readySubchannels; + + public CustomPicker(IReadOnlyList readySubchannels) + { + this.readySubchannels = readySubchannels; + } + + public override PickResult Pick(PickContext context) + { + return PickResult.ForSubchannel(readySubchannels[0]); + } +} + +file class CustomChannelControlHelper : IChannelControlHelper +{ + public int UpdateStateCallsCount { get; private set; } + + public Subchannel CreateSubchannel(SubchannelOptions options) + { + var subchannelTransportFactory = new CustomSubchannelTransportFactory(); + + var manager = new ConnectionManager( + new CustomResolver(), + true, + NullLoggerFactory.Instance, + new CustomBackoffPolicyFactory(), + subchannelTransportFactory, + []); + + return ((IChannelControlHelper)manager).CreateSubchannel(options); + } + + public void UpdateState(BalancerState state) + { + UpdateStateCallsCount++; + } + + public void RefreshResolver() { } +} + +file class CustomResolver() : PollingResolver(NullLoggerFactory.Instance) +{ + protected override Task ResolveAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +file class CustomBackoffPolicyFactory : IBackoffPolicyFactory +{ + public IBackoffPolicy Create() + { + return new CustomBackoffPolicy(); + } +} + +file class CustomBackoffPolicy : IBackoffPolicy +{ + public TimeSpan NextBackoff() + { + return TimeSpan.Zero; + } +} + +file class CustomSubchannelTransportFactory : ISubchannelTransportFactory +{ + public ISubchannelTransport Create(Subchannel subchannel) + { + return new CustomSubchannelTransport(); + } +} + +file class CustomSubchannelTransport : ISubchannelTransport +{ + public void Dispose() { } + + public DnsEndPoint? CurrentEndPoint { get; } + public TimeSpan? ConnectTimeout { get; } + public TransportStatus TransportStatus { get; } + + public ValueTask GetStreamAsync(DnsEndPoint endPoint, CancellationToken cancellationToken) + { + return ValueTask.FromResult(new MemoryStream()); + } + + public ValueTask TryConnectAsync(ConnectContext context, int attempt) + { + return ValueTask.FromResult(ConnectResult.Success); + } + + public void Disconnect() { } +} + +#endif diff --git a/testassets/InteropTestsGrpcWebWebsite/Dockerfile b/testassets/InteropTestsGrpcWebWebsite/Dockerfile index c8ecbf5c2..9a71faacf 100644 --- a/testassets/InteropTestsGrpcWebWebsite/Dockerfile +++ b/testassets/InteropTestsGrpcWebWebsite/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/nightly/sdk:9.0-preview AS build-env +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /app # Copy everything @@ -8,7 +8,7 @@ RUN dotnet restore testassets/InteropTestsGrpcWebWebsite RUN dotnet publish testassets/InteropTestsGrpcWebWebsite -c Release -o out # Build runtime image -FROM mcr.microsoft.com/dotnet/nightly/aspnet:9.0-preview +FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "InteropTestsGrpcWebWebsite.dll", "--urls", "http://+:80"] diff --git a/testassets/InteropTestsWebsite/Dockerfile b/testassets/InteropTestsWebsite/Dockerfile index 16e916257..59cdeb214 100644 --- a/testassets/InteropTestsWebsite/Dockerfile +++ b/testassets/InteropTestsWebsite/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/nightly/sdk:9.0-preview AS build-env +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build-env WORKDIR /app # Copy everything @@ -8,7 +8,7 @@ RUN dotnet restore testassets/InteropTestsWebsite RUN dotnet publish testassets/InteropTestsWebsite --framework net9.0 -c Release -o out -p:LatestFramework=true # Build runtime image -FROM mcr.microsoft.com/dotnet/nightly/aspnet:9.0-preview +FROM mcr.microsoft.com/dotnet/aspnet:9.0 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "InteropTestsWebsite.dll", "--port_http1", "80"] \ No newline at end of file From b88713e83f3c75282678fdc15bf830fa78bfff2c Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 9 Oct 2024 13:52:55 +0800 Subject: [PATCH 02/15] Update Grpc.Tools to 2.67.0 (#2554) --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index e12fc9bfb..9819d45ec 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,7 +33,7 @@ - + From 5fffc489f779a897b52679dfec1df02124268e83 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 11 Oct 2024 06:51:57 +0800 Subject: [PATCH 03/15] Fix System.Text.Json vulnerability warning (#2556) --- Directory.Packages.props | 1 + src/dotnet-grpc/dotnet-grpc.csproj | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9819d45ec..3e1741614 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -82,6 +82,7 @@ + diff --git a/src/dotnet-grpc/dotnet-grpc.csproj b/src/dotnet-grpc/dotnet-grpc.csproj index 3b246baeb..c48025ae9 100644 --- a/src/dotnet-grpc/dotnet-grpc.csproj +++ b/src/dotnet-grpc/dotnet-grpc.csproj @@ -1,4 +1,4 @@ - + Command line tool for gRPC projects gRPC RPC CLI @@ -26,6 +26,7 @@ + From 1732f28dc6ad74da33b2758f11cbdfabb2dcbc86 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 24 Oct 2024 04:06:42 +0800 Subject: [PATCH 04/15] Update package dependencies to 9.0 RC2 (#2560) --- Directory.Packages.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 3e1741614..9a3224d64 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ true - 9.0.0-preview.5.24306.11 + 9.0.0-rc.2.24474.3 8.0.6 7.0.5 6.0.33 @@ -9,7 +9,7 @@ 1.6.0 1.8.1 1.8.0-beta.1 - 9.0.0-preview.5.24306.7 + 9.0.0-rc.2.24473.5 6.0.0 From dacb7b41ea2fb42f2c0756e3d3a4ad4dcfecdc7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:34:00 +0800 Subject: [PATCH 05/15] Bump elliptic from 6.5.7 to 6.6.0 in /examples/Spar/Server/ClientApp (#2567) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.7 to 6.6.0. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.7...v6.6.0) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/Spar/Server/ClientApp/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/Spar/Server/ClientApp/package-lock.json b/examples/Spar/Server/ClientApp/package-lock.json index 6215cf0cb..fdcf0ae59 100644 --- a/examples/Spar/Server/ClientApp/package-lock.json +++ b/examples/Spar/Server/ClientApp/package-lock.json @@ -3783,9 +3783,9 @@ "dev": true }, "node_modules/elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dev": true, "dependencies": { "bn.js": "^4.11.9", @@ -12297,9 +12297,9 @@ "dev": true }, "elliptic": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", - "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz", + "integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==", "dev": true, "requires": { "bn.js": "^4.11.9", From 5a58c24efc1d0b7c5ff88e7b0582ea891b90b17f Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 15 Nov 2024 06:47:13 +0800 Subject: [PATCH 06/15] Update to .NET 9 RTM (#2571) --- Directory.Packages.props | 6 +++--- global.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9a3224d64..1c82cda73 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,7 +1,7 @@ true - 9.0.0-rc.2.24474.3 + 9.0.0 8.0.6 7.0.5 6.0.33 @@ -9,7 +9,7 @@ 1.6.0 1.8.1 1.8.0-beta.1 - 9.0.0-rc.2.24473.5 + 9.0.0 6.0.0 @@ -65,7 +65,7 @@ - + diff --git a/global.json b/global.json index 0344c423c..cdbb589ed 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100-rc.2.24474.11", + "version": "9.0.100", "rollForward": "latestFeature" } } From b6cfbd05e9ccb47ef64c2b8a2f88260fab46470e Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Mon, 9 Dec 2024 08:35:21 +0800 Subject: [PATCH 07/15] update ArgumentNullException.ThrowIfNull usage (#2563) --- src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs b/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs index fd0129fb3..904072415 100644 --- a/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs +++ b/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs @@ -13,7 +13,7 @@ internal static partial class ArgumentNullThrowHelper /// The name of the parameter with which corresponds. public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression("argument")] string? paramName = null) { -#if !NET7_0_OR_GREATER +#if !NET6_0_OR_GREATER if (argument is null) { Throw(paramName); @@ -23,7 +23,7 @@ public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpres #endif } -#if !NET7_0_OR_GREATER +#if !NET6_0_OR_GREATER [DoesNotReturn] internal static void Throw(string? paramName) => throw new ArgumentNullException(paramName); From 3bf19a113cbdb09012c14cc253f146ec822de11c Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Mon, 9 Dec 2024 08:36:19 +0800 Subject: [PATCH 08/15] use nameof for CallerArgumentExpression (#2562) --- src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs b/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs index 904072415..21b3466ed 100644 --- a/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs +++ b/src/Shared/ThrowHelpers/ArgumentNullThrowHelper.cs @@ -11,7 +11,7 @@ internal static partial class ArgumentNullThrowHelper /// Throws an if is null. /// The reference type argument to validate as non-null. /// The name of the parameter with which corresponds. - public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression("argument")] string? paramName = null) + public static void ThrowIfNull([NotNull] object? argument, [CallerArgumentExpression(nameof(argument))] string? paramName = null) { #if !NET6_0_OR_GREATER if (argument is null) From 651a0cb64a6b0971058078dfce6fc2c777f50ade Mon Sep 17 00:00:00 2001 From: Henrik Date: Mon, 9 Dec 2024 01:38:01 +0100 Subject: [PATCH 09/15] Correctness: Make some private & internal classes sealed where possible (#2559) --- perf/benchmarkapps/Shared/BenchmarkServiceImpl.cs | 4 ++-- .../CallHandlers/ClientStreamingServerCallHandler.cs | 2 +- .../CallHandlers/DuplexStreamingServerCallHandler.cs | 2 +- .../CallHandlers/ServerStreamingServerCallHandler.cs | 2 +- .../Internal/CallHandlers/UnaryServerCallHandler.cs | 2 +- src/Grpc.AspNetCore.Server/Internal/GrpcMarkerService.cs | 2 +- src/Grpc.AspNetCore.Server/Internal/GrpcServerBuilder.cs | 2 +- .../Internal/GrpcServiceOptionsSetup.cs | 4 ++-- .../Internal/HttpContextStreamReader.cs | 2 +- .../Internal/HttpContextStreamWriter.cs | 2 +- .../Internal/ReadOnlySequenceStream.cs | 2 +- .../Internal/ServerCallHandlerFactory.cs | 2 +- src/Grpc.AspNetCore.Server/Internal/SystemClock.cs | 2 +- .../Model/Internal/BinderServiceModelProvider.cs | 2 +- src/Grpc.AspNetCore.Server/Model/Internal/MethodModel.cs | 2 +- .../Model/Internal/ProviderServiceBinder.cs | 2 +- .../Model/Internal/ServiceMethodsRegistry.cs | 2 +- .../Model/Internal/ServiceRouteBuilder.cs | 2 +- src/Grpc.AspNetCore.Web/Internal/Base64PipeReader.cs | 2 +- src/Grpc.AspNetCore.Web/Internal/Base64PipeWriter.cs | 4 ++-- src/Grpc.AspNetCore.Web/Internal/GrpcWebFeature.cs | 2 +- src/Grpc.AspNetCore.Web/Internal/MemorySegment.cs | 2 +- src/Grpc.Core.Api/CallCredentials.cs | 4 ++-- src/Grpc.Core.Api/ClientBase.cs | 4 ++-- src/Grpc.Core.Api/Interceptors/CallInvokerExtensions.cs | 2 +- .../Interceptors/InterceptingCallInvoker.cs | 3 +-- src/Grpc.Core.Api/Internal/UnimplementedCallInvoker.cs | 5 +---- src/Grpc.Net.Client.Web/Internal/Base64RequestStream.cs | 2 +- src/Grpc.Net.Client.Web/Internal/Base64ResponseStream.cs | 2 +- .../Internal/GrpcWebRequestContent.cs | 2 +- .../Internal/GrpcWebResponseContent.cs | 2 +- .../Internal/GrpcWebResponseStream.cs | 2 +- src/Grpc.Net.Client.Web/Internal/OperatingSystem.cs | 2 +- .../Balancer/Internal/BalancerAddressEqualityComparer.cs | 2 +- .../Balancer/Internal/BalancerHttpHandler.cs | 9 ++------- src/Grpc.Net.Client/Balancer/Internal/ErrorPicker.cs | 3 +-- .../Balancer/Internal/PassiveSubchannelTransport.cs | 6 +----- .../Internal/SocketConnectivitySubchannelTransport.cs | 4 ++-- src/Grpc.Net.Client/Balancer/PickFirstBalancer.cs | 4 +--- src/Grpc.Net.Client/Internal/GrpcCallScope.cs | 2 +- src/Grpc.Net.Client/Internal/GrpcMethodInfo.cs | 8 ++++---- src/Grpc.Net.Client/Internal/Http/PushStreamContent.cs | 2 +- src/Grpc.Net.Client/Internal/Http/PushUnaryContent.cs | 2 +- src/Grpc.Net.Client/Internal/Http/WinHttpUnaryContent.cs | 2 +- .../Internal/HttpContentClientStreamReader.cs | 2 +- .../Internal/HttpContentClientStreamWriter.cs | 2 +- .../Internal/Retry/ChannelRetryThrottling.cs | 2 +- .../Internal/Retry/RetryCallBaseClientStreamReader.cs | 2 +- .../Internal/Retry/RetryCallBaseClientStreamWriter.cs | 2 +- src/Shared/Server/InterceptorPipelineBuilder.cs | 4 ++-- src/Shared/TrailingHeadersHelpers.cs | 2 +- src/dotnet-grpc/Commands/AddFileCommand.cs | 5 +---- src/dotnet-grpc/Commands/AddUrlCommand.cs | 3 +-- src/dotnet-grpc/Commands/ListCommand.cs | 3 +-- src/dotnet-grpc/Commands/RefreshCommand.cs | 4 +--- src/dotnet-grpc/Commands/RemoveCommand.cs | 5 +---- src/dotnet-grpc/Internal/CLIToolException.cs | 2 +- src/dotnet-grpc/Internal/GrpcDependencyAttribute.cs | 2 +- 58 files changed, 69 insertions(+), 95 deletions(-) diff --git a/perf/benchmarkapps/Shared/BenchmarkServiceImpl.cs b/perf/benchmarkapps/Shared/BenchmarkServiceImpl.cs index 83c03970f..bc8ae2bb9 100644 --- a/perf/benchmarkapps/Shared/BenchmarkServiceImpl.cs +++ b/perf/benchmarkapps/Shared/BenchmarkServiceImpl.cs @@ -20,7 +20,7 @@ using Grpc.Core; using Grpc.Testing; -class BenchmarkServiceImpl : BenchmarkService.BenchmarkServiceBase +sealed class BenchmarkServiceImpl : BenchmarkService.BenchmarkServiceBase { #if CLIENT_CERTIFICATE_AUTHENTICATION [Microsoft.AspNetCore.Authorization.Authorize] @@ -98,4 +98,4 @@ public static SimpleResponse CreateResponse(SimpleRequest request) var payload = new Payload { Body = body }; return new SimpleResponse { Payload = payload }; } -} \ No newline at end of file +} diff --git a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ClientStreamingServerCallHandler.cs b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ClientStreamingServerCallHandler.cs index f6d9bb155..99437edc9 100644 --- a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ClientStreamingServerCallHandler.cs +++ b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ClientStreamingServerCallHandler.cs @@ -24,7 +24,7 @@ namespace Grpc.AspNetCore.Server.Internal.CallHandlers; -internal class ClientStreamingServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase +internal sealed class ClientStreamingServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase where TRequest : class where TResponse : class where TService : class diff --git a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/DuplexStreamingServerCallHandler.cs b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/DuplexStreamingServerCallHandler.cs index cc5318511..2649b1fb5 100644 --- a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/DuplexStreamingServerCallHandler.cs +++ b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/DuplexStreamingServerCallHandler.cs @@ -23,7 +23,7 @@ namespace Grpc.AspNetCore.Server.Internal.CallHandlers; -internal class DuplexStreamingServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase +internal sealed class DuplexStreamingServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase where TRequest : class where TResponse : class where TService : class diff --git a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ServerStreamingServerCallHandler.cs b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ServerStreamingServerCallHandler.cs index 404822f98..da49752c9 100644 --- a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ServerStreamingServerCallHandler.cs +++ b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/ServerStreamingServerCallHandler.cs @@ -23,7 +23,7 @@ namespace Grpc.AspNetCore.Server.Internal.CallHandlers; -internal class ServerStreamingServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase +internal sealed class ServerStreamingServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase where TRequest : class where TResponse : class where TService : class diff --git a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/UnaryServerCallHandler.cs b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/UnaryServerCallHandler.cs index 499dc6491..f0d3047cd 100644 --- a/src/Grpc.AspNetCore.Server/Internal/CallHandlers/UnaryServerCallHandler.cs +++ b/src/Grpc.AspNetCore.Server/Internal/CallHandlers/UnaryServerCallHandler.cs @@ -24,7 +24,7 @@ namespace Grpc.AspNetCore.Server.Internal.CallHandlers; -internal class UnaryServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase +internal sealed class UnaryServerCallHandler<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService, TRequest, TResponse> : ServerCallHandlerBase where TRequest : class where TResponse : class where TService : class diff --git a/src/Grpc.AspNetCore.Server/Internal/GrpcMarkerService.cs b/src/Grpc.AspNetCore.Server/Internal/GrpcMarkerService.cs index 0b75f7e7f..635b74ac4 100644 --- a/src/Grpc.AspNetCore.Server/Internal/GrpcMarkerService.cs +++ b/src/Grpc.AspNetCore.Server/Internal/GrpcMarkerService.cs @@ -24,6 +24,6 @@ namespace Grpc.AspNetCore.Server.Internal; /// A marker class used to determine if all the required gRPC services were added /// to the . /// -internal class GrpcMarkerService +internal sealed class GrpcMarkerService { } diff --git a/src/Grpc.AspNetCore.Server/Internal/GrpcServerBuilder.cs b/src/Grpc.AspNetCore.Server/Internal/GrpcServerBuilder.cs index 465c83b05..1704becc7 100644 --- a/src/Grpc.AspNetCore.Server/Internal/GrpcServerBuilder.cs +++ b/src/Grpc.AspNetCore.Server/Internal/GrpcServerBuilder.cs @@ -20,7 +20,7 @@ namespace Grpc.AspNetCore.Server.Internal; -internal class GrpcServerBuilder : IGrpcServerBuilder +internal sealed class GrpcServerBuilder : IGrpcServerBuilder { public IServiceCollection Services { get; } diff --git a/src/Grpc.AspNetCore.Server/Internal/GrpcServiceOptionsSetup.cs b/src/Grpc.AspNetCore.Server/Internal/GrpcServiceOptionsSetup.cs index f2fad234e..f6a9193af 100644 --- a/src/Grpc.AspNetCore.Server/Internal/GrpcServiceOptionsSetup.cs +++ b/src/Grpc.AspNetCore.Server/Internal/GrpcServiceOptionsSetup.cs @@ -22,7 +22,7 @@ namespace Grpc.AspNetCore.Server.Internal; -internal class GrpcServiceOptionsSetup : IConfigureOptions +internal sealed class GrpcServiceOptionsSetup : IConfigureOptions { // Default to no send limit and 4mb receive limit. // Matches the gRPC C impl defaults @@ -45,7 +45,7 @@ public void Configure(GrpcServiceOptions options) } } -internal class GrpcServiceOptionsSetup : IConfigureOptions> where TService : class +internal sealed class GrpcServiceOptionsSetup : IConfigureOptions> where TService : class { private readonly GrpcServiceOptions _options; diff --git a/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamReader.cs b/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamReader.cs index 528aeed87..169a460d5 100644 --- a/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamReader.cs +++ b/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamReader.cs @@ -26,7 +26,7 @@ namespace Grpc.AspNetCore.Server.Internal; [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(HttpContextStreamReader<>.HttpContextStreamReaderDebugView))] -internal class HttpContextStreamReader : IAsyncStreamReader where TRequest : class +internal sealed class HttpContextStreamReader : IAsyncStreamReader where TRequest : class { private readonly HttpContextServerCallContext _serverCallContext; private readonly Func _deserializer; diff --git a/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamWriter.cs b/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamWriter.cs index 8a96d9fa1..d69b8e7e6 100644 --- a/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamWriter.cs +++ b/src/Grpc.AspNetCore.Server/Internal/HttpContextStreamWriter.cs @@ -26,7 +26,7 @@ namespace Grpc.AspNetCore.Server.Internal; [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(HttpContextStreamWriter<>.HttpContextStreamWriterDebugView))] -internal class HttpContextStreamWriter : IServerStreamWriter +internal sealed class HttpContextStreamWriter : IServerStreamWriter where TResponse : class { private readonly HttpContextServerCallContext _context; diff --git a/src/Grpc.AspNetCore.Server/Internal/ReadOnlySequenceStream.cs b/src/Grpc.AspNetCore.Server/Internal/ReadOnlySequenceStream.cs index bd196e9f7..36f16774a 100644 --- a/src/Grpc.AspNetCore.Server/Internal/ReadOnlySequenceStream.cs +++ b/src/Grpc.AspNetCore.Server/Internal/ReadOnlySequenceStream.cs @@ -21,7 +21,7 @@ namespace Grpc.AspNetCore.Server.Internal; // Potentially remove in the future when https://github.com/dotnet/corefx/issues/31804 is implemented -internal class ReadOnlySequenceStream : Stream +internal sealed class ReadOnlySequenceStream : Stream { private static readonly Task TaskOfZero = Task.FromResult(0); diff --git a/src/Grpc.AspNetCore.Server/Internal/ServerCallHandlerFactory.cs b/src/Grpc.AspNetCore.Server/Internal/ServerCallHandlerFactory.cs index 95d545942..7da6ff722 100644 --- a/src/Grpc.AspNetCore.Server/Internal/ServerCallHandlerFactory.cs +++ b/src/Grpc.AspNetCore.Server/Internal/ServerCallHandlerFactory.cs @@ -31,7 +31,7 @@ namespace Grpc.AspNetCore.Server.Internal; /// /// Creates server call handlers. Provides a place to get services that call handlers will use. /// -internal partial class ServerCallHandlerFactory<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> where TService : class +internal sealed partial class ServerCallHandlerFactory<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> where TService : class { private readonly ILoggerFactory _loggerFactory; private readonly IGrpcServiceActivator _serviceActivator; diff --git a/src/Grpc.AspNetCore.Server/Internal/SystemClock.cs b/src/Grpc.AspNetCore.Server/Internal/SystemClock.cs index cfa40177b..89c4db069 100644 --- a/src/Grpc.AspNetCore.Server/Internal/SystemClock.cs +++ b/src/Grpc.AspNetCore.Server/Internal/SystemClock.cs @@ -18,7 +18,7 @@ namespace Grpc.AspNetCore.Server.Internal; -internal class SystemClock : ISystemClock +internal sealed class SystemClock : ISystemClock { public static readonly SystemClock Instance = new SystemClock(); diff --git a/src/Grpc.AspNetCore.Server/Model/Internal/BinderServiceModelProvider.cs b/src/Grpc.AspNetCore.Server/Model/Internal/BinderServiceModelProvider.cs index 101ee27e6..537b8a35b 100644 --- a/src/Grpc.AspNetCore.Server/Model/Internal/BinderServiceModelProvider.cs +++ b/src/Grpc.AspNetCore.Server/Model/Internal/BinderServiceModelProvider.cs @@ -24,7 +24,7 @@ namespace Grpc.AspNetCore.Server.Model.Internal; -internal class BinderServiceMethodProvider<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> : IServiceMethodProvider where TService : class +internal sealed class BinderServiceMethodProvider<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> : IServiceMethodProvider where TService : class { private readonly ILogger> _logger; diff --git a/src/Grpc.AspNetCore.Server/Model/Internal/MethodModel.cs b/src/Grpc.AspNetCore.Server/Model/Internal/MethodModel.cs index 3b82d96d2..c030a4cd4 100644 --- a/src/Grpc.AspNetCore.Server/Model/Internal/MethodModel.cs +++ b/src/Grpc.AspNetCore.Server/Model/Internal/MethodModel.cs @@ -22,7 +22,7 @@ namespace Grpc.AspNetCore.Server.Model.Internal; -internal class MethodModel +internal sealed class MethodModel { public MethodModel(IMethod method, RoutePattern pattern, IList metadata, RequestDelegate requestDelegate) { diff --git a/src/Grpc.AspNetCore.Server/Model/Internal/ProviderServiceBinder.cs b/src/Grpc.AspNetCore.Server/Model/Internal/ProviderServiceBinder.cs index 84be01989..eeaae23c6 100644 --- a/src/Grpc.AspNetCore.Server/Model/Internal/ProviderServiceBinder.cs +++ b/src/Grpc.AspNetCore.Server/Model/Internal/ProviderServiceBinder.cs @@ -24,7 +24,7 @@ namespace Grpc.AspNetCore.Server.Model.Internal; -internal class ProviderServiceBinder<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> : ServiceBinderBase where TService : class +internal sealed class ProviderServiceBinder<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> : ServiceBinderBase where TService : class { private readonly ServiceMethodProviderContext _context; private readonly Type _declaringType; diff --git a/src/Grpc.AspNetCore.Server/Model/Internal/ServiceMethodsRegistry.cs b/src/Grpc.AspNetCore.Server/Model/Internal/ServiceMethodsRegistry.cs index 5c2cf7882..0d1df6e45 100644 --- a/src/Grpc.AspNetCore.Server/Model/Internal/ServiceMethodsRegistry.cs +++ b/src/Grpc.AspNetCore.Server/Model/Internal/ServiceMethodsRegistry.cs @@ -21,7 +21,7 @@ namespace Grpc.AspNetCore.Server.Model.Internal; /// /// A registry of all the service methods in the application. /// -internal class ServiceMethodsRegistry +internal sealed class ServiceMethodsRegistry { public List Methods { get; } = new List(); } diff --git a/src/Grpc.AspNetCore.Server/Model/Internal/ServiceRouteBuilder.cs b/src/Grpc.AspNetCore.Server/Model/Internal/ServiceRouteBuilder.cs index dc3613ffa..b30d28a39 100644 --- a/src/Grpc.AspNetCore.Server/Model/Internal/ServiceRouteBuilder.cs +++ b/src/Grpc.AspNetCore.Server/Model/Internal/ServiceRouteBuilder.cs @@ -27,7 +27,7 @@ namespace Grpc.AspNetCore.Server.Model.Internal; -internal class ServiceRouteBuilder<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> where TService : class +internal sealed class ServiceRouteBuilder<[DynamicallyAccessedMembers(GrpcProtocolConstants.ServiceAccessibility)] TService> where TService : class { private readonly IEnumerable> _serviceMethodProviders; private readonly ServerCallHandlerFactory _serverCallHandlerFactory; diff --git a/src/Grpc.AspNetCore.Web/Internal/Base64PipeReader.cs b/src/Grpc.AspNetCore.Web/Internal/Base64PipeReader.cs index 668adeb88..ff5dabbb1 100644 --- a/src/Grpc.AspNetCore.Web/Internal/Base64PipeReader.cs +++ b/src/Grpc.AspNetCore.Web/Internal/Base64PipeReader.cs @@ -25,7 +25,7 @@ namespace Grpc.AspNetCore.Web.Internal; /// /// Reads and decodes base64 encoded bytes from the inner reader. /// -internal class Base64PipeReader : PipeReader +internal sealed class Base64PipeReader : PipeReader { private readonly PipeReader _inner; private ReadOnlySequence _currentDecodedBuffer; diff --git a/src/Grpc.AspNetCore.Web/Internal/Base64PipeWriter.cs b/src/Grpc.AspNetCore.Web/Internal/Base64PipeWriter.cs index 22185a23b..b50c70d26 100644 --- a/src/Grpc.AspNetCore.Web/Internal/Base64PipeWriter.cs +++ b/src/Grpc.AspNetCore.Web/Internal/Base64PipeWriter.cs @@ -25,7 +25,7 @@ namespace Grpc.AspNetCore.Web.Internal; /// /// Writes bytes as base64 encoded to the inner writer. /// -internal class Base64PipeWriter : PipeWriter +internal sealed class Base64PipeWriter : PipeWriter { private readonly PipeWriter _inner; // We have to write original data to buffer. GetSpan/GetMemory isn't guaranteed to return the @@ -187,4 +187,4 @@ private void WriteRemainder() _remainder = 0; } } -} \ No newline at end of file +} diff --git a/src/Grpc.AspNetCore.Web/Internal/GrpcWebFeature.cs b/src/Grpc.AspNetCore.Web/Internal/GrpcWebFeature.cs index 23738e426..adfda938d 100644 --- a/src/Grpc.AspNetCore.Web/Internal/GrpcWebFeature.cs +++ b/src/Grpc.AspNetCore.Web/Internal/GrpcWebFeature.cs @@ -22,7 +22,7 @@ namespace Grpc.AspNetCore.Web.Internal; -internal class GrpcWebFeature : +internal sealed class GrpcWebFeature : IRequestBodyPipeFeature, IHttpResponseBodyFeature, IHttpResponseTrailersFeature, diff --git a/src/Grpc.AspNetCore.Web/Internal/MemorySegment.cs b/src/Grpc.AspNetCore.Web/Internal/MemorySegment.cs index 7a534f1e3..63b1eedd1 100644 --- a/src/Grpc.AspNetCore.Web/Internal/MemorySegment.cs +++ b/src/Grpc.AspNetCore.Web/Internal/MemorySegment.cs @@ -20,7 +20,7 @@ namespace Grpc.AspNetCore.Web.Internal; -internal class MemorySegment : ReadOnlySequenceSegment +internal sealed class MemorySegment : ReadOnlySequenceSegment { public MemorySegment(ReadOnlyMemory memory) { diff --git a/src/Grpc.Core.Api/CallCredentials.cs b/src/Grpc.Core.Api/CallCredentials.cs index 086ecd8ca..144989d5c 100644 --- a/src/Grpc.Core.Api/CallCredentials.cs +++ b/src/Grpc.Core.Api/CallCredentials.cs @@ -53,7 +53,7 @@ public static CallCredentials FromInterceptor(AsyncAuthInterceptor interceptor) /// public abstract void InternalPopulateConfiguration(CallCredentialsConfiguratorBase configurator, object? state); - private class CompositeCallCredentials : CallCredentials + private sealed class CompositeCallCredentials : CallCredentials { readonly IReadOnlyList credentials; @@ -69,7 +69,7 @@ public override void InternalPopulateConfiguration(CallCredentialsConfiguratorBa } } - private class AsyncAuthInterceptorCredentials : CallCredentials + private sealed class AsyncAuthInterceptorCredentials : CallCredentials { readonly AsyncAuthInterceptor interceptor; diff --git a/src/Grpc.Core.Api/ClientBase.cs b/src/Grpc.Core.Api/ClientBase.cs index 7379959b9..bd01586a0 100644 --- a/src/Grpc.Core.Api/ClientBase.cs +++ b/src/Grpc.Core.Api/ClientBase.cs @@ -175,12 +175,12 @@ public ClientBaseDebugType(ClientBase client) /// /// Represents configuration of ClientBase. The class itself is visible to /// subclasses, but contents are marked as internal to make the instances opaque. - /// The verbose name of this class was chosen to make name clash in generated code + /// The verbose name of this class was chosen to make name clash in generated code /// less likely. /// protected internal class ClientBaseConfiguration { - private class ClientBaseConfigurationInterceptor : Interceptor + private sealed class ClientBaseConfigurationInterceptor : Interceptor { readonly Func interceptor; diff --git a/src/Grpc.Core.Api/Interceptors/CallInvokerExtensions.cs b/src/Grpc.Core.Api/Interceptors/CallInvokerExtensions.cs index 81a27943a..3aedecfa6 100644 --- a/src/Grpc.Core.Api/Interceptors/CallInvokerExtensions.cs +++ b/src/Grpc.Core.Api/Interceptors/CallInvokerExtensions.cs @@ -94,7 +94,7 @@ public static CallInvoker Intercept(this CallInvoker invoker, Func interceptor; diff --git a/src/Grpc.Core.Api/Interceptors/InterceptingCallInvoker.cs b/src/Grpc.Core.Api/Interceptors/InterceptingCallInvoker.cs index d1e39a77d..902a98010 100644 --- a/src/Grpc.Core.Api/Interceptors/InterceptingCallInvoker.cs +++ b/src/Grpc.Core.Api/Interceptors/InterceptingCallInvoker.cs @@ -16,7 +16,6 @@ #endregion -using System; using System.Diagnostics; using Grpc.Core.Utils; @@ -27,7 +26,7 @@ namespace Grpc.Core.Interceptors; /// intercept calls through a given interceptor. /// [DebuggerDisplay("{invoker}")] -internal class InterceptingCallInvoker : CallInvoker +internal sealed class InterceptingCallInvoker : CallInvoker { readonly CallInvoker invoker; readonly Interceptor interceptor; diff --git a/src/Grpc.Core.Api/Internal/UnimplementedCallInvoker.cs b/src/Grpc.Core.Api/Internal/UnimplementedCallInvoker.cs index 7fadcd255..bebf7694d 100644 --- a/src/Grpc.Core.Api/Internal/UnimplementedCallInvoker.cs +++ b/src/Grpc.Core.Api/Internal/UnimplementedCallInvoker.cs @@ -17,16 +17,13 @@ #endregion using System; -using System.Threading.Tasks; -using Grpc.Core; -using Grpc.Core.Utils; namespace Grpc.Core.Internal; /// /// Call invoker that throws NotImplementedException for all requests. /// -internal class UnimplementedCallInvoker : CallInvoker +internal sealed class UnimplementedCallInvoker : CallInvoker { public UnimplementedCallInvoker() { diff --git a/src/Grpc.Net.Client.Web/Internal/Base64RequestStream.cs b/src/Grpc.Net.Client.Web/Internal/Base64RequestStream.cs index cd73c42b5..e9c1c2499 100644 --- a/src/Grpc.Net.Client.Web/Internal/Base64RequestStream.cs +++ b/src/Grpc.Net.Client.Web/Internal/Base64RequestStream.cs @@ -21,7 +21,7 @@ namespace Grpc.Net.Client.Web.Internal; -internal class Base64RequestStream : Stream +internal sealed class Base64RequestStream : Stream { private readonly Stream _inner; private byte[]? _buffer; diff --git a/src/Grpc.Net.Client.Web/Internal/Base64ResponseStream.cs b/src/Grpc.Net.Client.Web/Internal/Base64ResponseStream.cs index ec37ae87a..18bd6eec2 100644 --- a/src/Grpc.Net.Client.Web/Internal/Base64ResponseStream.cs +++ b/src/Grpc.Net.Client.Web/Internal/Base64ResponseStream.cs @@ -22,7 +22,7 @@ namespace Grpc.Net.Client.Web.Internal; -internal class Base64ResponseStream : Stream +internal sealed class Base64ResponseStream : Stream { private readonly Stream _inner; diff --git a/src/Grpc.Net.Client.Web/Internal/GrpcWebRequestContent.cs b/src/Grpc.Net.Client.Web/Internal/GrpcWebRequestContent.cs index 4a9586223..4309bf34c 100644 --- a/src/Grpc.Net.Client.Web/Internal/GrpcWebRequestContent.cs +++ b/src/Grpc.Net.Client.Web/Internal/GrpcWebRequestContent.cs @@ -20,7 +20,7 @@ namespace Grpc.Net.Client.Web.Internal; -internal class GrpcWebRequestContent : HttpContent +internal sealed class GrpcWebRequestContent : HttpContent { private readonly HttpContent _inner; private readonly GrpcWebMode _mode; diff --git a/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseContent.cs b/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseContent.cs index 8df348fe6..f7105942e 100644 --- a/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseContent.cs +++ b/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseContent.cs @@ -21,7 +21,7 @@ namespace Grpc.Net.Client.Web.Internal; -internal class GrpcWebResponseContent : HttpContent +internal sealed class GrpcWebResponseContent : HttpContent { private readonly HttpContent _inner; private readonly GrpcWebMode _mode; diff --git a/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseStream.cs b/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseStream.cs index 3f29028d5..7e2cc7054 100644 --- a/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseStream.cs +++ b/src/Grpc.Net.Client.Web/Internal/GrpcWebResponseStream.cs @@ -29,7 +29,7 @@ namespace Grpc.Net.Client.Web.Internal; /// for trailers. When the trailers message is encountered then they are parsed as HTTP/1.1 trailers and /// added to the HttpResponseMessage.TrailingHeaders. /// -internal class GrpcWebResponseStream : Stream +internal sealed class GrpcWebResponseStream : Stream { private const int HeaderLength = 5; diff --git a/src/Grpc.Net.Client.Web/Internal/OperatingSystem.cs b/src/Grpc.Net.Client.Web/Internal/OperatingSystem.cs index 0c15c19bb..759cdc838 100644 --- a/src/Grpc.Net.Client.Web/Internal/OperatingSystem.cs +++ b/src/Grpc.Net.Client.Web/Internal/OperatingSystem.cs @@ -25,7 +25,7 @@ internal interface IOperatingSystem bool IsBrowser { get; } } -internal class OperatingSystem : IOperatingSystem +internal sealed class OperatingSystem : IOperatingSystem { public static readonly OperatingSystem Instance = new OperatingSystem(); diff --git a/src/Grpc.Net.Client/Balancer/Internal/BalancerAddressEqualityComparer.cs b/src/Grpc.Net.Client/Balancer/Internal/BalancerAddressEqualityComparer.cs index 60c62bf59..5f852a416 100644 --- a/src/Grpc.Net.Client/Balancer/Internal/BalancerAddressEqualityComparer.cs +++ b/src/Grpc.Net.Client/Balancer/Internal/BalancerAddressEqualityComparer.cs @@ -21,7 +21,7 @@ namespace Grpc.Net.Client.Balancer.Internal; -internal class BalancerAddressEqualityComparer : IEqualityComparer +internal sealed class BalancerAddressEqualityComparer : IEqualityComparer { internal static readonly BalancerAddressEqualityComparer Instance = new BalancerAddressEqualityComparer(); diff --git a/src/Grpc.Net.Client/Balancer/Internal/BalancerHttpHandler.cs b/src/Grpc.Net.Client/Balancer/Internal/BalancerHttpHandler.cs index 37bc3c7e9..8b0953e8e 100644 --- a/src/Grpc.Net.Client/Balancer/Internal/BalancerHttpHandler.cs +++ b/src/Grpc.Net.Client/Balancer/Internal/BalancerHttpHandler.cs @@ -17,19 +17,14 @@ #endregion #if SUPPORT_LOAD_BALANCING -using System; using System.Diagnostics; -using System.IO; using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using Grpc.Shared; using Microsoft.Extensions.Logging; namespace Grpc.Net.Client.Balancer.Internal; -internal partial class BalancerHttpHandler : DelegatingHandler +internal sealed partial class BalancerHttpHandler : DelegatingHandler { private static readonly object SetupLock = new object(); @@ -178,7 +173,7 @@ internal static partial class Log public static partial void SendingRequest(ILogger logger, Uri requestUri); [LoggerMessage(Level = LogLevel.Trace, EventId = 2, EventName = "StartingConnectCallback", Message = "Starting connect callback for {Endpoint}.")] - private static partial void StartingConnectCallback(ILogger logger, string endpoint); + private static partial void StartingConnectCallback(ILogger logger, string endpoint); public static void StartingConnectCallback(ILogger logger, DnsEndPoint endpoint) { diff --git a/src/Grpc.Net.Client/Balancer/Internal/ErrorPicker.cs b/src/Grpc.Net.Client/Balancer/Internal/ErrorPicker.cs index b96865943..e9eb4fcf0 100644 --- a/src/Grpc.Net.Client/Balancer/Internal/ErrorPicker.cs +++ b/src/Grpc.Net.Client/Balancer/Internal/ErrorPicker.cs @@ -17,13 +17,12 @@ #endregion #if SUPPORT_LOAD_BALANCING -using System; using System.Diagnostics; using Grpc.Core; namespace Grpc.Net.Client.Balancer.Internal; -internal class ErrorPicker : SubchannelPicker +internal sealed class ErrorPicker : SubchannelPicker { private readonly Status _status; diff --git a/src/Grpc.Net.Client/Balancer/Internal/PassiveSubchannelTransport.cs b/src/Grpc.Net.Client/Balancer/Internal/PassiveSubchannelTransport.cs index a4ea4093c..83217f6c4 100644 --- a/src/Grpc.Net.Client/Balancer/Internal/PassiveSubchannelTransport.cs +++ b/src/Grpc.Net.Client/Balancer/Internal/PassiveSubchannelTransport.cs @@ -17,12 +17,8 @@ #endregion #if SUPPORT_LOAD_BALANCING -using System; using System.Diagnostics; -using System.IO; using System.Net; -using System.Threading; -using System.Threading.Tasks; using Grpc.Core; namespace Grpc.Net.Client.Balancer.Internal; @@ -32,7 +28,7 @@ namespace Grpc.Net.Client.Balancer.Internal; /// This transport will only be used when there is one address. /// It isn't able to correctly determine connectivity state. /// -internal class PassiveSubchannelTransport : ISubchannelTransport, IDisposable +internal sealed class PassiveSubchannelTransport : ISubchannelTransport, IDisposable { private readonly Subchannel _subchannel; private DnsEndPoint? _currentEndPoint; diff --git a/src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs b/src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs index f1f3ffab3..41656ae64 100644 --- a/src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs +++ b/src/Grpc.Net.Client/Balancer/Internal/SocketConnectivitySubchannelTransport.cs @@ -28,7 +28,7 @@ namespace Grpc.Net.Client.Balancer.Internal; /// /// Transport that makes it possible to monitor connectivity state while using HttpClient. -/// +/// /// Features: /// 1. When a connection is requested the transport creates a Socket and connects to the server. /// The socket is used with the first stream created by SocketsHttpHandler.ConnectCallback. @@ -39,7 +39,7 @@ namespace Grpc.Net.Client.Balancer.Internal; /// 2. Transport supports multiple addresses. When connecting it will iterate through the addresses, /// attempting to connect to each one. /// -internal class SocketConnectivitySubchannelTransport : ISubchannelTransport, IDisposable +internal sealed class SocketConnectivitySubchannelTransport : ISubchannelTransport, IDisposable { private const int MaximumInitialSocketDataSize = 1024 * 16; internal static readonly TimeSpan SocketPingInterval = TimeSpan.FromSeconds(5); diff --git a/src/Grpc.Net.Client/Balancer/PickFirstBalancer.cs b/src/Grpc.Net.Client/Balancer/PickFirstBalancer.cs index 88f4cfead..bc1f5ad90 100644 --- a/src/Grpc.Net.Client/Balancer/PickFirstBalancer.cs +++ b/src/Grpc.Net.Client/Balancer/PickFirstBalancer.cs @@ -17,8 +17,6 @@ #endregion #if SUPPORT_LOAD_BALANCING -using System; -using System.Collections.Generic; using Grpc.Core; using Grpc.Net.Client.Balancer.Internal; using Grpc.Net.Client.Configuration; @@ -187,7 +185,7 @@ public override PickResult Pick(PickContext context) } } -internal class RequestConnectionPicker : PickFirstPicker +internal sealed class RequestConnectionPicker : PickFirstPicker { public RequestConnectionPicker(Subchannel subchannel) : base(subchannel) { diff --git a/src/Grpc.Net.Client/Internal/GrpcCallScope.cs b/src/Grpc.Net.Client/Internal/GrpcCallScope.cs index afc3d79c7..abc9b4b02 100644 --- a/src/Grpc.Net.Client/Internal/GrpcCallScope.cs +++ b/src/Grpc.Net.Client/Internal/GrpcCallScope.cs @@ -21,7 +21,7 @@ namespace Grpc.Net.Client.Internal; -internal class GrpcCallScope : IReadOnlyList> +internal sealed class GrpcCallScope : IReadOnlyList> { private const string GrpcMethodTypeKey = "GrpcMethodType"; private const string GrpcUriKey = "GrpcUri"; diff --git a/src/Grpc.Net.Client/Internal/GrpcMethodInfo.cs b/src/Grpc.Net.Client/Internal/GrpcMethodInfo.cs index 5f357730b..fad5e0b18 100644 --- a/src/Grpc.Net.Client/Internal/GrpcMethodInfo.cs +++ b/src/Grpc.Net.Client/Internal/GrpcMethodInfo.cs @@ -24,7 +24,7 @@ namespace Grpc.Net.Client.Internal; /// /// Cached log scope and URI for a gRPC . /// -internal class GrpcMethodInfo +internal sealed class GrpcMethodInfo { public GrpcMethodInfo(GrpcCallScope logScope, Uri callUri, MethodConfig? methodConfig) { @@ -116,13 +116,13 @@ internal static HedgingPolicyInfo CreateHedgingPolicy(HedgingPolicy h) public MethodConfigInfo? MethodConfig { get; } } -internal class MethodConfigInfo +internal sealed class MethodConfigInfo { public RetryPolicyInfo? RetryPolicy { get; set; } public HedgingPolicyInfo? HedgingPolicy { get; set; } } -internal class RetryPolicyInfo +internal sealed class RetryPolicyInfo { public int MaxAttempts { get; init; } public TimeSpan InitialBackoff { get; init; } @@ -131,7 +131,7 @@ internal class RetryPolicyInfo public List RetryableStatusCodes { get; init; } = default!; } -internal class HedgingPolicyInfo +internal sealed class HedgingPolicyInfo { public int MaxAttempts { get; set; } public TimeSpan HedgingDelay { get; set; } diff --git a/src/Grpc.Net.Client/Internal/Http/PushStreamContent.cs b/src/Grpc.Net.Client/Internal/Http/PushStreamContent.cs index 13ae19a3f..2e1e2407d 100644 --- a/src/Grpc.Net.Client/Internal/Http/PushStreamContent.cs +++ b/src/Grpc.Net.Client/Internal/Http/PushStreamContent.cs @@ -20,7 +20,7 @@ namespace Grpc.Net.Client.Internal.Http; -internal class PushStreamContent : HttpContent +internal sealed class PushStreamContent : HttpContent where TRequest : class where TResponse : class { diff --git a/src/Grpc.Net.Client/Internal/Http/PushUnaryContent.cs b/src/Grpc.Net.Client/Internal/Http/PushUnaryContent.cs index c31a72251..a1b232fd5 100644 --- a/src/Grpc.Net.Client/Internal/Http/PushUnaryContent.cs +++ b/src/Grpc.Net.Client/Internal/Http/PushUnaryContent.cs @@ -22,7 +22,7 @@ namespace Grpc.Net.Client.Internal; // TODO: Still need generic args? -internal class PushUnaryContent : HttpContent +internal sealed class PushUnaryContent : HttpContent where TRequest : class where TResponse : class { diff --git a/src/Grpc.Net.Client/Internal/Http/WinHttpUnaryContent.cs b/src/Grpc.Net.Client/Internal/Http/WinHttpUnaryContent.cs index 44e1c374d..39e946a85 100644 --- a/src/Grpc.Net.Client/Internal/Http/WinHttpUnaryContent.cs +++ b/src/Grpc.Net.Client/Internal/Http/WinHttpUnaryContent.cs @@ -27,7 +27,7 @@ namespace Grpc.Net.Client.Internal.Http; /// The payload is then written directly to the request using specialized context /// and serializer method. /// -internal class WinHttpUnaryContent : HttpContent +internal sealed class WinHttpUnaryContent : HttpContent where TRequest : class where TResponse : class { diff --git a/src/Grpc.Net.Client/Internal/HttpContentClientStreamReader.cs b/src/Grpc.Net.Client/Internal/HttpContentClientStreamReader.cs index 7ec3a1534..907604752 100644 --- a/src/Grpc.Net.Client/Internal/HttpContentClientStreamReader.cs +++ b/src/Grpc.Net.Client/Internal/HttpContentClientStreamReader.cs @@ -27,7 +27,7 @@ namespace Grpc.Net.Client.Internal; [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(HttpContentClientStreamReader<,>.HttpContentClientStreamReaderDebugView))] -internal class HttpContentClientStreamReader : IAsyncStreamReader +internal sealed class HttpContentClientStreamReader : IAsyncStreamReader where TRequest : class where TResponse : class { diff --git a/src/Grpc.Net.Client/Internal/HttpContentClientStreamWriter.cs b/src/Grpc.Net.Client/Internal/HttpContentClientStreamWriter.cs index 028658e18..83666f9ba 100644 --- a/src/Grpc.Net.Client/Internal/HttpContentClientStreamWriter.cs +++ b/src/Grpc.Net.Client/Internal/HttpContentClientStreamWriter.cs @@ -26,7 +26,7 @@ namespace Grpc.Net.Client.Internal; [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(HttpContentClientStreamWriter<,>.HttpContentClientStreamWriterDebugView))] -internal class HttpContentClientStreamWriter : ClientStreamWriterBase +internal sealed class HttpContentClientStreamWriter : ClientStreamWriterBase where TRequest : class where TResponse : class { diff --git a/src/Grpc.Net.Client/Internal/Retry/ChannelRetryThrottling.cs b/src/Grpc.Net.Client/Internal/Retry/ChannelRetryThrottling.cs index 17c52c84a..874f843eb 100644 --- a/src/Grpc.Net.Client/Internal/Retry/ChannelRetryThrottling.cs +++ b/src/Grpc.Net.Client/Internal/Retry/ChannelRetryThrottling.cs @@ -21,7 +21,7 @@ namespace Grpc.Net.Client.Internal.Retry; -internal partial class ChannelRetryThrottling +internal sealed partial class ChannelRetryThrottling { private readonly object _lock = new object(); private readonly double _tokenRatio; diff --git a/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamReader.cs b/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamReader.cs index 5c68c18cb..da41c5d82 100644 --- a/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamReader.cs +++ b/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamReader.cs @@ -24,7 +24,7 @@ namespace Grpc.Net.Client.Internal.Retry; [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(RetryCallBaseClientStreamReader<,>.RetryCallBaseClientStreamReaderDebugView))] -internal class RetryCallBaseClientStreamReader : IAsyncStreamReader +internal sealed class RetryCallBaseClientStreamReader : IAsyncStreamReader where TRequest : class where TResponse : class { diff --git a/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamWriter.cs b/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamWriter.cs index 0ceffe746..9d5857df2 100644 --- a/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamWriter.cs +++ b/src/Grpc.Net.Client/Internal/Retry/RetryCallBaseClientStreamWriter.cs @@ -24,7 +24,7 @@ namespace Grpc.Net.Client.Internal.Retry; [DebuggerDisplay("{DebuggerToString(),nq}")] [DebuggerTypeProxy(typeof(RetryCallBaseClientStreamWriter<,>.RetryCallBaseClientStreamWriterDebugView))] -internal class RetryCallBaseClientStreamWriter : ClientStreamWriterBase +internal sealed class RetryCallBaseClientStreamWriter : ClientStreamWriterBase where TRequest : class where TResponse : class { diff --git a/src/Shared/Server/InterceptorPipelineBuilder.cs b/src/Shared/Server/InterceptorPipelineBuilder.cs index df1da7297..71e5e9a1d 100644 --- a/src/Shared/Server/InterceptorPipelineBuilder.cs +++ b/src/Shared/Server/InterceptorPipelineBuilder.cs @@ -22,7 +22,7 @@ namespace Grpc.Shared.Server; -internal class InterceptorPipelineBuilder +internal sealed class InterceptorPipelineBuilder where TRequest : class where TResponse : class { @@ -159,4 +159,4 @@ private static GrpcActivatorHandle CreateInterceptor(InterceptorReg return interceptorHandle; } -} \ No newline at end of file +} diff --git a/src/Shared/TrailingHeadersHelpers.cs b/src/Shared/TrailingHeadersHelpers.cs index 024a5b9b9..ce4d29518 100644 --- a/src/Shared/TrailingHeadersHelpers.cs +++ b/src/Shared/TrailingHeadersHelpers.cs @@ -51,7 +51,7 @@ public static void EnsureTrailingHeaders(this HttpResponseMessage responseMessag public static readonly string ResponseTrailersKey = "__ResponseTrailers"; - private class ResponseTrailers : HttpHeaders + private sealed class ResponseTrailers : HttpHeaders { public static readonly ResponseTrailers Empty = new ResponseTrailers(); } diff --git a/src/dotnet-grpc/Commands/AddFileCommand.cs b/src/dotnet-grpc/Commands/AddFileCommand.cs index 35302c143..dd6e79b48 100644 --- a/src/dotnet-grpc/Commands/AddFileCommand.cs +++ b/src/dotnet-grpc/Commands/AddFileCommand.cs @@ -16,17 +16,14 @@ #endregion -using System; using System.CommandLine; -using System.CommandLine.Invocation; using Grpc.Dotnet.Cli.Internal; using Grpc.Dotnet.Cli.Options; using Grpc.Dotnet.Cli.Properties; -using Microsoft.Build.Evaluation; namespace Grpc.Dotnet.Cli.Commands; -internal class AddFileCommand : CommandBase +internal sealed class AddFileCommand : CommandBase { public AddFileCommand(IConsole console, string? projectPath, HttpClient httpClient) : base(console, projectPath, httpClient) { } diff --git a/src/dotnet-grpc/Commands/AddUrlCommand.cs b/src/dotnet-grpc/Commands/AddUrlCommand.cs index fa8a52265..99e9b0ebf 100644 --- a/src/dotnet-grpc/Commands/AddUrlCommand.cs +++ b/src/dotnet-grpc/Commands/AddUrlCommand.cs @@ -17,14 +17,13 @@ #endregion using System.CommandLine; -using System.CommandLine.Invocation; using Grpc.Dotnet.Cli.Internal; using Grpc.Dotnet.Cli.Options; using Grpc.Dotnet.Cli.Properties; namespace Grpc.Dotnet.Cli.Commands; -internal class AddUrlCommand : CommandBase +internal sealed class AddUrlCommand : CommandBase { public AddUrlCommand(IConsole console, string? projectPath, HttpClient httpClient) : base(console, projectPath, httpClient) { } diff --git a/src/dotnet-grpc/Commands/ListCommand.cs b/src/dotnet-grpc/Commands/ListCommand.cs index 158510355..99056aec4 100644 --- a/src/dotnet-grpc/Commands/ListCommand.cs +++ b/src/dotnet-grpc/Commands/ListCommand.cs @@ -17,7 +17,6 @@ #endregion using System.CommandLine; -using System.CommandLine.Invocation; using System.CommandLine.Rendering; using System.CommandLine.Rendering.Views; using Grpc.Dotnet.Cli.Internal; @@ -27,7 +26,7 @@ namespace Grpc.Dotnet.Cli.Commands; -internal class ListCommand : CommandBase +internal sealed class ListCommand : CommandBase { public ListCommand(IConsole console, string? projectPath, HttpClient httpClient) : base(console, projectPath, httpClient) { } diff --git a/src/dotnet-grpc/Commands/RefreshCommand.cs b/src/dotnet-grpc/Commands/RefreshCommand.cs index 2069f1f15..be5522748 100644 --- a/src/dotnet-grpc/Commands/RefreshCommand.cs +++ b/src/dotnet-grpc/Commands/RefreshCommand.cs @@ -17,15 +17,13 @@ #endregion using System.CommandLine; -using System.CommandLine.Invocation; using Grpc.Dotnet.Cli.Internal; using Grpc.Dotnet.Cli.Options; using Grpc.Dotnet.Cli.Properties; -using Microsoft.Build.Evaluation; namespace Grpc.Dotnet.Cli.Commands; -internal class RefreshCommand : CommandBase +internal sealed class RefreshCommand : CommandBase { public RefreshCommand(IConsole console, string? projectPath, HttpClient httpClient) : base(console, projectPath, httpClient) { } diff --git a/src/dotnet-grpc/Commands/RemoveCommand.cs b/src/dotnet-grpc/Commands/RemoveCommand.cs index 1a956b352..3bfc7d11e 100644 --- a/src/dotnet-grpc/Commands/RemoveCommand.cs +++ b/src/dotnet-grpc/Commands/RemoveCommand.cs @@ -16,17 +16,14 @@ #endregion -using System; using System.CommandLine; -using System.CommandLine.Invocation; using Grpc.Dotnet.Cli.Internal; using Grpc.Dotnet.Cli.Options; using Grpc.Dotnet.Cli.Properties; -using Microsoft.Build.Evaluation; namespace Grpc.Dotnet.Cli.Commands; -internal class RemoveCommand : CommandBase +internal sealed class RemoveCommand : CommandBase { public RemoveCommand(IConsole console, string? projectPath, HttpClient httpClient) : base(console, projectPath, httpClient) { } diff --git a/src/dotnet-grpc/Internal/CLIToolException.cs b/src/dotnet-grpc/Internal/CLIToolException.cs index b9dbb71ec..92ac9945d 100644 --- a/src/dotnet-grpc/Internal/CLIToolException.cs +++ b/src/dotnet-grpc/Internal/CLIToolException.cs @@ -19,7 +19,7 @@ namespace Grpc.Dotnet.Cli.Internal; -internal class CLIToolException : Exception +internal sealed class CLIToolException : Exception { public CLIToolException(string message) : base(message) { } } diff --git a/src/dotnet-grpc/Internal/GrpcDependencyAttribute.cs b/src/dotnet-grpc/Internal/GrpcDependencyAttribute.cs index 7b1a69f63..08f0b311b 100644 --- a/src/dotnet-grpc/Internal/GrpcDependencyAttribute.cs +++ b/src/dotnet-grpc/Internal/GrpcDependencyAttribute.cs @@ -20,7 +20,7 @@ namespace Grpc.Dotnet.Cli.Internal; [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] -internal class GrpcDependencyAttribute : Attribute +internal sealed class GrpcDependencyAttribute : Attribute { public GrpcDependencyAttribute(string name, string version, string privateAssets, string applicableServices, string? applicableToWeb = null) { From 42593dde65f9ee483924d58c570353ac87c3f8d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:48:20 +0800 Subject: [PATCH 10/15] Bump vue from 2.6.14 to 3.0.0 in /examples/Spar/Server/ClientApp (#2565) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../Spar/Server/ClientApp/package-lock.json | 172 +++++++++++++++--- examples/Spar/Server/ClientApp/package.json | 2 +- 2 files changed, 149 insertions(+), 25 deletions(-) diff --git a/examples/Spar/Server/ClientApp/package-lock.json b/examples/Spar/Server/ClientApp/package-lock.json index fdcf0ae59..7cf245d58 100644 --- a/examples/Spar/Server/ClientApp/package-lock.json +++ b/examples/Spar/Server/ClientApp/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "google-protobuf": "^3.19.4", "grpc-web": "^1.3.1", - "vue": "^2.6.12", + "vue": "^3.0.0", "vue-class-component": "^7.2.6", "vue-hot-reload-api": "^2.3.4", "vue-property-decorator": "^9.1.2" @@ -420,7 +420,6 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -429,7 +428,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -490,7 +488,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1690,7 +1687,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -1856,6 +1852,27 @@ "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==", "dev": true }, + "node_modules/@vue/compiler-core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.0.tgz", + "integrity": "sha512-XqPC7vdv4rFE77S71oCHmT1K4Ks3WE2Gi6Lr4B5wn0Idmp+NyQQBUHsCNieMDRiEpgtJrw+yOHslrsV0AfAsfQ==", + "dependencies": { + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", + "@vue/shared": "3.0.0", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.0.tgz", + "integrity": "sha512-ukDEGOP8P7lCPyStuM3F2iD5w2QPgUu2xwCW2XNeqPjFKIlR2xMsWjy4raI/cLjN6W16GtlMFaZdK8tLj5PRog==", + "dependencies": { + "@vue/compiler-core": "3.0.0", + "@vue/shared": "3.0.0" + } + }, "node_modules/@vue/component-compiler-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", @@ -1875,6 +1892,38 @@ "prettier": "^1.18.2 || ^2.0.0" } }, + "node_modules/@vue/reactivity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.0.tgz", + "integrity": "sha512-mEGkztGQrAPZRhV7C6PorrpT3+NtuA4dY2QjMzzrW31noKhssWTajRZTwpLF39NBRrF5UU6cp9+1I0FfavMgEQ==", + "dependencies": { + "@vue/shared": "3.0.0" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.0.tgz", + "integrity": "sha512-3ABMLeA0ZbeVNLbGGLXr+pNUwqXILOqz8WCVGfDWwQb+jW114Cm8djOHVVDoqdvRETQvDf8yHSUmpKHZpQuTkA==", + "dependencies": { + "@vue/reactivity": "3.0.0", + "@vue/shared": "3.0.0" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.0.tgz", + "integrity": "sha512-f312n5w9gK6mVvkDSj6/Xnot1XjlKXzFBYybmoy6ahAVC8ExbQ+LOWti1IZM/adU8VMNdKaw7Q53Hxz3y5jX8g==", + "dependencies": { + "@vue/runtime-core": "3.0.0", + "@vue/shared": "3.0.0", + "csstype": "^2.6.8" + } + }, + "node_modules/@vue/shared": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.0.0.tgz", + "integrity": "sha512-4XWL/avABGxU2E2ZF1eZq3Tj7fvksCMssDZUHOykBIMmh5d+KcAnQMC5XHMhtnA0NAvktYsA2YpdsVwVmhWzvA==" + }, "node_modules/abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -3485,6 +3534,11 @@ "cssom": "0.3.x" } }, + "node_modules/csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -3983,6 +4037,11 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -8161,7 +8220,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -8681,7 +8739,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, "engines": { "node": ">=4" } @@ -9208,9 +9265,14 @@ "dev": true }, "node_modules/vue": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", - "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.0.0.tgz", + "integrity": "sha512-ZMrAARZ32sGIaYKr7Fk2GZEBh/VhulSrGxcGBiAvbN4fhjl3tuJyNFbbbLFqGjndbLoBW66I2ECq8ICdvkKdJw==", + "dependencies": { + "@vue/compiler-dom": "3.0.0", + "@vue/runtime-dom": "3.0.0", + "@vue/shared": "3.0.0" + } }, "node_modules/vue-class-component": { "version": "7.2.6", @@ -9691,14 +9753,12 @@ "@babel/helper-string-parser": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.16.7", @@ -9743,8 +9803,7 @@ "@babel/parser": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.16.7", @@ -10550,7 +10609,6 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, "requires": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -10685,6 +10743,27 @@ "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==", "dev": true }, + "@vue/compiler-core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.0.0.tgz", + "integrity": "sha512-XqPC7vdv4rFE77S71oCHmT1K4Ks3WE2Gi6Lr4B5wn0Idmp+NyQQBUHsCNieMDRiEpgtJrw+yOHslrsV0AfAsfQ==", + "requires": { + "@babel/parser": "^7.11.5", + "@babel/types": "^7.11.5", + "@vue/shared": "3.0.0", + "estree-walker": "^2.0.1", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.0.0.tgz", + "integrity": "sha512-ukDEGOP8P7lCPyStuM3F2iD5w2QPgUu2xwCW2XNeqPjFKIlR2xMsWjy4raI/cLjN6W16GtlMFaZdK8tLj5PRog==", + "requires": { + "@vue/compiler-core": "3.0.0", + "@vue/shared": "3.0.0" + } + }, "@vue/component-compiler-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz", @@ -10702,6 +10781,38 @@ "vue-template-es2015-compiler": "^1.9.0" } }, + "@vue/reactivity": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.0.0.tgz", + "integrity": "sha512-mEGkztGQrAPZRhV7C6PorrpT3+NtuA4dY2QjMzzrW31noKhssWTajRZTwpLF39NBRrF5UU6cp9+1I0FfavMgEQ==", + "requires": { + "@vue/shared": "3.0.0" + } + }, + "@vue/runtime-core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.0.0.tgz", + "integrity": "sha512-3ABMLeA0ZbeVNLbGGLXr+pNUwqXILOqz8WCVGfDWwQb+jW114Cm8djOHVVDoqdvRETQvDf8yHSUmpKHZpQuTkA==", + "requires": { + "@vue/reactivity": "3.0.0", + "@vue/shared": "3.0.0" + } + }, + "@vue/runtime-dom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.0.0.tgz", + "integrity": "sha512-f312n5w9gK6mVvkDSj6/Xnot1XjlKXzFBYybmoy6ahAVC8ExbQ+LOWti1IZM/adU8VMNdKaw7Q53Hxz3y5jX8g==", + "requires": { + "@vue/runtime-core": "3.0.0", + "@vue/shared": "3.0.0", + "csstype": "^2.6.8" + } + }, + "@vue/shared": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.0.0.tgz", + "integrity": "sha512-4XWL/avABGxU2E2ZF1eZq3Tj7fvksCMssDZUHOykBIMmh5d+KcAnQMC5XHMhtnA0NAvktYsA2YpdsVwVmhWzvA==" + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -12046,6 +12157,11 @@ "cssom": "0.3.x" } }, + "csstype": { + "version": "2.6.21", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -12444,6 +12560,11 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -15817,8 +15938,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { "version": "0.5.3", @@ -16233,8 +16353,7 @@ "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" }, "to-object-path": { "version": "0.3.0", @@ -16673,9 +16792,14 @@ "dev": true }, "vue": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz", - "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.0.0.tgz", + "integrity": "sha512-ZMrAARZ32sGIaYKr7Fk2GZEBh/VhulSrGxcGBiAvbN4fhjl3tuJyNFbbbLFqGjndbLoBW66I2ECq8ICdvkKdJw==", + "requires": { + "@vue/compiler-dom": "3.0.0", + "@vue/runtime-dom": "3.0.0", + "@vue/shared": "3.0.0" + } }, "vue-class-component": { "version": "7.2.6", diff --git a/examples/Spar/Server/ClientApp/package.json b/examples/Spar/Server/ClientApp/package.json index f49dbdd08..8c3e8bd7d 100644 --- a/examples/Spar/Server/ClientApp/package.json +++ b/examples/Spar/Server/ClientApp/package.json @@ -11,7 +11,7 @@ "dependencies": { "google-protobuf": "^3.19.4", "grpc-web": "^1.3.1", - "vue": "^2.6.12", + "vue": "^3.0.0", "vue-class-component": "^7.2.6", "vue-hot-reload-api": "^2.3.4", "vue-property-decorator": "^9.1.2" From be98a74f688282466aaea4460bac63f363b1ce99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:48:58 +0800 Subject: [PATCH 11/15] Bump cross-spawn from 7.0.3 to 7.0.6 in /testassets/InteropTestsGrpcWebWebsite/Tests (#2574) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../InteropTestsGrpcWebWebsite/Tests/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testassets/InteropTestsGrpcWebWebsite/Tests/package-lock.json b/testassets/InteropTestsGrpcWebWebsite/Tests/package-lock.json index be28a0d87..dda6f2d74 100644 --- a/testassets/InteropTestsGrpcWebWebsite/Tests/package-lock.json +++ b/testassets/InteropTestsGrpcWebWebsite/Tests/package-lock.json @@ -1647,9 +1647,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", From cacf59d94227f338ac12cbbb47187118277f433d Mon Sep 17 00:00:00 2001 From: subhraOffGit <162678244+subhraOffGit@users.noreply.github.com> Date: Mon, 9 Dec 2024 06:19:13 +0530 Subject: [PATCH 12/15] [vote]Added Active maintainers into MAINTAINERS.md. (#2449) --- MAINTAINERS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 1155b86fc..4454ee7fb 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -15,9 +15,10 @@ for general contribution guidelines. - [BrennanConroy](https://github.com/BrennanConroy), Microsoft Corporation - [davidfowl](https://github.com/davidfowl), Microsoft Corporation - [JamesNK](https://github.com/JamesNK), Microsoft Corporation -- [jtattermusch](https://github.com/jtattermusch), Google LLC - [mgravell](https://github.com/mgravell), Microsoft Corporation - [ReubenBond](https://github.com/ReubenBond), Microsoft Corporation +- [stanley-cheung](https://github.com/stanley-cheung), Google LLC ## Emeritus Maintainers (in alphabetical order) +- [jtattermusch](https://github.com/jtattermusch) - [JunTaoLuo](https://github.com/JunTaoLuo) From 6a3c9774f651b2d5ab0b92a8987732f0a6643b25 Mon Sep 17 00:00:00 2001 From: Dexter Ajoku Date: Mon, 9 Dec 2024 01:54:24 +0100 Subject: [PATCH 13/15] Refactor: Use `await using` for `packageVersionStream` to ensure proper disposal of async resources (#2521) --- src/dotnet-grpc/Commands/CommandBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotnet-grpc/Commands/CommandBase.cs b/src/dotnet-grpc/Commands/CommandBase.cs index d0a400cfd..ce56115da 100644 --- a/src/dotnet-grpc/Commands/CommandBase.cs +++ b/src/dotnet-grpc/Commands/CommandBase.cs @@ -127,7 +127,7 @@ public async Task EnsureNugetPackagesAsync(Services services) }*/ try { - using var packageVersionStream = await _httpClient.GetStreamAsync(PackageVersionUrl); + await using var packageVersionStream = await _httpClient.GetStreamAsync(PackageVersionUrl); using var packageVersionDocument = await JsonDocument.ParseAsync(packageVersionStream); var packageVersionsElement = packageVersionDocument.RootElement.GetProperty("Packages"); var packageVersionsDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); From b7af033360155835fede1f042f6e45cc3ce94aa0 Mon Sep 17 00:00:00 2001 From: Henrik Date: Mon, 9 Dec 2024 11:03:21 +0100 Subject: [PATCH 14/15] Performance microoptimizations (#2558) Co-authored-by: James Newton-King --- .../Internal/GrpcProtocolHelpers.cs | 10 ++++------ src/Grpc.AspNetCore.Web/Internal/ServerGrpcWebMode.cs | 2 +- src/Shared/Server/MethodOptions.cs | 5 +---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Grpc.AspNetCore.Server/Internal/GrpcProtocolHelpers.cs b/src/Grpc.AspNetCore.Server/Internal/GrpcProtocolHelpers.cs index ef5a7b191..9e4e1f7a0 100644 --- a/src/Grpc.AspNetCore.Server/Internal/GrpcProtocolHelpers.cs +++ b/src/Grpc.AspNetCore.Server/Internal/GrpcProtocolHelpers.cs @@ -18,6 +18,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; using System.Text; using Grpc.Core; @@ -108,7 +109,7 @@ public static byte[] ParseBinaryHeader(string base64) switch (base64.Length % 4) { case 0: - // base64 has the required padding + // base64 has the required padding decodable = base64; break; case 2: @@ -208,11 +209,8 @@ public static AuthContext CreateAuthContext(X509Certificate2 clientCertificate) static void AddProperty(Dictionary> properties, string name, string value) { - if (!properties.TryGetValue(name, out var values)) - { - values = new List(); - properties[name] = values; - } + ref var values = ref CollectionsMarshal.GetValueRefOrAddDefault(properties, name, out _); + values ??= []; values.Add(AuthProperty.Create(name, Encoding.UTF8.GetBytes(value))); } diff --git a/src/Grpc.AspNetCore.Web/Internal/ServerGrpcWebMode.cs b/src/Grpc.AspNetCore.Web/Internal/ServerGrpcWebMode.cs index 2d7a74938..ce980264d 100644 --- a/src/Grpc.AspNetCore.Web/Internal/ServerGrpcWebMode.cs +++ b/src/Grpc.AspNetCore.Web/Internal/ServerGrpcWebMode.cs @@ -20,7 +20,7 @@ namespace Grpc.AspNetCore.Web.Internal; internal readonly record struct ServerGrpcWebContext(ServerGrpcWebMode Request, ServerGrpcWebMode Response); -internal enum ServerGrpcWebMode +internal enum ServerGrpcWebMode : byte { None, GrpcWeb, diff --git a/src/Shared/Server/MethodOptions.cs b/src/Shared/Server/MethodOptions.cs index 1f0e29c24..f50fc4cd3 100644 --- a/src/Shared/Server/MethodOptions.cs +++ b/src/Shared/Server/MethodOptions.cs @@ -158,10 +158,7 @@ private static void AddCompressionProviders(Dictionary Date: Thu, 12 Dec 2024 10:17:08 +0800 Subject: [PATCH 15/15] Complete health checks watch service on server shutting down (#2582) --- .../GrpcHealthChecksOptions.cs | 18 ++ .../GrpcHealthChecksPublisher.cs | 1 - .../Internal/HealthServiceIntegration.cs | 81 ++++++- src/Grpc.HealthCheck/HealthServiceImpl.cs | 7 + .../HealthChecks/HealthServiceTests.cs | 221 +++++++++++++++++- .../TestHostApplicationLifetime.cs | 37 +++ 6 files changed, 359 insertions(+), 6 deletions(-) create mode 100644 test/Grpc.AspNetCore.Server.Tests/TestObjects/TestHostApplicationLifetime.cs diff --git a/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksOptions.cs b/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksOptions.cs index 84bfe3d91..4390eba2e 100644 --- a/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksOptions.cs +++ b/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksOptions.cs @@ -17,6 +17,7 @@ #endregion using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Hosting; namespace Grpc.AspNetCore.HealthChecks; @@ -39,4 +40,21 @@ public sealed class GrpcHealthChecksOptions /// published by are returned. /// public bool UseHealthChecksCache { get; set; } + + /// + /// Gets or sets a value indicating whether to suppress completing Watch health check calls when the application begins shutting down. + /// The default value is false. + /// + /// + /// + /// When false, health checks Watch calls are completed with a status of NotServing when the server application begins shutting down. + /// Shutdown is indicated by the token being raised and causes Watch to complete. + /// When true, health checks Watch calls are left running. Running calls will be eventually be forcefully aborted when the server finishes shutting down. + /// + /// + /// Completing the Watch call allows the server to gracefully exit. If Watch calls aren't shutdown then the server runs until + /// is exceeded and the server forcefully aborts remaining active requests. + /// + /// + public bool SuppressCompletionOnShutdown { get; set; } } diff --git a/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksPublisher.cs b/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksPublisher.cs index 7fee5bfe5..24b23031e 100644 --- a/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksPublisher.cs +++ b/src/Grpc.AspNetCore.HealthChecks/GrpcHealthChecksPublisher.cs @@ -16,7 +16,6 @@ #endregion -using System.Linq; using Grpc.Health.V1; using Grpc.HealthCheck; using Microsoft.Extensions.Diagnostics.HealthChecks; diff --git a/src/Grpc.AspNetCore.HealthChecks/Internal/HealthServiceIntegration.cs b/src/Grpc.AspNetCore.HealthChecks/Internal/HealthServiceIntegration.cs index fbe185e48..710dccd4b 100644 --- a/src/Grpc.AspNetCore.HealthChecks/Internal/HealthServiceIntegration.cs +++ b/src/Grpc.AspNetCore.HealthChecks/Internal/HealthServiceIntegration.cs @@ -22,6 +22,7 @@ using Grpc.HealthCheck; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; namespace Grpc.AspNetCore.HealthChecks.Internal; @@ -32,17 +33,20 @@ internal sealed class HealthServiceIntegration : Grpc.Health.V1.Health.HealthBas private readonly GrpcHealthChecksOptions _grpcHealthCheckOptions; private readonly HealthServiceImpl _healthServiceImpl; private readonly HealthCheckService _healthCheckService; + private readonly IHostApplicationLifetime _applicationLifetime; public HealthServiceIntegration( HealthServiceImpl healthServiceImpl, IOptions healthCheckOptions, IOptions grpcHealthCheckOptions, - HealthCheckService healthCheckService) + HealthCheckService healthCheckService, + IHostApplicationLifetime applicationLifetime) { _healthCheckOptions = healthCheckOptions.Value; _grpcHealthCheckOptions = grpcHealthCheckOptions.Value; _healthServiceImpl = healthServiceImpl; _healthCheckService = healthCheckService; + _applicationLifetime = applicationLifetime; } public override Task Check(HealthCheckRequest request, ServerCallContext context) @@ -57,15 +61,84 @@ public override Task Check(HealthCheckRequest request, Serv } } - public override Task Watch(HealthCheckRequest request, IServerStreamWriter responseStream, ServerCallContext context) + public override async Task Watch(HealthCheckRequest request, IServerStreamWriter responseStream, ServerCallContext context) { + ServerCallContext resolvedContext; + IServerStreamWriter resolvedResponseStream; + + if (!_grpcHealthCheckOptions.SuppressCompletionOnShutdown) + { + // Create a linked token source to cancel the request if the application is stopping. + // This is required because the server won't shut down gracefully if the request is still open. + // The context needs to be wrapped because HealthServiceImpl is in an assembly that can't reference IHostApplicationLifetime. + var cts = CancellationTokenSource.CreateLinkedTokenSource(context.CancellationToken, _applicationLifetime.ApplicationStopping); + resolvedContext = new WrappedServerCallContext(context, cts); + } + else + { + resolvedContext = context; + } + if (!_grpcHealthCheckOptions.UseHealthChecksCache) { // Stream writer replaces first health checks results from the cache with newly calculated health check results. - responseStream = new WatchServerStreamWriter(this, request, responseStream, context.CancellationToken); + resolvedResponseStream = new WatchServerStreamWriter(this, request, responseStream, context.CancellationToken); + } + else + { + resolvedResponseStream = responseStream; + } + + await _healthServiceImpl.Watch(request, resolvedResponseStream, resolvedContext); + + // If the request is not canceled and the application is stopping then return NotServing before finishing. + if (!context.CancellationToken.IsCancellationRequested && _applicationLifetime.ApplicationStopping.IsCancellationRequested) + { + await responseStream.WriteAsync(new HealthCheckResponse { Status = HealthCheckResponse.Types.ServingStatus.NotServing }); } + } - return _healthServiceImpl.Watch(request, responseStream, context); + private sealed class WrappedServerCallContext : ServerCallContext + { + private readonly ServerCallContext _serverCallContext; + private readonly CancellationTokenSource _cancellationTokenSource; + + public WrappedServerCallContext(ServerCallContext serverCallContext, CancellationTokenSource cancellationTokenSource) + { + _serverCallContext = serverCallContext; + _cancellationTokenSource = cancellationTokenSource; + } + + protected override string MethodCore => _serverCallContext.Method; + protected override string HostCore => _serverCallContext.Host; + protected override string PeerCore => _serverCallContext.Peer; + protected override DateTime DeadlineCore => _serverCallContext.Deadline; + protected override Metadata RequestHeadersCore => _serverCallContext.RequestHeaders; + protected override CancellationToken CancellationTokenCore => _cancellationTokenSource.Token; + protected override Metadata ResponseTrailersCore => _serverCallContext.ResponseTrailers; + protected override Status StatusCore + { + get => _serverCallContext.Status; + set => _serverCallContext.Status = value; + } + protected override WriteOptions? WriteOptionsCore + { + get => _serverCallContext.WriteOptions; + set => _serverCallContext.WriteOptions = value; + } + protected override AuthContext AuthContextCore => _serverCallContext.AuthContext; + + protected override IDictionary UserStateCore => _serverCallContext.UserState; + + protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions? options) + { + return _serverCallContext.CreatePropagationToken(options); + } + + protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders) + { + return _serverCallContext.WriteResponseHeadersAsync(responseHeaders); + } } private async Task GetHealthCheckResponseAsync(string service, bool throwOnNotFound, CancellationToken cancellationToken) diff --git a/src/Grpc.HealthCheck/HealthServiceImpl.cs b/src/Grpc.HealthCheck/HealthServiceImpl.cs index 80003ee68..4dc6bf84c 100644 --- a/src/Grpc.HealthCheck/HealthServiceImpl.cs +++ b/src/Grpc.HealthCheck/HealthServiceImpl.cs @@ -141,6 +141,13 @@ public override Task Check(HealthCheckRequest request, Serv /// A task indicating completion of the handler. public override async Task Watch(HealthCheckRequest request, IServerStreamWriter responseStream, ServerCallContext context) { + // The call has already been canceled. Writing to the response will fail so immediately exit. + // In the real world this situation is unlikely to happen as the server would have prevented a canceled call from making it this far. + if (context.CancellationToken.IsCancellationRequested) + { + return; + } + string service = request.Service; // Channel is used to to marshall multiple callers updating status into a single queue. diff --git a/test/Grpc.AspNetCore.Server.Tests/HealthChecks/HealthServiceTests.cs b/test/Grpc.AspNetCore.Server.Tests/HealthChecks/HealthServiceTests.cs index 649981b66..9eac43567 100644 --- a/test/Grpc.AspNetCore.Server.Tests/HealthChecks/HealthServiceTests.cs +++ b/test/Grpc.AspNetCore.Server.Tests/HealthChecks/HealthServiceTests.cs @@ -20,6 +20,7 @@ using Grpc.AspNetCore.HealthChecks; using Grpc.AspNetCore.HealthChecks.Internal; using Grpc.AspNetCore.Server.Tests.Infrastructure; +using Grpc.AspNetCore.Server.Tests.TestObjects; using Grpc.Core; using Grpc.Health.V1; using Grpc.HealthCheck; @@ -63,6 +64,8 @@ public async Task HealthService_Watch_UsePublishedChecks_WriteResults() o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); var serviceProvider = services.BuildServiceProvider(); @@ -133,6 +136,8 @@ public async Task HealthService_Watch_RunChecks_WriteResults() o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); var serviceProvider = services.BuildServiceProvider(); @@ -206,6 +211,8 @@ public async Task HealthService_Watch_RunChecks_Log() o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); var serviceProvider = services.BuildServiceProvider(); @@ -260,6 +267,210 @@ public async Task HealthService_Watch_RunChecks_Log() Assert.AreEqual("Service '' status updated to NotServing. 1 health report entries evaluated.", serviceMappingStatusUpdated.Message); } + [Test] + public async Task HealthService_Watch_ServerShutdownDuringCall_WatchCompleted() + { + // Arrange + var testSink = new TestSink(); + var testProvider = new TestLoggerProvider(testSink); + + var healthCheckResult = new HealthCheckResult(HealthStatus.Healthy); + + var services = new ServiceCollection(); + services.AddLogging(o => o.AddProvider(testProvider).SetMinimumLevel(LogLevel.Debug)); + services.AddNUnitLogger(); + services.AddGrpcHealthChecks().AddAsyncCheck( + "", + () => Task.FromResult(healthCheckResult), new string[] { "sample" }); + services.Configure(o => + { + o.Delay = TimeSpan.FromSeconds(1); + o.Period = TimeSpan.FromSeconds(1); + }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); + + var serviceProvider = services.BuildServiceProvider(); + + var healthService = CreateService(serviceProvider); + var hostedService = serviceProvider.GetServices().Single(); + + HealthCheckResponse? response = null; + var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); + var testServerStreamWriter = new TestServerStreamWriter(); + testServerStreamWriter.OnWriteAsync = async message => + { + response = message; + await syncPoint.WaitToContinue(); + }; + + var cts = new CancellationTokenSource(); + var callTask = healthService.Watch( + new HealthCheckRequest(), + testServerStreamWriter, + new TestServerCallContext(DateTime.MaxValue, cts.Token)); + + // Act + await hostedService.StartAsync(CancellationToken.None); + + // Assert + try + { + await syncPoint.WaitForSyncPoint().DefaultTimeout(); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.Serving, response!.Status); + syncPoint.Continue(); + + syncPoint = new SyncPoint(runContinuationsAsynchronously: true); + lifetime.StopApplication(); + await syncPoint.WaitForSyncPoint().DefaultTimeout(); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.NotServing, response!.Status); + syncPoint.Continue(); + + await callTask.DefaultTimeout(); + } + finally + { + await hostedService.StopAsync(CancellationToken.None); + } + } + + [Test] + public async Task HealthService_Watch_ServerShutdownBeforeCall_WatchCompleted() + { + // Arrange + var testSink = new TestSink(); + var testProvider = new TestLoggerProvider(testSink); + + var healthCheckResult = new HealthCheckResult(HealthStatus.Healthy); + + var services = new ServiceCollection(); + services.AddLogging(o => o.AddProvider(testProvider).SetMinimumLevel(LogLevel.Debug)); + services.AddNUnitLogger(); + services.AddGrpcHealthChecks().AddAsyncCheck( + "", + () => Task.FromResult(healthCheckResult), new string[] { "sample" }); + services.Configure(o => + { + o.Delay = TimeSpan.FromSeconds(1); + o.Period = TimeSpan.FromSeconds(1); + }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); + + var serviceProvider = services.BuildServiceProvider(); + + var healthService = CreateService(serviceProvider); + var hostedService = serviceProvider.GetServices().Single(); + + HealthCheckResponse? response = null; + var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); + var testServerStreamWriter = new TestServerStreamWriter(); + testServerStreamWriter.OnWriteAsync = async message => + { + response = message; + await syncPoint.WaitToContinue(); + }; + + lifetime.StopApplication(); + + var cts = new CancellationTokenSource(); + var callTask = healthService.Watch( + new HealthCheckRequest(), + testServerStreamWriter, + new TestServerCallContext(DateTime.MaxValue, cts.Token)); + + // Act + await hostedService.StartAsync(CancellationToken.None); + + // Assert + try + { + await syncPoint.WaitForSyncPoint().DefaultTimeout(); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.NotServing, response!.Status); + syncPoint.Continue(); + + await callTask.DefaultTimeout(); + } + finally + { + await hostedService.StopAsync(CancellationToken.None); + } + } + + [Test] + public async Task HealthService_Watch_ServerShutdown_SuppressCompletion_WatchNotCompleted() + { + // Arrange + var testSink = new TestSink(); + var testProvider = new TestLoggerProvider(testSink); + + var healthCheckResult = new HealthCheckResult(HealthStatus.Healthy); + + var services = new ServiceCollection(); + services.AddLogging(o => o.AddProvider(testProvider).SetMinimumLevel(LogLevel.Debug)); + services.AddNUnitLogger(); + services.AddGrpcHealthChecks(o => o.SuppressCompletionOnShutdown = true).AddAsyncCheck( + "", + () => Task.FromResult(healthCheckResult), new string[] { "sample" }); + services.Configure(o => + { + o.Delay = TimeSpan.FromSeconds(1); + o.Period = TimeSpan.FromSeconds(1); + }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); + + var serviceProvider = services.BuildServiceProvider(); + + var healthService = CreateService(serviceProvider); + var hostedService = serviceProvider.GetServices().Single(); + + HealthCheckResponse? response = null; + var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); + var testServerStreamWriter = new TestServerStreamWriter(); + testServerStreamWriter.OnWriteAsync = async message => + { + response = message; + await syncPoint.WaitToContinue(); + }; + + var cts = new CancellationTokenSource(); + var callTask = healthService.Watch( + new HealthCheckRequest(), + testServerStreamWriter, + new TestServerCallContext(DateTime.MaxValue, cts.Token)); + + // Act + await hostedService.StartAsync(CancellationToken.None); + + // Assert + try + { + await syncPoint.WaitForSyncPoint().DefaultTimeout(); + Assert.AreEqual(HealthCheckResponse.Types.ServingStatus.Serving, response!.Status); + syncPoint.Continue(); + + syncPoint = new SyncPoint(runContinuationsAsynchronously: true); + lifetime.StopApplication(); + + // Wait a second to double check that watch doesn't complete. + var waitForSyncPointTask = syncPoint.WaitForSyncPoint(); + var timeoutTask = Task.Delay(TimeSpan.FromSeconds(0.5)); + if (await Task.WhenAny(callTask, waitForSyncPointTask, timeoutTask) != timeoutTask) + { + Assert.Fail("Expected watch to not complete."); + } + + cts.Cancel(); + + await callTask.DefaultTimeout(); + } + finally + { + await hostedService.StopAsync(CancellationToken.None); + } + } + private class TestHealthCheckPublisher : IHealthCheckPublisher { public Func? OnHealthReport { get; set; } @@ -279,7 +490,8 @@ private static HealthServiceIntegration CreateService(IServiceProvider servicePr serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService>(), serviceProvider.GetRequiredService>(), - serviceProvider.GetRequiredService()); + serviceProvider.GetRequiredService(), + serviceProvider.GetRequiredService()); } [Test] @@ -300,6 +512,9 @@ public async Task HealthService_CheckWithFilter_RunChecks_FilteredResultsExclude .AddAsyncCheck("default", () => RunHealthCheck(healthCheckStatus, "")) .AddAsyncCheck("filtered", () => RunHealthCheck(healthCheckStatus, "filtered"), new string[] { "exclude" }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); + var serviceProvider = services.BuildServiceProvider(); var healthService = CreateService(serviceProvider); @@ -384,6 +599,8 @@ public async Task HealthService_CheckWithFilter_UsePublishedChecks_FilteredResul o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); HealthReport? report = null; var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); @@ -458,6 +675,8 @@ public async Task HealthService_RemoveDefault_DefaultNotFound() o.Delay = TimeSpan.FromSeconds(1); o.Period = TimeSpan.FromSeconds(1); }); + var lifetime = new TestHostApplicationLifetime(); + services.AddSingleton(lifetime); HealthReport? report = null; var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); diff --git a/test/Grpc.AspNetCore.Server.Tests/TestObjects/TestHostApplicationLifetime.cs b/test/Grpc.AspNetCore.Server.Tests/TestObjects/TestHostApplicationLifetime.cs new file mode 100644 index 000000000..bc2a90c7e --- /dev/null +++ b/test/Grpc.AspNetCore.Server.Tests/TestObjects/TestHostApplicationLifetime.cs @@ -0,0 +1,37 @@ +#region Copyright notice and license + +// Copyright 2019 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#endregion + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.Hosting; + +namespace Grpc.AspNetCore.Server.Tests.TestObjects; + +public class TestHostApplicationLifetime : IHostApplicationLifetime +{ + private readonly CancellationTokenSource _cts = new(); + + public CancellationToken ApplicationStarted => _cts.Token; + public CancellationToken ApplicationStopped => _cts.Token; + public CancellationToken ApplicationStopping => _cts.Token; + + public void StopApplication() + { + _cts.Cancel(); + } +}