Skip to content

Commit be8caa6

Browse files
authored
Gracefully handle cancelled instant search requests (loic-sharma#383)
Adds a middleware to swallow cancellation exceptions due to instant searching. Swallows frontend errors caused by instant searching. Part of loic-sharma#354
1 parent c813544 commit be8caa6

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using Microsoft.AspNetCore.Builder;
3+
4+
namespace BaGet.Core.Server.Extensions
5+
{
6+
public static class IApplicationBuilderExtensions
7+
{
8+
public static IApplicationBuilder UseOperationCancelledMiddleware(this IApplicationBuilder app)
9+
{
10+
if (app == null) throw new ArgumentNullException(nameof(app));
11+
12+
return app.UseMiddleware<OperationCancelledMiddleware>();
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Net;
3+
using System.Threading.Tasks;
4+
using Microsoft.AspNetCore.Http;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace BaGet
8+
{
9+
/// <summary>
10+
/// Captures <see cref="OperationCanceledException" /> and converts to HTTP 409 response.
11+
/// Based off: https://github.com/aspnet/AspNetCore/blob/28157e62597bf0e043bc7e937e44c5ec81946b83/src/Middleware/Diagnostics/src/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs
12+
/// </summary>
13+
public class OperationCancelledMiddleware
14+
{
15+
private readonly RequestDelegate _next;
16+
private readonly ILogger<OperationCancelledMiddleware> _logger;
17+
18+
public OperationCancelledMiddleware(RequestDelegate next, ILogger<OperationCancelledMiddleware> logger)
19+
{
20+
_next = next ?? throw new ArgumentNullException(nameof(next));
21+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
22+
}
23+
24+
public async Task Invoke(HttpContext context)
25+
{
26+
try
27+
{
28+
await _next(context);
29+
}
30+
catch (Exception e) when (ShouldHandleException(context, e))
31+
{
32+
try
33+
{
34+
_logger.LogWarning("Request cancelled");
35+
36+
context.Response.Clear();
37+
context.Response.StatusCode = (int)HttpStatusCode.Conflict;
38+
return;
39+
}
40+
catch (Exception)
41+
{
42+
// If there's an exception, rethrow the original exception.
43+
}
44+
45+
throw;
46+
}
47+
48+
bool ShouldHandleException(HttpContext ctx, Exception e)
49+
{
50+
if (ctx.Response.HasStarted) return false;
51+
52+
if (e is OperationCanceledException) return true;
53+
if (e.InnerException is OperationCanceledException) return true;
54+
55+
return false;
56+
}
57+
}
58+
}
59+
}

src/BaGet.UI/src/SearchResults.tsx

+13-1
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,27 @@ class SearchResults extends React.Component<ISearchResultsProps, ISearchResultsS
217217
const url = this.buildUrl(query, includePrerelease, packageType, targetFramework);
218218

219219
fetch(url, {signal: this.resultsController.signal}).then(response => {
220-
return response.json();
220+
return response.ok
221+
? response.json()
222+
: null;
221223
}).then(resultsJson => {
224+
if (!resultsJson) {
225+
return;
226+
}
227+
222228
const results = resultsJson as ISearchResponse;
223229

224230
this.setState({
225231
includePrerelease,
226232
items: results.data,
227233
targetFramework,
228234
});
235+
})
236+
.catch((e) => {
237+
var ex = e as DOMException;
238+
if (!ex || ex.code !== DOMException.ABORT_ERR) {
239+
console.log("Unexpected error on search", e);
240+
}
229241
});
230242
}
231243

src/BaGet/Startup.cs

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using BaGet.Configuration;
33
using BaGet.Core;
4+
using BaGet.Core.Server.Extensions;
45
using BaGet.Extensions;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Hosting;
@@ -58,6 +59,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
5859
app.UseSpaStaticFiles();
5960

6061
app.UseCors(ConfigureCorsOptions.CorsPolicy);
62+
app.UseOperationCancelledMiddleware();
6163

6264
app.UseMvc(routes =>
6365
{

0 commit comments

Comments
 (0)