From 29c28aeabd68b18b09fd037e58f97a76aaa7524f Mon Sep 17 00:00:00 2001 From: Timothy Makkison Date: Wed, 6 Nov 2024 12:29:19 +0000 Subject: [PATCH] feat: prevent serialization of `CancellationToken?` --- Refit.Tests/RestService.cs | 56 ++++++++++++++++++++++++++++++++++++++ Refit/RestMethodInfo.cs | 2 +- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Refit.Tests/RestService.cs b/Refit.Tests/RestService.cs index 984a8be35..e6efefa3e 100644 --- a/Refit.Tests/RestService.cs +++ b/Refit.Tests/RestService.cs @@ -324,6 +324,18 @@ public interface IFragmentApi Task QueryAfterFragment(); } +public interface ICancellableApi +{ + [Get("/foo")] + Task GetWithCancellation(CancellationToken token = default); + + [Get("/foo")] + Task GetWithCancellationAndReturn(CancellationToken token = default); + + [Get("/foo")] + Task GetWithNullableCancellation(CancellationToken? token); +} + public class HttpBinGet { public Dictionary Args { get; set; } @@ -2568,6 +2580,50 @@ public async Task ShouldStripQueryAfterFragment() mockHttp.VerifyNoOutstandingExpectation(); } + [Fact] + public async Task TaskShouldCancelWhenRequested() + { + var ctSource = new CancellationTokenSource(); + var token = ctSource.Token; + + var fixture = RestService.For("https://github.com"); + + ctSource.Cancel(); + var task = fixture.GetWithCancellation(token); + await Assert.ThrowsAsync(async () => await task); + } + + [Fact] + public async Task TaskResultShouldCancelWhenRequested() + { + var ctSource = new CancellationTokenSource(); + var token = ctSource.Token; + + var fixture = RestService.For("https://github.com"); + + ctSource.Cancel(); + var task = fixture.GetWithCancellationAndReturn(token); + await Assert.ThrowsAsync(async () => await task); + } + + + [Fact] + public async Task NullableCancellationTokenShouldBeIgnored() + { + var mockHttp = new MockHttpMessageHandler(); + var settings = new RefitSettings { HttpMessageHandlerFactory = () => mockHttp, }; + + mockHttp + .Expect(HttpMethod.Get, "https://github.com/foo") + .Respond(HttpStatusCode.OK); + + var fixture = RestService.For("https://github.com", settings); + + await fixture.GetWithNullableCancellation(null); + + mockHttp.VerifyNoOutstandingExpectation(); + } + [Fact] public async Task TypeCollisionTest() { diff --git a/Refit/RestMethodInfo.cs b/Refit/RestMethodInfo.cs index f7e758dbd..6273967b2 100644 --- a/Refit/RestMethodInfo.cs +++ b/Refit/RestMethodInfo.cs @@ -89,7 +89,7 @@ public RestMethodInfoInternal( // Exclude cancellation token parameters from this list ParameterInfoArray = methodInfo .GetParameters() - .Where(static p => p.ParameterType != typeof(CancellationToken)) + .Where(static p => p.ParameterType != typeof(CancellationToken) && p.ParameterType != typeof(CancellationToken?)) .ToArray(); (ParameterMap, FragmentPath) = BuildParameterMap(RelativePath, ParameterInfoArray); BodyParameterInfo = FindBodyParameter(ParameterInfoArray, IsMultipart, hma.Method);