Problems to cancel a request (CancellationToken)
See original GitHub issueExpected Behavior
The action of the api does not correctly receive the cancellation token when aborting a request through Ocelot.
Actual Behavior
By calling the api directly and while it is running, I cancel the request. The CancellationToken parameter takes the correct value and the appropriate exception is thrown. On the other hand, if I make the same request through Ocelot, the exception is not thrown.
Steps to Reproduce the Problem
Api Code
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class PaymentsController : ControllerBase
{
private readonly ILogger logger;
public PaymentsController(ILogger<Logging> logger)
{
this.logger = logger;
}
[HttpGet("{id}")]
[ProducesResponseType(400)]
[ProducesResponseType(typeof(string), 202)]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult<string>> GetAsync(string id, CancellationToken ct)
{
try
{
Console.WriteLine("Se va a para la ejecución");
await Task.Delay(5000, ct);
ct.ThrowIfCancellationRequested();
Console.WriteLine($"La ejecución continua -{ct.IsCancellationRequested}-");
}
catch (Exception ex) when (ex is TaskCanceledException || ex is OperationCanceledException)
{
Console.WriteLine("Operación cancelada");
return BadRequest();
}
return Ok("todo OK");
}
}
Direct request to the api: http://…/api/v1/Payments/2
Hosting environment: Staging
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://*****/api/v1/Payments/2
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Route matched with {action = "GetAsync", controller = "Payments"}. Executing action LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api)
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Executing action method LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api) with arguments (2, System.Threading.CancellationToken) - Validation state: Valid
Se va a para la ejecuci¢n
Operaci¢n cancelada
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action method LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api), returned result Microsoft.AspNetCore.Mvc.BadRequestResult in 37.1519ms.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
Executing HttpStatusCodeResult, setting HTTP status code 400
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api) in 416.8234ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 570.0624ms 0
Request to the api through Ocelot: https://…/api/v1/Payments/2
Ocelot logs:
dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[3]
Hosting starting
Hosting environment: Staging
Application started. Press Ctrl+C to shut down.
dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[4]
Hosting started
dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0]
Loaded hosting startup assembly LK.Psd2.ApiGw
dbug: Microsoft.AspNetCore.Hosting.Internal.WebHost[0]
Loaded hosting startup assembly Microsoft.AspNetCore.Server.IISIntegration
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET https://******/api/v1/Payments/2
dbug: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
Wildcard detected, all requests with hosts will be allowed.
trce: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
All hosts are allowed.
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: no request id, previousRequestId: no previous request id, message: MiddlewareStarting: Microsoft.AspNetCore.HttpsPolicy.HstsMiddleware; /api/v1/Payments/2
trce: Microsoft.AspNetCore.HttpsPolicy.HstsMiddleware[3]
Adding HSTS header to response.
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: no request id, previousRequestId: no previous request id, message: MiddlewareStarting: Microsoft.AspNetCore.Builder.UseExtensions+<>c__DisplayClass0_1; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: no request id, previousRequestId: no previous request id, message: MiddlewareStarting: Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: no request id, previousRequestId: no previous request id, message: MiddlewareStarting: TransitionToOcelotMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: no request id, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ExceptionHandlerMiddleware; /api/v1/Payments/2
dbug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: ocelot pipeline started
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ResponderMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: DownstreamRouteFinderMiddleware; /api/v1/Payments/2
dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Upstream url path is /api/v1/Payments/2
dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: downstream templates are /api/{version}/{everything}
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: SecurityMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: HttpHeadersTransformationMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: DownstreamRequestInitialiserMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ClientRateLimitMiddleware; /api/v1/Payments/2
info: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for /api/{version}/{everything}
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ReRouteRequestIdMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: AuthenticationMiddleware; /api/v1/Payments/2
info: Ocelot.Authentication.Middleware.AuthenticationMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: No authentication needed for /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ClaimsToClaimsMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: AuthorisationMiddleware; /api/v1/Payments/2
info: Ocelot.Authorisation.Middleware.AuthorisationMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: /api/{version}/{everything} route does not require user to be authorised
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ClaimsToHeadersMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: ClaimsToQueryStringMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: LoadBalancingMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: DownstreamUrlCreatorMiddleware; /api/v1/Payments/2
dbug: Ocelot.DownstreamUrlCreator.Middleware.DownstreamUrlCreatorMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Downstream url is http://******/api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: OutputCacheMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareStarted: HttpRequesterMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: HttpRequesterMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: OutputCacheMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: DownstreamUrlCreatorMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: LoadBalancingMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ClaimsToQueryStringMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ClaimsToHeadersMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: AuthorisationMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ClaimsToClaimsMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: AuthenticationMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ReRouteRequestIdMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ClientRateLimitMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: DownstreamRequestInitialiserMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: HttpHeadersTransformationMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: SecurityMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: DownstreamRouteFinderMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ResponderMiddleware; /api/v1/Payments/2
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: Ocelot.MiddlewareFinished: ExceptionHandlerMiddleware; /api/v1/Payments/2
dbug: Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer[1]
Connection ID "17654110540902969818" disconnecting.
dbug: Ocelot.Requester.Middleware.HttpRequesterMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: setting http response message
dbug: Ocelot.Responder.Middleware.ResponderMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: no pipeline errors, setting and returning completed response
dbug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: ocelot pipeline finished
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: MiddlewareFinished: TransitionToOcelotMiddleware; 200
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: MiddlewareFinished: Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware; 200
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: MiddlewareFinished: Microsoft.AspNetCore.Builder.UseExtensions+<>c__DisplayClass0_1; 200
trce: Ocelot.Logging.OcelotDiagnosticListener[0]
requestId: 800031db-0000-f500-b63f-84710c7967bb, previousRequestId: no previous request id, message: MiddlewareFinished: Microsoft.AspNetCore.HttpsPolicy.HstsMiddleware; 200
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 7468.4477ms 200 text/plain; charset=utf-8
api logs:
Hosting environment: Staging
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET http://******/api/v1/Payments/2 0
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Route matched with {action = "GetAsync", controller = "Payments"}. Executing action LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api)
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
Executing action method LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api) with arguments (2, System.Threading.CancellationToken) - Validation state: Valid
Se va a para la ejecuci¢n
La ejecuci¢n continua -False-
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action method LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api), returned result Microsoft.AspNetCore.Mvc.OkObjectResult in 5019.3262ms.
info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1]
Executing ObjectResult, writing value of type 'System.String'.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
Executed action LK.Psd2.Api.Controllers.V1.PaymentsController.GetAsync (LK.Psd2.Api) in 5402.3125ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 5559.3111ms 200 text/plain; charset=utf-8
Ocelot config:
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "psd2.desa.net",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/{version}/{everything}",
"UpstreamHttpMethod": []
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId"
}
}
Ocelot Startup.cs
public class Startup
{
private readonly IConfiguration cfg;
public Startup(IConfiguration configuration) => cfg = configuration;
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddOcelot();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseCors("CorsPolicy");
app.UseOcelot().Wait();
}
}
Specifications
- Version: 13.0.0
- Platform: .netcore 2.2.2 (Windows Server 2012R2 IIS 8.5)
- Subsystem:
Issue Analytics
- State:
- Created 4 years ago
- Comments:16 (8 by maintainers)
Top Results From Across the Web
How to cancel a CancellationToken
A CancellationToken can only be canceled by its parent CancellationTokenSource . If you don't have access to the parent CTS, you can't cancel...
Read more >Recommended patterns for CancellationToken
Know when you've passed the point of no cancellation. Don't cancel if you've already incurred side-effects that your method isn't prepared ...
Read more >Cancellation in Managed Threads
CancellationTokenSource, Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
Read more >Cancellation, Part 2: Requesting Cancellation
A CancellationToken can only respond to cancellation requests; the CancellationTokenSource is necessary to request cancellation. So the ...
Read more >A Deep Dive into C#'s CancellationToken | by Mitesh Shah
Once the IsCancellationRequested property in a cancellation token is set to true , it can't be set to false again and you cant...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@TomPallister please chime in! It seems like the cancellation issue is a show-stopper for quite some people, we are also using a forked version of Ocelot. There is an easy fix (just merge PR #1367)
Please merge PR #1367! I just confirmed that it still is a fully functioning fix for this issue. What are we waiting for? 🙂
Request cancellation is a basic concept; we should be able to rely on Ocelot to handle this correctly.
We were very surprised to find out that it’s (no longer) working correctly, we’re using Ocelot for quite some time now and we think it was functioning in the past (maybe before version 16.0.0?).