Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring tests to support both external and loopback servers #74579

Closed
Original file line number Diff line number Diff line change
Expand Up @@ -1024,4 +1024,75 @@ public override Task WaitForCloseAsync(CancellationToken cancellationToken)
throw new NotImplementedException();
}
}

#if !NETFRAMEWORK
public sealed class Http2Stream : Stream
{
private readonly Http2LoopbackConnection _connection;
private readonly int _streamId;
private bool _readEnded;
private ReadOnlyMemory<byte> _leftoverReadData;

public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;

public Http2Stream(Http2LoopbackConnection connection, int streamId)
{
_connection = connection;
_streamId = streamId;
}

public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
if (!_leftoverReadData.IsEmpty)
{
int read = Math.Min(buffer.Length, _leftoverReadData.Length);
_leftoverReadData.Span.Slice(0, read).CopyTo(buffer.Span);
_leftoverReadData = _leftoverReadData.Slice(read);
return read;
}

if (_readEnded)
{
return 0;
}

DataFrame dataFrame = (DataFrame)await _connection.ReadDataFrameAsync();
Assert.Equal(_streamId, dataFrame.StreamId);
_leftoverReadData = dataFrame.Data;
_readEnded = dataFrame.EndStreamFlag;

return await ReadAsync(buffer, cancellationToken);
}

public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();

public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
await _connection.SendResponseDataAsync(_streamId, buffer, endStream: false);
}

public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();

protected override void Dispose(bool disposing) => DisposeAsync().GetAwaiter().GetResult();

public override async ValueTask DisposeAsync()
{
await _connection.SendResponseDataAsync(_streamId, Memory<byte>.Empty, endStream: true);
}

public override void Flush() { }
public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask;

public override int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException();
public override long Seek(long offset, SeekOrigin origin) => throw new NotImplementedException();
public override void SetLength(long value) => throw new NotImplementedException();
public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException();
public override long Length => throw new NotImplementedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public async Task Proxy_ConnectThruProxy_Success(Uri server)
server,
TimeOutMilliseconds,
_output,
UseVersion,
default(TimeSpan),
proxy))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Xunit.Abstractions;
using System.Net.Http;
using System.Diagnostics;
using System.Net.Test.Common;

