Skip to content

Commit c4db2e9

Browse files
committed
Add Test MqttHostedServerStartup
1 parent 672435c commit c4db2e9

File tree

4 files changed

+135
-0
lines changed

4 files changed

+135
-0
lines changed

Source/MQTTnet.AspnetCore/MQTTnet.AspNetCore.csproj

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3+
<!--TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102 WIP: Fix ignored exceptions in MqttHostedServer startup #2102 -->
4+
<PropertyGroup>
5+
<DefineConstants>$(DefineConstants);HOSTEDSERVER_WITHOUT_STARTUP_YIELD</DefineConstants>
6+
</PropertyGroup>
7+
38
<PropertyGroup>
49
<TargetFramework>net8.0</TargetFramework>
510
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>

Source/MQTTnet.AspnetCore/MqttHostedServer.cs

+56
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
//TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102
6+
////WIP: Fix ignored exceptions in MqttHostedServer startup #2102
7+
#if HOSTEDSERVER_WITHOUT_STARTUP_YIELD
8+
59
using System;
610
using System.Collections.Generic;
711
using System.Threading;
@@ -35,3 +39,55 @@ public override async Task StopAsync(CancellationToken cancellationToken)
3539
await base.StopAsync(cancellationToken);
3640
}
3741
}
42+
#else
43+
44+
// Licensed to the .NET Foundation under one or more agreements.
45+
// The .NET Foundation licenses this file to you under the MIT license.
46+
// See the LICENSE file in the project root for more information.
47+
48+
using System;
49+
using System.Collections.Generic;
50+
using System.Threading;
51+
using System.Threading.Tasks;
52+
using Microsoft.Extensions.Hosting;
53+
using MQTTnet.Diagnostics.Logger;
54+
using MQTTnet.Server;
55+
56+
namespace MQTTnet.AspNetCore;
57+
58+
public sealed class MqttHostedServer : MqttServer, IHostedService
59+
{
60+
readonly IHostApplicationLifetime _hostApplicationLifetime;
61+
readonly MqttServerFactory _mqttFactory;
62+
63+
public MqttHostedServer(
64+
IHostApplicationLifetime hostApplicationLifetime,
65+
MqttServerFactory mqttFactory,
66+
MqttServerOptions options,
67+
IEnumerable<IMqttServerAdapter> adapters,
68+
IMqttNetLogger logger) : base(options, adapters, logger)
69+
{
70+
_mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
71+
_hostApplicationLifetime = hostApplicationLifetime;
72+
}
73+
74+
public async Task StartAsync(CancellationToken cancellationToken)
75+
{
76+
// The yield makes sure that the hosted service is considered up and running.
77+
await Task.Yield();
78+
79+
_hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
80+
}
81+
82+
public Task StopAsync(CancellationToken cancellationToken)
83+
{
84+
return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
85+
}
86+
87+
void OnStarted()
88+
{
89+
_ = StartAsync();
90+
}
91+
}
92+
93+
#endif

Source/MQTTnet.AspnetCore/ServiceCollectionExtensions.cs

+8
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,16 @@ public static void AddHostedMqttServer(this IServiceCollection services)
4545
services.TryAddSingleton(new MqttServerFactory());
4646

4747
services.AddSingleton<MqttHostedServer>();
48+
//TODO: remove before close https://github.com/dotnet/MQTTnet/pull/2102
49+
////WIP: Fix ignored exceptions in MqttHostedServer startup #2102
50+
#if HOSTEDSERVER_WITHOUT_STARTUP_YIELD
4851
services.AddHostedService(s => s.GetService<MqttHostedServer>());
4952
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>().MqttServer);
53+
#else
54+
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
55+
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());
56+
#endif
57+
5058
}
5159

5260
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using MQTTnet.AspNetCore;
4+
using System.Net.Sockets;
5+
using System.Net;
6+
using System.Threading.Tasks;
7+
using Microsoft.Extensions.DependencyInjection;
8+
using MQTTnet.Server;
9+
using Microsoft.AspNetCore.Hosting;
10+
11+
namespace MQTTnet.Tests.ASP
12+
{
13+
[TestClass]
14+
public class MqttHostedServerStartup
15+
{
16+
private async Task TestStartup(bool useOccupiedPort)
17+
{
18+
using TcpListener l = new TcpListener(IPAddress.Any, 0);
19+
l.Start();
20+
int port = ((IPEndPoint)l.LocalEndpoint).Port;
21+
22+
if(!useOccupiedPort)
23+
l.Stop();
24+
25+
26+
var builder = WebApplication.CreateBuilder();
27+
builder.WebHost.UseUrls("http://127.0.0.1:0");
28+
29+
builder.Services.AddMqttTcpServerAdapter();
30+
builder.Services.AddHostedMqttServer(cfg =>
31+
{
32+
cfg
33+
.WithDefaultEndpoint()
34+
.WithDefaultEndpointPort(port);
35+
});
36+
37+
38+
var app = builder.Build();
39+
40+
if(!useOccupiedPort)
41+
{
42+
await app.StartAsync();
43+
var server = app.Services.GetRequiredService<MqttServer>();
44+
Assert.IsTrue(server.IsStarted);
45+
await app.StopAsync();
46+
}
47+
else
48+
{
49+
await Assert.ThrowsExceptionAsync<SocketException>(() =>
50+
app.StartAsync()
51+
);
52+
}
53+
}
54+
55+
[TestMethod]
56+
[DoNotParallelize]
57+
public Task TestSuccessfullyStartup()
58+
=> TestStartup(useOccupiedPort: false);
59+
60+
[TestMethod]
61+
[DoNotParallelize]
62+
public Task TestFailedStartup()
63+
=> TestStartup(useOccupiedPort: true);
64+
65+
}
66+
}

0 commit comments

Comments
 (0)