Avoid nullref in KestrelEventSource (#2483)
* Avoid nullref in KestrelEventSource * Improve HostNameIsReachableAttribute to speed up test discovery
This commit is contained in:
parent
ee12c4fcf2
commit
10f3b6863e
|
|
@ -32,8 +32,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
{
|
||||
ConnectionStart(
|
||||
connection.ConnectionId,
|
||||
connection.LocalEndPoint.ToString(),
|
||||
connection.RemoteEndPoint.ToString());
|
||||
connection.LocalEndPoint?.ToString(),
|
||||
connection.RemoteEndPoint?.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[NetworkIsReachable]
|
||||
[HostNameIsReachable]
|
||||
public async Task RegisterAddresses_HostName_Success()
|
||||
{
|
||||
var hostName = Dns.GetHostName();
|
||||
|
|
@ -308,7 +308,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[NetworkIsReachable]
|
||||
[HostNameIsReachable]
|
||||
public async Task ListenAnyIP_HostName_Success()
|
||||
{
|
||||
var hostName = Dns.GetHostName();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class HostNameIsReachableAttribute : Attribute, ITestCondition
|
||||
{
|
||||
private string _hostname;
|
||||
private string _error;
|
||||
private bool? _isMet;
|
||||
|
||||
public bool IsMet
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isMet ?? (_isMet = HostNameIsReachable().GetAwaiter().GetResult()).Value;
|
||||
}
|
||||
}
|
||||
|
||||
public string SkipReason => _hostname != null
|
||||
? $"Test cannot run when network is unreachable. Socket exception: '{_error}'"
|
||||
: "Could not determine hostname for current test machine";
|
||||
|
||||
private async Task<bool> HostNameIsReachable()
|
||||
{
|
||||
try
|
||||
{
|
||||
_hostname = Dns.GetHostName();
|
||||
|
||||
// if the network is unreachable on macOS, throws with SocketError.NetworkUnreachable
|
||||
// if the network device is not configured, throws with SocketError.HostNotFound
|
||||
// if the network is reachable, throws with SocketError.ConnectionRefused or succeeds
|
||||
var timeoutTask = Task.Delay(1000);
|
||||
if (await Task.WhenAny(ConnectToHost(_hostname, 80), timeoutTask) == timeoutTask)
|
||||
{
|
||||
_error = "Attempt to establish a connection took over a second without success or failure.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (SocketException ex) when (
|
||||
ex.SocketErrorCode == SocketError.NetworkUnreachable
|
||||
|| ex.SocketErrorCode == SocketError.HostNotFound)
|
||||
{
|
||||
_error = ex.Message;
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow other errors. Allows the test to throw the failures instead
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async Task<Socket> ConnectToHost(string hostName, int port)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<Socket>();
|
||||
|
||||
var socketArgs = new SocketAsyncEventArgs();
|
||||
socketArgs.RemoteEndPoint = new DnsEndPoint(hostName, port);
|
||||
socketArgs.Completed += (s, e) => tcs.TrySetResult(e.ConnectSocket);
|
||||
|
||||
// Must use static ConnectAsync(), since instance Connect() does not support DNS names on OSX/Linux.
|
||||
if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, socketArgs))
|
||||
{
|
||||
await tcs.Task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var socket = socketArgs.ConnectSocket;
|
||||
|
||||
if (socket == null)
|
||||
{
|
||||
throw new SocketException((int)socketArgs.SocketError);
|
||||
}
|
||||
else
|
||||
{
|
||||
return socket;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class NetworkIsReachableAttribute : Attribute, ITestCondition
|
||||
{
|
||||
private string _hostname;
|
||||
private string _error;
|
||||
|
||||
public bool IsMet
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
_hostname = Dns.GetHostName();
|
||||
|
||||
// if the network is unreachable on macOS, throws with SocketError.NetworkUnreachable
|
||||
// if the network device is not configured, throws with SocketError.HostNotFound
|
||||
// if the network is reachable, throws with SocketError.ConnectionRefused or succeeds
|
||||
HttpClientSlim.GetStringAsync($"http://{_hostname}").GetAwaiter().GetResult();
|
||||
}
|
||||
catch (SocketException ex) when (
|
||||
ex.SocketErrorCode == SocketError.NetworkUnreachable
|
||||
|| ex.SocketErrorCode == SocketError.HostNotFound)
|
||||
{
|
||||
_error = ex.Message;
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow other errors. Allows the test to throw the failures instead
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public string SkipReason => _hostname != null
|
||||
? $"Test cannot run when network is unreachable. Socket exception: '{_error}'"
|
||||
: "Could not determine hostname for current test machine";
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue