ASP.NET controller action to test TCP connectivity from the provider URI.
/// <summary>
/// Tests TCP connectivity to a specified service URI with retry logic (max of 3 attempts, with 5 second timeouts).
/// </summary>
/// <param name="uri">Service URI including protocol, host, and port (e.g., snpp://9999999999@snpp.example.net:444, wctp://9999999999@wctp.example.net:80), https://google.com</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Connectivity test results including success status, attempt details, and timing information.</returns>
/// <response code="200">Connectivity test completed successfully. Check the 'connected' field in response for actual connectivity status.</response>
/// <response code="400">Invalid URI format or missing required parameters.</response>
/// <example>
/// GET /api/health/connectivity?uri=snpp://9999999999@snpp.example.net:444
/// </example>
[HttpGet("connectivity")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(object), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> TestConnectivity(
[FromQuery] [System.ComponentModel.DataAnnotations.Required] string uri,
CancellationToken cancellationToken = default
)
{
if (string.IsNullOrWhiteSpace(uri))
{
return BadRequest(new { error = "URI parameter is required" });
}
try
{
var parsedUri = new Uri(uri);
var host = parsedUri.Host;
var port = parsedUri.Port;
if (port == -1)
{
return BadRequest(new { error = "Port must be specified in URI" });
}
const int maxRetries = 3;
const int timeoutMs = 5000;
var startTime = DateTime.UtcNow;
var attempts = new List<object>();
var connected = false;
for (var attempt = 1; attempt <= maxRetries; attempt++)
{
var attemptStart = DateTime.UtcNow;
try
{
using var tcpClient = new TcpClient();
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeoutCts.CancelAfter(timeoutMs);
await tcpClient.ConnectAsync(host, port, timeoutCts.Token);
var attemptDuration = DateTime.UtcNow - attemptStart;
attempts.Add(new
{
attempt,
success = true,
duration = attemptDuration.TotalMilliseconds,
error = null as string,
exceptionType = null as string
});
connected = true;
break;
}
catch (OperationCanceledException ex) when (cancellationToken.IsCancellationRequested)
{
var attemptDuration = DateTime.UtcNow - attemptStart;
attempts.Add(new
{
attempt,
success = false,
duration = attemptDuration.TotalMilliseconds,
error = "Operation was cancelled by user",
exceptionType = ex.GetType().Name
});
}
catch (OperationCanceledException ex)
{
var attemptDuration = DateTime.UtcNow - attemptStart;
attempts.Add(new
{
attempt,
success = false,
duration = attemptDuration.TotalMilliseconds,
error = $"Connection timeout after {timeoutMs}ms: {ex.Message}",
exceptionType = ex.GetType().Name
});
}
catch (Exception ex)
{
var attemptDuration = DateTime.UtcNow - attemptStart;
attempts.Add(new
{
attempt,
success = false,
duration = attemptDuration.TotalMilliseconds,
error = ex.InnerException?.Message ?? ex.Message,
exceptionType = ex.GetType().Name
});
}
}
var totalDuration = DateTime.UtcNow - startTime;
var response = new
{
uri,
host,
port,
connected,
totalDuration = totalDuration.TotalMilliseconds,
attempts,
timestamp = DateTime.UtcNow
};
return Ok(response);
}
catch (UriFormatException)
{
return BadRequest(new { error = "Invalid URI format" });
}
catch (Exception ex)
{
return BadRequest(new { error = $"Error parsing URI: {ex.Message}" });
}
}