Handle HTTP request exceptions.
MODIFY
WalletWasabi.Backend/Filters/IdempotentActionFilterAttributeImpl.cs
WalletWasabi.Backend/Filters/IdempotentActionFilterAttributeImpl.cs
@@ -37,17 +37,21 @@ namespace WalletWasabi.Backend.Filters
if (context.ModelState.IsValid)
{
- if (context.ActionArguments.TryGetValue("request", out object? model))
+ if (!context.ActionArguments.TryGetValue("request", out object? model))
{
- string cacheKey = GetCacheEntryKey(request.Path, model);
+ throw new InvalidOperationException("Control actions marked as Idempotent must receive a 'request' argument.");
+ }
+ string cacheKey = GetCacheEntryKey(request.Path, model);
TaskCompletionSource<IActionResult>? cachedResponseTcs = null;
+ TaskCompletionSource<IActionResult>? responseTcs = null;
lock (ResponseCacheLock)
{
if (!ResponseCache.TryGetValue(cacheKey, out cachedResponseTcs))
{
- ResponseCache.Set(cacheKey, new TaskCompletionSource<IActionResult>(), DateTimeOffset.UtcNow.Add(CacheTimeout));
+ responseTcs = new();
+ ResponseCache.Set(cacheKey, responseTcs, DateTimeOffset.UtcNow.Add(CacheTimeout));
}
}
@@ -66,15 +70,23 @@ namespace WalletWasabi.Backend.Filters
}
context.HttpContext.Items[ContextCacheKey] = cacheKey;
- }
- else
+
+ ActionExecutedContext executed = await next().ConfigureAwait(false);
+
+ // Request failed for some reason, we don't want to hold any other requests that wait for this one request to finish.
+ if (executed.Exception != null && !executed.ExceptionHandled)
{
- throw new InvalidOperationException("Control actions marked as Idempotent must receive a 'request' argument.");
+ if (responseTcs is not null)
+ {
+ responseTcs.TrySetCanceled();
}
}
-
+ }
+ else
+ {
await next().ConfigureAwait(false);
}
+ }
/// <inheritdoc/>
public override void OnResultExecuted(ResultExecutedContext context)

Handle HTTP request exceptions.