namespace System.Net.WebSockets.Client.Tests
{
Expand Down Expand Up @@ -56,7 +57,7 @@ public static IEnumerable<object[]> UnavailableWebSocketServers
{
server = System.Net.Test.Common.Configuration.Http.RemoteEchoServer;
var ub = new UriBuilder("ws", server.Host, server.Port, server.PathAndQuery);
exceptionMessage = ResourceHelper.GetExceptionMessage("net_WebSockets_ConnectStatusExpected", (int) HttpStatusCode.OK, (int) HttpStatusCode.SwitchingProtocols);
exceptionMessage = ResourceHelper.GetExceptionMessage("net_WebSockets_ConnectStatusExpected", (int)HttpStatusCode.OK, (int)HttpStatusCode.SwitchingProtocols);

yield return new object[] { ub.Uri, exceptionMessage, WebSocketError.NotAWebSocket };
}
Expand All @@ -65,7 +66,7 @@ public static IEnumerable<object[]> UnavailableWebSocketServers

public async Task TestCancellation(Func<ClientWebSocket, Task> action, Uri server)
{
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output))
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(server, TimeOutMilliseconds, _output, UseVersion))
{
try
{
Expand Down Expand Up @@ -111,6 +112,9 @@ protected static async Task<WebSocketReceiveResult> ReceiveEntireMessageAsync(We
protected virtual bool UseCustomInvoker => false;

protected virtual bool UseHttpClient => false;
protected virtual Version UseVersion => HttpVersion.Version11;
protected virtual bool UseRemoteServer => false;
protected virtual bool UseSsl => false;

protected bool UseSharedHandler => !UseCustomInvoker && !UseHttpClient;

Expand Down Expand Up @@ -141,14 +145,45 @@ protected static async Task<WebSocketReceiveResult> ReceiveEntireMessageAsync(We
return null;
}

protected Task CreateEchoServerAsync(Func<Uri, Task> clientFunc)
{
if (UseRemoteServer)
{
if (UseVersion.Major == 2)
{
throw new NotSupportedException("Remote servers don't support WebSockets over HTTP/2 yet");
}

if (UseSsl)
{
return clientFunc(Test.Common.Configuration.WebSockets.SecureRemoteEchoServer);
}
else
{
return clientFunc(Test.Common.Configuration.WebSockets.RemoteEchoServer);
}
}
else
{
if (UseVersion.Major == 2)
{
return WebSocketHelper.GetEchoHttp2LoopbackServer(clientFunc, new Http2Options() { WebSocketEndpoint = true, UseSsl = UseSsl });
}
else
{
return WebSocketHelper.GetEchoLoopbackServer(clientFunc, new LoopbackServer.Options() { WebSocketEndpoint = true, UseSsl = UseSsl }); ;
}
}
}

protected Task<ClientWebSocket> GetConnectedWebSocket(Uri uri, int TimeOutMilliseconds, ITestOutputHelper output) =>
WebSocketHelper.GetConnectedWebSocket(uri, TimeOutMilliseconds, output, invoker: GetInvoker());
WebSocketHelper.GetConnectedWebSocket(uri, TimeOutMilliseconds, output, version: UseVersion, invoker: GetInvoker());

protected Task ConnectAsync(ClientWebSocket cws, Uri uri, CancellationToken cancellationToken) =>
cws.ConnectAsync(uri, GetInvoker(), cancellationToken);

protected Task TestEcho(Uri uri, WebSocketMessageType type, int timeOutMilliseconds, ITestOutputHelper output) =>
WebSocketHelper.TestEcho(uri, WebSocketMessageType.Text, TimeOutMilliseconds, _output, GetInvoker());
WebSocketHelper.TestEcho(uri, WebSocketMessageType.Text, TimeOutMilliseconds, _output, UseVersion, GetInvoker());

public static bool WebSocketsSupported { get { return WebSocketHelper.WebSocketsSupported; } }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public KeepAliveTest(ITestOutputHelper output) : base(output) { }
[OuterLoop] // involves long delay
public async Task KeepAlive_LongDelayBetweenSendReceives_Succeeds()
{
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(System.Net.Test.Common.Configuration.WebSockets.RemoteEchoServer, TimeOutMilliseconds, _output, TimeSpan.FromSeconds(1)))
using (ClientWebSocket cws = await WebSocketHelper.GetConnectedWebSocket(System.Net.Test.Common.Configuration.WebSockets.RemoteEchoServer, TimeOutMilliseconds, _output, UseVersion, TimeSpan.FromSeconds(1)))
{
await cws.SendAsync(new ArraySegment<byte>(new byte[1] { 42 }), WebSocketMessageType.Binary, true, CancellationToken.None);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,98 +12,67 @@

namespace System.Net.WebSockets.Client.Tests
{
public sealed class HttpClientSendReceiveTest_Http2 : SendReceiveTest_Http2
{
public HttpClientSendReceiveTest_Http2(ITestOutputHelper output) : base(output) { }
// Memory segment

protected override bool UseHttpClient => true;
// Invoker
[SkipOnPlatform(TestPlatforms.Browser, "HTTP/2 WebSockets aren't supported on Browser")]
public sealed class InvokerMemorySendReceiveLocalTest_Http2 : InvokerMemorySendReceiveLocalTest
{
public InvokerMemorySendReceiveLocalTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

public sealed class InvokerSendReceiveTest_Http2 : SendReceiveTest_Http2
[SkipOnPlatform(TestPlatforms.Browser, "Self-signed certificates are not supported on browser")]
public sealed class InvokerMemorySendReceiveLocalSslTest_Http2 : InvokerMemorySendReceiveLocalSslTest
{
public InvokerSendReceiveTest_Http2(ITestOutputHelper output) : base(output) { }

protected override bool UseCustomInvoker => true;
public InvokerMemorySendReceiveLocalSslTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

public abstract class SendReceiveTest_Http2 : ClientWebSocketTestBase
//HttpClient
[SkipOnPlatform(TestPlatforms.Browser, "HTTP/2 WebSockets aren't supported on Browser")]
public sealed class HttpClientMemorySendReceiveLocalTest_Http2 : HttpClientMemorySendReceiveLocalTest
{
public SendReceiveTest_Http2(ITestOutputHelper output) : base(output) { }

[Fact]
[SkipOnPlatform(TestPlatforms.Browser, "System.Net.Sockets is not supported on this platform")]
public async Task ReceiveNoThrowAfterSend_NoSsl()
{
var serverMessage = new byte[] { 4, 5, 6 };
await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
{
using (var cws = new ClientWebSocket())
using (var cts = new CancellationTokenSource(TimeOutMilliseconds))
{
cws.Options.HttpVersion = HttpVersion.Version20;
cws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionExact;

await cws.ConnectAsync(uri, GetInvoker(), cts.Token);

await cws.SendAsync(new byte[] { 2, 3, 4 }, WebSocketMessageType.Binary, true, cts.Token);

var readBuffer = new byte[serverMessage.Length];
await cws.ReceiveAsync(readBuffer, cts.Token);
Assert.Equal(serverMessage, readBuffer);
}
},
async server =>
{
Http2LoopbackConnection connection = await server.EstablishConnectionAsync(new SettingsEntry { SettingId = SettingId.EnableConnect, Value = 1 });
(int streamId, HttpRequestData requestData) = await connection.ReadAndParseRequestHeaderAsync(readBody: false);
// send status 200 OK to establish websocket
await connection.SendResponseHeadersAsync(streamId, endStream: false).ConfigureAwait(false);

// send reply
byte binaryMessageType = 2;
var prefix = new byte[] { binaryMessageType, (byte)serverMessage.Length };
byte[] constructMessage = prefix.Concat(serverMessage).ToArray();
await connection.SendResponseDataAsync(streamId, constructMessage, endStream: false);

}, new Http2Options() { WebSocketEndpoint = true, UseSsl = false });
}
public HttpClientMemorySendReceiveLocalTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

[Fact]
[SkipOnPlatform(TestPlatforms.Browser, "Self-signed certificates are not supported on browser")]
public async Task ReceiveNoThrowAfterSend_WithSsl()
{
var serverMessage = new byte[] { 4, 5, 6 };
await Http2LoopbackServer.CreateClientAndServerAsync(async uri =>
{
using (var cws = new ClientWebSocket())
using (var cts = new CancellationTokenSource(TimeOutMilliseconds))
{
cws.Options.HttpVersion = HttpVersion.Version20;
cws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionExact;
[SkipOnPlatform(TestPlatforms.Browser, "Self-signed certificates are not supported on browser")]
public sealed class HttpClientMemorySendReceiveLocalSslTest_Http2 : HttpClientMemorySendReceiveLocalSslTest
{
public HttpClientMemorySendReceiveLocalSslTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

await cws.ConnectAsync(uri, GetInvoker(), cts.Token);
// Array segment

await cws.SendAsync(new byte[] { 2, 3, 4 }, WebSocketMessageType.Binary, true, cts.Token);
//Invoker
[SkipOnPlatform(TestPlatforms.Browser, "HTTP/2 WebSockets aren't supported on Browser")]
public sealed class InvokerArraySegmentSendReceiveLocalTest_Http2 : InvokerArraySegmentSendReceiveLocalTest
{
public InvokerArraySegmentSendReceiveLocalTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

var readBuffer = new byte[serverMessage.Length];
await cws.ReceiveAsync(readBuffer, cts.Token);
Assert.Equal(serverMessage, readBuffer);
}
},
async server =>
{
Http2LoopbackConnection connection = await server.EstablishConnectionAsync(new SettingsEntry { SettingId = SettingId.EnableConnect, Value = 1 });
(int streamId, HttpRequestData requestData) = await connection.ReadAndParseRequestHeaderAsync(readBody: false);
// send status 200 OK to establish websocket
await connection.SendResponseHeadersAsync(streamId, endStream: false).ConfigureAwait(false);
[SkipOnPlatform(TestPlatforms.Browser, "Self-signed certificates are not supported on browser")]
public sealed class InvokerArraySegmentSendReceiveLocalSslTest_Http2 : InvokerArraySegmentSendReceiveLocalSslTest
{
public InvokerArraySegmentSendReceiveLocalSslTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

// send reply
byte binaryMessageType = 2;
var prefix = new byte[] { binaryMessageType, (byte)serverMessage.Length };
byte[] constructMessage = prefix.Concat(serverMessage).ToArray();
await connection.SendResponseDataAsync(streamId, constructMessage, endStream: false);
//HttpClient
[SkipOnPlatform(TestPlatforms.Browser, "HTTP/2 WebSockets aren't supported on Browser")]
public sealed class HttpClientArraySegmentSendReceiveLocalTest_Http2 : HttpClientArraySegmentSendReceiveLocalTest
{
public HttpClientArraySegmentSendReceiveLocalTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}

}, new Http2Options() { WebSocketEndpoint = true });
}
[SkipOnPlatform(TestPlatforms.Browser, "Self-signed certificates are not supported on browser")]
public sealed class HttpClientArraySegmentSendReceiveLocalSslTest_Http2 : HttpClientArraySegmentSendReceiveLocalSslTest
{
public HttpClientArraySegmentSendReceiveLocalSslTest_Http2(ITestOutputHelper output) : base(output) { }
protected override Version UseVersion => HttpVersion.Version20;
}
}
Loading