Skip to content

Commit 4ea69e3

Browse files
author
rhamlett_microsoft
committed
Fix for latency probe to get correct address and port.
1 parent e9a9d25 commit 4ea69e3

File tree

1 file changed

+53
-11
lines changed

1 file changed

+53
-11
lines changed

src/PerfProblemSimulator/Services/LatencyProbeService.cs

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Microsoft.AspNetCore.SignalR;
2+
using Microsoft.AspNetCore.Hosting.Server;
3+
using Microsoft.AspNetCore.Hosting.Server.Features;
24
using PerfProblemSimulator.Hubs;
35
using System.Diagnostics;
46
using System.Net.Http;
@@ -44,10 +46,12 @@ public class LatencyProbeService : IHostedService, IDisposable
4446
private readonly IHttpClientFactory _httpClientFactory;
4547
private readonly ILogger<LatencyProbeService> _logger;
4648
private readonly IConfiguration _configuration;
49+
private readonly IServer _server;
4750

4851
private Thread? _probeThread;
4952
private CancellationTokenSource? _cts;
5053
private bool _disposed;
54+
private string? _baseUrl;
5155

5256
/// <summary>
5357
/// Probe interval in milliseconds. 100ms provides good granularity for
@@ -68,19 +72,24 @@ public LatencyProbeService(
6872
IHubContext<MetricsHub, IMetricsClient> hubContext,
6973
IHttpClientFactory httpClientFactory,
7074
ILogger<LatencyProbeService> logger,
71-
IConfiguration configuration)
75+
IConfiguration configuration,
76+
IServer server)
7277
{
7378
_hubContext = hubContext ?? throw new ArgumentNullException(nameof(hubContext));
7479
_httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
7580
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
7681
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
82+
_server = server ?? throw new ArgumentNullException(nameof(server));
7783
}
7884

7985
/// <inheritdoc />
8086
public Task StartAsync(CancellationToken cancellationToken)
8187
{
8288
_cts = new CancellationTokenSource();
8389

90+
// Get the server's actual listening address
91+
_baseUrl = GetProbeBaseUrl();
92+
8493
// Create a dedicated thread (not from thread pool) for reliable probing
8594
_probeThread = new Thread(ProbeLoop)
8695
{
@@ -90,9 +99,10 @@ public Task StartAsync(CancellationToken cancellationToken)
9099
_probeThread.Start(_cts.Token);
91100

92101
_logger.LogInformation(
93-
"Latency probe service started. Interval: {Interval}ms, Timeout: {Timeout}ms",
102+
"Latency probe service started. Interval: {Interval}ms, Timeout: {Timeout}ms, Target: {BaseUrl}",
94103
ProbeIntervalMs,
95-
RequestTimeoutMs);
104+
RequestTimeoutMs,
105+
_baseUrl);
96106

97107
return Task.CompletedTask;
98108
}
@@ -116,8 +126,11 @@ private void ProbeLoop(object? state)
116126
{
117127
var cancellationToken = (CancellationToken)state!;
118128

119-
// Get the base URL from configuration or use default
120-
var baseUrl = GetProbeBaseUrl();
129+
// Wait a moment for the server to fully start
130+
Thread.Sleep(2000);
131+
132+
// Get the base URL (may need to refresh after server starts)
133+
var baseUrl = _baseUrl ?? GetProbeBaseUrl();
121134
_logger.LogInformation("Latency probe targeting: {BaseUrl}/api/health/probe", baseUrl);
122135

123136
// Create HttpClient with timeout
@@ -228,15 +241,37 @@ private void BroadcastLatency(LatencyMeasurement measurement)
228241
/// </summary>
229242
private string GetProbeBaseUrl()
230243
{
231-
// Try to get from configuration, otherwise use sensible defaults
244+
// Try to get the actual server addresses from IServer
245+
try
246+
{
247+
var addressFeature = _server.Features.Get<IServerAddressesFeature>();
248+
if (addressFeature?.Addresses.Count > 0)
249+
{
250+
// Prefer http over https for local probing (avoid SSL overhead)
251+
var httpAddress = addressFeature.Addresses.FirstOrDefault(a => a.StartsWith("http://"));
252+
if (!string.IsNullOrEmpty(httpAddress))
253+
{
254+
// Replace wildcard with localhost
255+
return httpAddress.Replace("*", "localhost").Replace("+", "localhost").Replace("[::]", "localhost");
256+
}
257+
258+
var firstAddress = addressFeature.Addresses.First();
259+
return firstAddress.Replace("*", "localhost").Replace("+", "localhost").Replace("[::]", "localhost");
260+
}
261+
}
262+
catch (Exception ex)
263+
{
264+
_logger.LogWarning(ex, "Could not get server addresses from IServer feature");
265+
}
266+
267+
// Try to get from configuration
232268
var urls = _configuration["Urls"];
233269
if (!string.IsNullOrEmpty(urls))
234270
{
235-
// Take the first URL if multiple are configured
236271
var firstUrl = urls.Split(';')[0].Trim();
237272
if (!string.IsNullOrEmpty(firstUrl))
238273
{
239-
return firstUrl;
274+
return firstUrl.Replace("*", "localhost").Replace("+", "localhost");
240275
}
241276
}
242277

@@ -247,12 +282,19 @@ private string GetProbeBaseUrl()
247282
var firstUrl = envUrls.Split(';')[0].Trim();
248283
if (!string.IsNullOrEmpty(firstUrl))
249284
{
250-
return firstUrl;
285+
return firstUrl.Replace("*", "localhost").Replace("+", "localhost");
251286
}
252287
}
253288

254-
// Default to localhost on standard ports
255-
return "http://localhost:5000";
289+
// Check applicationUrl from launchSettings (common development scenario)
290+
var appUrl = _configuration["applicationUrl"];
291+
if (!string.IsNullOrEmpty(appUrl))
292+
{
293+
return appUrl.Split(';')[0].Trim();
294+
}
295+
296+
// Default to localhost on the common development port
297+
return "http://localhost:5221";
256298
}
257299

258300
/// <inheritdoc />

0 commit comments

Comments
 (0)