From 3dba93622291dc70b7ef7a1b143fd8af08421584 Mon Sep 17 00:00:00 2001 From: Tim M <49349513+TimothyMakkison@users.noreply.github.com> Date: Wed, 6 Nov 2024 22:17:20 +0000 Subject: [PATCH] feat: prevent serialization of `CancellationToken?` (#1917) Co-authored-by: Chris Pulman --- 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 9f0553070..0d7c13c6e 100644 --- a/Refit.Tests/RestService.cs +++ b/Refit.Tests/RestService.cs @@ -321,6 +321,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; } @@ -2547,6 +2559,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);