You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
6.7 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
namespace DCT_test
{
// Uses the production WinRT bridge callback and copy loops on loopback.
// Radio discovery, WPS and a physical tablet are deliberately outside this test.
internal static class VerifyWifiDirect
{
private sealed class Bridge : IDisposable
{
private readonly StreamSocketListener listener = new StreamSocketListener();
private readonly WifiDirectHost host;
private readonly CancellationTokenSource cancellation = new CancellationTokenSource();
private int receivedConnections;
public Bridge(IPEndPoint server)
{
host = new WifiDirectHost(server.Port);
typeof(WifiDirectHost).GetField("_cts", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(host, cancellation);
var callback = typeof(WifiDirectHost).GetMethod("SocketListener_ConnectionReceived",
BindingFlags.Instance | BindingFlags.NonPublic);
listener.Control.NoDelay = true;
listener.ConnectionReceived += (sender, args) =>
{
Interlocked.Increment(ref receivedConnections);
callback.Invoke(host, new object[] { sender, args });
};
listener.BindEndpointAsync(new HostName("127.0.0.1"), "0")
.AsTask().GetAwaiter().GetResult();
}
public TcpClient Connect()
{
var client = new TcpClient { NoDelay = true };
client.Connect(IPAddress.Loopback, int.Parse(listener.Information.LocalPort));
if (!SpinWait.SpinUntil(() => Volatile.Read(ref receivedConnections) > 0, 2000))
{
client.Dispose();
throw new Exception("WinRT listener did not accept the test client");
}
return client;
}
public void Dispose()
{
host.Dispose();
listener.Dispose();
// The async bridge can still observe cancellation while disposing its I/O.
}
}
private static int Main()
{
try
{
TransportRegression.RunCurrentAppCompatibility();
foreach (bool fragmented in new[] { false, true })
{
var bridges = new List<Bridge>();
try
{
TransportRegression.RunCurrentAppCompatibility(endpoint =>
{
var bridge = new Bridge(endpoint);
bridges.Add(bridge);
return bridge.Connect();
}, fragmented);
Console.WriteLine("PASS: production Wi-Fi Direct WinRT bridge protocol parity; fragmented=" + fragmented);
}
finally { foreach (var bridge in bridges) bridge.Dispose(); }
}
VerifyBridgeBytes();
return 0;
}
catch (Exception ex) { Console.Error.WriteLine(ex); return 1; }
}
private static void VerifyBridgeBytes()
{
var server = new TcpListener(IPAddress.Loopback, 0);
server.Start();
try
{
using (var bridge = new Bridge((IPEndPoint)server.LocalEndpoint))
using (var peer = bridge.Connect())
using (var target = server.AcceptTcpClient())
{
var peerStream = peer.GetStream();
var targetStream = target.GetStream();
peerStream.ReadTimeout = targetStream.ReadTimeout = 5000;
peerStream.WriteTimeout = targetStream.WriteTimeout = 5000;
byte[] payload = new byte[2 * 1024 * 1024];
for (int i = 0; i < payload.Length; i++) payload[i] = (byte)(i * 37 + i / 251);
var up = Task.Run(() => peerStream.Write(payload, 0, payload.Length));
CheckBytes(targetStream, payload);
up.GetAwaiter().GetResult();
var down = Task.Run(() => targetStream.Write(payload, 0, payload.Length));
Thread.Sleep(1800); // A temporary paused receiver must keep byte order and the connection.
CheckBytes(peerStream, payload);
down.GetAwaiter().GetResult();
targetStream.WriteByte(0x85);
if (peerStream.ReadByte() != 0x85) throw new Exception("Control byte after receiver pause");
target.Close();
try
{
if (peerStream.ReadByte() != -1) throw new Exception("Bridge must propagate server close");
}
catch (IOException ex)
{
// Disposing a WinRT socket with its upstream read pending can
// surface a TCP reset. Only accept that after our explicit close,
// after every payload byte and the following control were checked.
var socketError = ex.InnerException as SocketException;
if (socketError == null || (socketError.SocketErrorCode != System.Net.Sockets.SocketError.ConnectionReset &&
socketError.SocketErrorCode != System.Net.Sockets.SocketError.ConnectionAborted)) throw;
}
Console.WriteLine("PASS: production WinRT bridge 2MiB each direction, exact bytes, 1.8s reader pause, control after pause, close propagation");
}
}
finally { server.Stop(); }
}
private static void CheckBytes(Stream source, byte[] expected)
{
byte[] buffer = new byte[1777];
int offset = 0;
while (offset < expected.Length)
{
int count = source.Read(buffer, 0, Math.Min(buffer.Length, expected.Length - offset));
if (count == 0) throw new EndOfStreamException("Bridge closed before all bytes arrived");
for (int i = 0; i < count; i++)
if (buffer[i] != expected[offset + i]) throw new Exception("Bridge changed byte " + (offset + i));
offset += count;
}
}
}
}