Skip to content

Commit

Permalink
feat(server): implement IAsyncDisposable
Browse files Browse the repository at this point in the history
  • Loading branch information
skwasjer committed Nov 18, 2023
1 parent 3639e12 commit ba1b218
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 55 deletions.
63 changes: 48 additions & 15 deletions src/MockHttp.Server/MockHttpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,22 @@ namespace MockHttp;
/// <summary>
/// A mock HTTP server that listens on a specific URL and responds according to a configured <see cref="MockHttpHandler" />.
/// </summary>
public sealed class MockHttpServer : IDisposable
public sealed class MockHttpServer
: IDisposable,
IAsyncDisposable
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Uri _hostUri;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly SemaphoreSlim _lock = new(1);
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly IWebHostBuilder _webHostBuilder;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private IWebHost? _host;
private Action<IApplicationBuilder>? _configureAppBuilder;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly Uri _hostUri;
private bool _disposed;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private Action<IApplicationBuilder>? _configureAppBuilder;
private IWebHost? _host;

/// <summary>
/// Initializes a new instance of the <see cref="MockHttpServer" /> using specified <paramref name="mockHttpHandler" /> and configures it to listen on specified <paramref name="hostUrl" />.
Expand Down Expand Up @@ -77,10 +81,20 @@ public MockHttpServer(MockHttpHandler mockHttpHandler, ILoggerFactory? loggerFac
/// <inheritdoc />
public void Dispose()
{
_lock.Dispose();
DisposeAsync().AsTask().GetAwaiter().GetResult();
}

/// <inheritdoc />
public async ValueTask DisposeAsync()
{
if (_disposed)
{
return;
}

_host?.Dispose();
_host = null;
await StopAsync().ConfigureAwait(false);
_lock.Dispose();
_disposed = true;
}

/// <summary>
Expand All @@ -103,6 +117,8 @@ public Uri HostUri
{
get
{
CheckDisposed();

_lock.Wait();
try
{
Expand All @@ -121,13 +137,15 @@ public Uri HostUri
/// <summary>
/// Gets whether the host is started.
/// </summary>
public bool IsStarted => _host is not null;
public bool IsStarted => !_disposed && _host is not null;

/// <summary>
/// Starts listening on the configured addresses.
/// </summary>
public async Task StartAsync(CancellationToken cancellationToken = default)
{
CheckDisposed();

await _lock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
Expand All @@ -150,6 +168,8 @@ public async Task StartAsync(CancellationToken cancellationToken = default)
/// </summary>
public async Task StopAsync(CancellationToken cancellationToken = default)
{
CheckDisposed();

await _lock.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
Expand All @@ -158,16 +178,20 @@ public async Task StopAsync(CancellationToken cancellationToken = default)
return;
}

// Make local copy, so we can null it before disposing.
IWebHost host = _host;
_host = null;
using (host)
{
await host.StopAsync(cancellationToken).ConfigureAwait(false);
}
await _host.StopAsync(cancellationToken).ConfigureAwait(false);
}
finally
{
if (_host is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
else
{
_host?.Dispose();
}

_host = null;
_lock.Release();
}
}
Expand All @@ -177,6 +201,7 @@ public async Task StopAsync(CancellationToken cancellationToken = default)
/// </summary>
public HttpClient CreateClient()
{
CheckDisposed();
return new HttpClient { BaseAddress = HostUri };
}

Expand Down Expand Up @@ -238,4 +263,12 @@ private void AddMockHttpServerHeader(IApplicationBuilder applicationBuilder)
await next().ConfigureAwait(false);
});
}

private void CheckDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public Task InitializeAsync()

public Task DisposeAsync()
{
return Server.StopAsync();
return Server.DisposeAsync().AsTask();
}

private static bool SupportsIpv6()
Expand Down
Loading

0 comments on commit ba1b218

Please sign in to comment.