Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
2cd4296
Added verbose TCP test logic
jackdcasey Dec 25, 2019
1337193
Cleaned up output, removed source IP address
jackdcasey Dec 25, 2019
d7b4368
Merge branch 'master' of https://github.com/PowerShell/PowerShell int…
jackdcasey Dec 27, 2019
9325584
Consolidated detailed output logic into ProcessConnectionByTCPPort()
jackdcasey Dec 27, 2019
c005933
Updated existing tests with -Quiet switch
jackdcasey Dec 27, 2019
1698fc5
Improved output formatting, changed logic to match
jackdcasey Dec 28, 2019
34d56af
Error handling and logic improvements
jackdcasey Dec 28, 2019
3896c98
Updated tests to match new output
jackdcasey Dec 28, 2019
1b6c06d
Updated logic, tcp tests now run once by default
jackdcasey Dec 29, 2019
cdbcd07
Moved TcpConnectionTestResult enum into TcpTestStatus class
jackdcasey Dec 29, 2019
a5d3d6c
Removed unnecessary whitespace
jackdcasey Dec 29, 2019
8cddfbf
Wrapped TcpClient() in Using statement
jackdcasey Dec 29, 2019
55f598e
Appended missing period on comments
jackdcasey Dec 29, 2019
7cb7621
Change TcpTestStatus to TcpPortStatus for better clarity
jackdcasey Dec 29, 2019
c0960d8
Set default count back to 4
jackdcasey Dec 30, 2019
6edaa80
Move stopwatch reset into finally block
jackdcasey Dec 30, 2019
aa19612
Change TcpConnectionTestResult.New to TcpConnectionTestResult.None
jackdcasey Dec 30, 2019
dd904cc
Formatting fixes
jackdcasey Dec 30, 2019
24053d3
Increase column size for Result output
jackdcasey Jan 2, 2020
6e6d9d2
Update logic for -quiet option
jackdcasey Jan 2, 2020
042ddba
Change "TestNum" property to "Id"
jackdcasey Jan 2, 2020
69091a8
Change Destination and DestinationAddress to Target and TargetAddress…
jackdcasey Jan 3, 2020
bb52703
Implement new form of using declaration
jackdcasey Jan 3, 2020
9d7bbb8
Fix indentation
jackdcasey Jan 6, 2020
f6bf3b6
Update output, improve logic and update tests
jackdcasey Jan 7, 2020
fa04847
Add CancellationToken to allow cancel before timeout
jackdcasey Jan 8, 2020
5787821
Assign testResult.Connected to client.Connected
jackdcasey Jan 9, 2020
750b3a4
Move TcpPortStatus object creation to after connection test
jackdcasey Jan 13, 2020
0e83977
Remove delay from final connection test in loop
jackdcasey Jan 13, 2020
8a6011b
Move Count logic from ProcessConnectionByTCPPort into own function
jackdcasey Jan 13, 2020
9061872
Merge branch 'master' of https://github.com/PowerShell/PowerShell int…
jackdcasey Jan 22, 2020
c4a46b7
Fix small formatting error
jackdcasey Jan 22, 2020
19b560a
Add handling for TaskCanceledException
jackdcasey Jan 22, 2020
bd632df
Merge branch 'master' of https://github.com/PowerShell/PowerShell int…
jackdcasey Jan 23, 2020
9196819
Remove redundant CancellationToken getter
jackdcasey Jan 23, 2020
0546764
Clean up handling of SocketException
jackdcasey Jan 24, 2020
c899f2c
Increase readability of final loop check
jackdcasey Jan 25, 2020
558bda4
Remove unnecessary TimeSpan invocation
jackdcasey Jan 27, 2020
48d9434
Rename cancellationTokenSource to match convention
jackdcasey Jan 27, 2020
980bcbe
Move SetCountForTcpTest() into BeginProcessing()
jackdcasey Jan 27, 2020
a44c3a3
Added nameof to Count check
jackdcasey Jan 30, 2020
b7be2d3
Removed explicit type declaration for TcpClient
jackdcasey Jan 30, 2020
9c9beb0
Move delay and timeout evaluation out of the cycle
jackdcasey Jan 31, 2020
bb28039
Merge branch 'master' into TestConnection-AddVerboseTcpTest
jackdcasey Jun 26, 2020
ff24361
Remove redundant CancellationTokenSource
jackdcasey Jun 26, 2020
2a8aa2a
Initialize detailed SwitchParameter
jackdcasey Jul 9, 2020
f92cde7
Add check for Detailed parameter
jackdcasey Jul 9, 2020
6816d02
Remove redundant tests for non-detailed output
jackdcasey Jul 9, 2020
629bb36
Add return after first boolean output
jackdcasey Jul 9, 2020
fd28410
Update tests with new detailed parameter set
jackdcasey Jul 9, 2020
22ca7a4
Merge branch 'master' into TestConnection-AddVerboseTcpTest
TravisEz13 May 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace Microsoft.PowerShell.Commands
[OutputType(typeof(PingMtuStatus), ParameterSetName = new string[] { MtuSizeDetectParameterSet })]
[OutputType(typeof(int), ParameterSetName = new string[] { MtuSizeDetectParameterSet })]
[OutputType(typeof(TraceStatus), ParameterSetName = new string[] { TraceRouteParameterSet })]
[OutputType(typeof(TcpPortStatus), ParameterSetName = new string[] { TcpPortParameterSet })]
public class TestConnectionCommand : PSCmdlet, IDisposable
{
#region Parameter Set Names
Expand Down Expand Up @@ -134,6 +135,7 @@ public class TestConnectionCommand : PSCmdlet, IDisposable
/// The default (from Windows) is 4 times.
/// </summary>
[Parameter(ParameterSetName = DefaultPingParameterSet)]
[Parameter(ParameterSetName = TcpPortParameterSet)]
[ValidateRange(ValidateRangeKind.Positive)]
public int Count { get; set; } = 4;

Expand All @@ -143,6 +145,7 @@ public class TestConnectionCommand : PSCmdlet, IDisposable
/// </summary>
[Parameter(ParameterSetName = DefaultPingParameterSet)]
[Parameter(ParameterSetName = RepeatPingParameterSet)]
[Parameter(ParameterSetName = TcpPortParameterSet)]
[ValidateRange(ValidateRangeKind.Positive)]
public int Delay { get; set; } = 1;

Expand All @@ -169,6 +172,7 @@ public class TestConnectionCommand : PSCmdlet, IDisposable
/// Gets or sets whether to continue pinging until user presses Ctrl-C (or Int.MaxValue threshold reached).
/// </summary>
[Parameter(Mandatory = true, ParameterSetName = RepeatPingParameterSet)]
[Parameter(ParameterSetName = TcpPortParameterSet)]
[Alias("Continuous")]
public SwitchParameter Repeat { get; set; }

Expand All @@ -180,6 +184,13 @@ public class TestConnectionCommand : PSCmdlet, IDisposable
[Parameter]
public SwitchParameter Quiet { get; set; }

/// <summary>
/// Gets or sets whether to enable detailed output mode while running a TCP connection test.
/// Without this flag, the TCP test will return a boolean result.
/// </summary>
[Parameter]
public SwitchParameter Detailed;

/// <summary>
/// Gets or sets the timeout value for an individual ping in seconds.
/// If a response is not received in this time, no response is assumed.
Expand Down Expand Up @@ -227,6 +238,7 @@ public class TestConnectionCommand : PSCmdlet, IDisposable

/// <summary>
/// BeginProcessing implementation for TestConnectionCommand.
/// Sets Count for different types of tests unless specified explicitly.
/// </summary>
protected override void BeginProcessing()
{
Expand All @@ -235,6 +247,9 @@ protected override void BeginProcessing()
case RepeatPingParameterSet:
Count = int.MaxValue;
break;
case TcpPortParameterSet:
SetCountForTcpTest();
break;
}
}

Expand Down Expand Up @@ -281,6 +296,18 @@ protected override void StopProcessing()

#region ConnectionTest

private void SetCountForTcpTest()
{
if (Repeat.IsPresent)
{
Count = int.MaxValue;
}
else if (!MyInvocation.BoundParameters.ContainsKey(nameof(Count)))
{
Count = 1;
}
}

private void ProcessConnectionByTCPPort(string targetNameOrAddress)
{
if (!TryResolveNameOrAddress(targetNameOrAddress, out _, out IPAddress? targetAddress))
Expand All @@ -293,42 +320,80 @@ private void ProcessConnectionByTCPPort(string targetNameOrAddress)
return;
}

TcpClient client = new();
int timeoutMilliseconds = TimeoutSeconds * 1000;
int delayMilliseconds = Delay * 1000;

try
for (var i = 1; i <= Count; i++)
{
Task connectionTask = client.ConnectAsync(targetAddress, TcpPort);
string targetString = targetAddress.ToString();
long latency = 0;
SocketError status = SocketError.SocketError;

Stopwatch stopwatch = new Stopwatch();

for (var i = 1; i <= TimeoutSeconds; i++)
using var client = new TcpClient();

try
{
Task timeoutTask = Task.Delay(millisecondsDelay: 1000);
Task.WhenAny(connectionTask, timeoutTask).Result.Wait();
stopwatch.Start();

if (timeoutTask.Status == TaskStatus.Faulted || timeoutTask.Status == TaskStatus.Canceled)
if (client.ConnectAsync(targetAddress, TcpPort).Wait(timeoutMilliseconds, _dnsLookupCancel.Token))
{
// Waiting is interrupted by Ctrl-C.
WriteObject(false);
return;
latency = stopwatch.ElapsedMilliseconds;
status = SocketError.Success;
}

if (connectionTask.Status == TaskStatus.RanToCompletion)
else
{
WriteObject(true);
return;
status = SocketError.TimedOut;
}
}
}
catch
{
// Silently ignore connection errors.
}
finally
{
client.Close();
}
catch (AggregateException ae)
{
ae.Handle((ex) =>
{
if (ex is TaskCanceledException)
{
throw new PipelineStoppedException();
}
if (ex is SocketException socketException)
{
status = socketException.SocketErrorCode;
return true;
}
else
{
return false;
}
});
}
finally
{
stopwatch.Reset();
}

if (!Detailed.IsPresent)
{
WriteObject(status == SocketError.Success);
return;
}
else
{
WriteObject(new TcpPortStatus(
i,
Source,
targetNameOrAddress,
targetAddress,
TcpPort,
latency,
status == SocketError.Success,
status
));
}

WriteObject(false);
if (i < Count)
{
Task.Delay(delayMilliseconds).Wait(_dnsLookupCancel.Token);
}
}
}

#endregion ConnectionTest
Expand Down Expand Up @@ -875,6 +940,75 @@ private PingReply SendCancellablePing(
}
}

/// <summary>
/// The class contains information about the TCP connection test.
/// </summary>
public class TcpPortStatus
{
/// <summary>
/// Initializes a new instance of the <see cref="TcpPortStatus"/> class.
/// </summary>
/// <param name="id">The number of this test.</param>
/// <param name="source">The source machine name or IP of the test.</param>
/// <param name="target">The target machine name or IP of the test.</param>
/// <param name="targetAddress">The resolved IP from the target.</param>
/// <param name="port">The port used for the connection.</param>
/// <param name="latency">The latency of the test.</param>
/// <param name="connected">If the test connection succeeded.</param>
/// <param name="status">Status of the underlying socket.</param>
internal TcpPortStatus(int id, string source, string target, IPAddress targetAddress, int port, long latency, bool connected, SocketError status)
{
Id = id;
Source = source;
Target = target;
TargetAddress = targetAddress;
Port = port;
Latency = latency;
Connected = connected;
Status = status;
}

/// <summary>
/// Gets and sets the count of the test.
/// </summary>
public int Id { get; set; }

/// <summary>
/// Gets the source from which the test was sent.
/// </summary>
public string Source { get; }

/// <summary>
/// Gets the target name.
/// </summary>
public string Target { get; }

/// <summary>
/// Gets the resolved address for the target.
/// </summary>
public IPAddress TargetAddress { get; }

/// <summary>
/// Gets the port used for the test.
/// </summary>
public int Port { get; }

/// <summary>
/// Gets or sets the latancy of the connection.
/// </summary>
public long Latency { get; set; }

/// <summary>
/// Gets or sets the result of the test.
/// </summary>
public bool Connected { get; set; }

/// <summary>
/// Gets or sets the state of the socket after the test.
/// </summary>
public SocketError Status { get; set; }
}

/// <summary>
/// The class contains information about the source, the destination and ping results.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ internal static IEnumerable<ExtendedTypeDefinition> GetFormatData()
"Microsoft.PowerShell.MarkdownRender.PSMarkdownOptionInfo",
ViewsOf_Microsoft_PowerShell_MarkdownRender_MarkdownOptionInfo());

