Skip to content

Commit 35abc67

Browse files
committed
handle exceptions and fix UTs
1 parent 4bd6855 commit 35abc67

File tree

2 files changed

+28
-28
lines changed

2 files changed

+28
-28
lines changed

Tests.Vpn.Service/DownloaderTest.cs

+8-26
Original file line numberDiff line numberDiff line change
@@ -375,17 +375,17 @@ public async Task DownloadExistingDifferentContent(CancellationToken ct)
375375

376376
[Test(Description = "Unexpected response code from server")]
377377
[CancelAfter(30_000)]
378-
public void UnexpectedResponseCode(CancellationToken ct)
378+
public async Task UnexpectedResponseCode(CancellationToken ct)
379379
{
380380
using var httpServer = new TestHttpServer(ctx => { ctx.Response.StatusCode = 404; });
381381
var url = new Uri(httpServer.BaseUrl + "/test");
382382
var destPath = Path.Combine(_tempDir, "test");
383383

384384
var manager = new Downloader(NullLogger<Downloader>.Instance);
385-
// The "outer" Task should fail.
386-
var ex = Assert.ThrowsAsync<HttpRequestException>(async () =>
387-
await manager.StartDownloadAsync(new HttpRequestMessage(HttpMethod.Get, url), destPath,
388-
NullDownloadValidator.Instance, ct));
385+
// The "inner" Task should fail.
386+
var dlTask = await manager.StartDownloadAsync(new HttpRequestMessage(HttpMethod.Get, url), destPath,
387+
NullDownloadValidator.Instance, ct);
388+
var ex = Assert.ThrowsAsync<HttpRequestException>(async () => await dlTask.Task);
389389
Assert.That(ex.Message, Does.Contain("404"));
390390
}
391391

@@ -412,22 +412,6 @@ public async Task MismatchedETag(CancellationToken ct)
412412
Assert.That(ex.Message, Does.Contain("ETag does not match SHA1 hash of downloaded file").And.Contains("beef"));
413413
}
414414

415-
[Test(Description = "Timeout on response headers")]
416-
[CancelAfter(30_000)]
417-
public void CancelledOuter(CancellationToken ct)
418-
{
419-
using var httpServer = new TestHttpServer(async _ => { await Task.Delay(TimeSpan.FromSeconds(5), ct); });
420-
var url = new Uri(httpServer.BaseUrl + "/test");
421-
var destPath = Path.Combine(_tempDir, "test");
422-
423-
var manager = new Downloader(NullLogger<Downloader>.Instance);
424-
// The "outer" Task should fail.
425-
var smallerCt = new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token;
426-
Assert.ThrowsAsync<TaskCanceledException>(async () => await manager.StartDownloadAsync(
427-
new HttpRequestMessage(HttpMethod.Get, url), destPath,
428-
NullDownloadValidator.Instance, smallerCt));
429-
}
430-
431415
[Test(Description = "Timeout on response body")]
432416
[CancelAfter(30_000)]
433417
public async Task CancelledInner(CancellationToken ct)
@@ -479,12 +463,10 @@ public async Task ValidationFailureExistingFile(CancellationToken ct)
479463
await File.WriteAllTextAsync(destPath, "test", ct);
480464

481465
var manager = new Downloader(NullLogger<Downloader>.Instance);
466+
var dlTask = await manager.StartDownloadAsync(new HttpRequestMessage(HttpMethod.Get, url), destPath,
467+
new TestDownloadValidator(new Exception("test exception")), ct);
482468
// The "outer" Task should fail because the inner task never starts.
483-
var ex = Assert.ThrowsAsync<Exception>(async () =>
484-
{
485-
await manager.StartDownloadAsync(new HttpRequestMessage(HttpMethod.Get, url), destPath,
486-
new TestDownloadValidator(new Exception("test exception")), ct);
487-
});
469+
var ex = Assert.ThrowsAsync<Exception>(async () => { await dlTask.Task; });
488470
Assert.That(ex.Message, Does.Contain("Existing file failed validation"));
489471
Assert.That(ex.InnerException, Is.Not.Null);
490472
Assert.That(ex.InnerException!.Message, Is.EqualTo("test exception"));

Vpn.Service/Downloader.cs

+20-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Formats.Asn1;
44
using System.Net;
55
using System.Runtime.CompilerServices;
6+
using System.Runtime.ExceptionServices;
67
using System.Security.Cryptography;
78
using System.Security.Cryptography.X509Certificates;
89
using Coder.Desktop.Vpn.Utilities;
@@ -290,7 +291,24 @@ public async Task<DownloadTask> StartDownloadAsync(HttpRequestMessage req, strin
290291
_ => new DownloadTask(_logger, req, destinationPath, validator));
291292
// EnsureStarted is a no-op if we didn't create a new DownloadTask.
292293
// So, we will only remove the destination once for each time we start a new task.
293-
task.EnsureStarted(tsk => _downloads.TryRemove(destinationPath, out _), ct);
294+
task.EnsureStarted(tsk =>
295+
{
296+
// remove the key first, before checking the exception, to ensure
297+
// we still clean up.
298+
_downloads.TryRemove(destinationPath, out _);
299+
if (tsk.Exception == null)
300+
{
301+
return;
302+
}
303+
304+
if (tsk.Exception.InnerException != null)
305+
{
306+
ExceptionDispatchInfo.Capture(tsk.Exception.InnerException).Throw();
307+
}
308+
309+
// not sure if this is hittable, but just in case:
310+
throw tsk.Exception;
311+
}, ct);
294312

295313
// If the existing (or new) task is for the same URL, return it.
296314
if (task.Request.RequestUri == req.RequestUri)
@@ -363,7 +381,7 @@ internal void EnsureStarted(Action<Task> continuation, CancellationToken ct = de
363381
{
364382
using var _ = _semaphore.Lock();
365383
if (Task == null!)
366-
Task = Start(ct).ContinueWith(continuation, CancellationToken.None);
384+
Task = Start(ct).ContinueWith(continuation, ct);
367385
}
368386

369387
/// <summary>

0 commit comments

Comments
 (0)