yield return new ExtendedTypeDefinition(
"Microsoft.PowerShell.Commands.TestConnectionCommand+TcpPortStatus",
ViewsOf_Microsoft_PowerShell_Commands_TestConnectionCommand_TcpPortStatus());

yield return new ExtendedTypeDefinition(
"Microsoft.PowerShell.Commands.TestConnectionCommand+PingStatus",
ViewsOf_Microsoft_PowerShell_Commands_TestConnectionCommand_PingStatus());
Expand Down Expand Up @@ -1924,6 +1928,31 @@ private static IEnumerable<FormatViewDefinition> ViewsOf_Microsoft_PowerShell_Ma
.EndList());
}

private static IEnumerable<FormatViewDefinition> ViewsOf_Microsoft_PowerShell_Commands_TestConnectionCommand_TcpPortStatus()
{
yield return new FormatViewDefinition(
"Microsoft.PowerShell.Commands.TestConnectionCommand+TcpPortStatus",
TableControl.Create()
.AddHeader(Alignment.Right, label: "Id", width: 4)
.AddHeader(Alignment.Left, label: "Source", width: 16)
.AddHeader(Alignment.Left, label: "Address", width: 25)
.AddHeader(Alignment.Right, label: "Port", width: 7)
.AddHeader(Alignment.Right, label: "Latency(ms)", width: 7)
.AddHeader(Alignment.Left, label: "Connected", width: 10)
.AddHeader(Alignment.Left, label: "Status", width: 24)
.StartRowDefinition()
.AddPropertyColumn("Id")
.AddPropertyColumn("Source")
.AddPropertyColumn("TargetAddress")
.AddPropertyColumn("Port")
.AddPropertyColumn("Latency")
.AddPropertyColumn("Connected")
.AddPropertyColumn("Status")
.EndRowDefinition()
.GroupByProperty("Target")
.EndTable());
}

private static IEnumerable<FormatViewDefinition> ViewsOf_Microsoft_PowerShell_Commands_TestConnectionCommand_PingStatus()
{
yield return new FormatViewDefinition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,49 @@ Describe "Connection" -Tag "CI", "RequireAdminOnWindows" {
$UnreachableAddress = "10.11.12.13"
}

It "Test connection to local host port 80" {
It "Test connection to local host on working port" {
Test-Connection '127.0.0.1' -TcpPort $WebListener.HttpPort | Should -BeTrue
}

It "Test connection to unreachable host port 80" {
Test-Connection $UnreachableAddress -TcpPort 80 -TimeOut 1 | Should -BeFalse
}

It "Test detailed connection to local host on working port" {
$result = Test-Connection '127.0.0.1' -TcpPort $WebListener.HttpPort -Detailed

$result.Count | Should -Be 1
$result[0].Id | Should -BeExactly 1
$result[0].TargetAddress | Should -BeExactly '127.0.0.1'
$result[0].Port | Should -Be $WebListener.HttpPort
$result[0].Latency | Should -BeGreaterOrEqual 0
$result[0].Connected | Should -BeTrue
$result[0].Status | Should -BeExactly 'Success'
}

It "Test detailed connection to local host on working port with modified count" {
$result = Test-Connection '127.0.0.1' -TcpPort $WebListener.HttpPort -Detailed -Count 2

$result.Count | Should -Be 2
$result[0].Id | Should -BeExactly 1
$result[0].TargetAddress | Should -BeExactly '127.0.0.1'
$result[0].Port | Should -Be $WebListener.HttpPort
$result[0].Latency | Should -BeGreaterOrEqual 0
$result[0].Connected | Should -BeTrue
$result[0].Status | Should -BeExactly 'Success'
}

It "Test detailed connection to unreachable host port 80" {
$result = Test-Connection $UnreachableAddress -TcpPort 80 -Detailed -TimeOut 1

$result.Count | Should -Be 1
$result[0].Id | Should -BeExactly 1
$result[0].TargetAddress | Should -BeExactly $UnreachableAddress
$result[0].Port | Should -Be 80
$result[0].Latency | Should -BeExactly 0
$result[0].Connected | Should -BeFalse
$result[0].Status | Should -Not -BeExactly 'Success'
}
}

Describe "Test-Connection should run in the default synchronization context (threadpool)" -Tag "CI" {
Expand Down