React to CoreFxLab packages (#998)
This commit is contained in:
parent
f21d107766
commit
792745ad98
|
|
@ -11,6 +11,11 @@
|
||||||
<SignAssembly>true</SignAssembly>
|
<SignAssembly>true</SignAssembly>
|
||||||
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
<PublicSign Condition="'$(OS)' != 'Windows_NT'">true</PublicSign>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- This is an experimental version of the compiler. See https://github.com/dotnet/csharplang/issues/666 for more details. -->
|
||||||
|
<PackageReference Include="Microsoft.NETCore.Compilers" Version="$(MicrosoftNETCoreCompilersPackageVersion)" PrivateAssets="All" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks
|
||||||
public class MessageParserBenchmark
|
public class MessageParserBenchmark
|
||||||
{
|
{
|
||||||
private static readonly Random Random = new Random();
|
private static readonly Random Random = new Random();
|
||||||
private ReadOnlyBuffer<byte> _binaryInput;
|
private ReadOnlyMemory<byte> _binaryInput;
|
||||||
private ReadOnlyBuffer<byte> _textInput;
|
private ReadOnlyMemory<byte> _textInput;
|
||||||
|
|
||||||
[Params(32, 64)]
|
[Params(32, 64)]
|
||||||
public int ChunkSize { get; set; }
|
public int ChunkSize { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -50,21 +50,23 @@
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview1-27475</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.1.0-preview1-27475</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.0-preview1-27475</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
<MicrosoftExtensionsSecurityHelperSourcesPackageVersion>2.1.0-preview1-27475</MicrosoftExtensionsSecurityHelperSourcesPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.0</MicrosoftNETCoreApp20PackageVersion>
|
||||||
|
<MicrosoftNETCoreCompilersPackageVersion>2.6.0-beta2-62211-02</MicrosoftNETCoreCompilersPackageVersion>
|
||||||
<MicrosoftNETTestSdkPackageVersion>15.3.0</MicrosoftNETTestSdkPackageVersion>
|
<MicrosoftNETTestSdkPackageVersion>15.3.0</MicrosoftNETTestSdkPackageVersion>
|
||||||
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
<MoqPackageVersion>4.7.49</MoqPackageVersion>
|
||||||
<MsgPackCliPackageVersion>0.9.0-beta2</MsgPackCliPackageVersion>
|
<MsgPackCliPackageVersion>0.9.0-beta2</MsgPackCliPackageVersion>
|
||||||
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
<NewtonsoftJsonPackageVersion>10.0.1</NewtonsoftJsonPackageVersion>
|
||||||
<StackExchangeRedisStrongNamePackageVersion>1.2.4</StackExchangeRedisStrongNamePackageVersion>
|
<StackExchangeRedisStrongNamePackageVersion>1.2.4</StackExchangeRedisStrongNamePackageVersion>
|
||||||
<SystemBinaryPackageVersion>0.1.0-e170811-6</SystemBinaryPackageVersion>
|
<SystemBinaryPackageVersion>0.1.0-alpha-002</SystemBinaryPackageVersion>
|
||||||
<SystemBuffersPrimitivesPackageVersion>0.1.0-e170811-6</SystemBuffersPrimitivesPackageVersion>
|
<SystemBuffersPrimitivesPackageVersion>0.1.0-alpha-002</SystemBuffersPrimitivesPackageVersion>
|
||||||
<SystemIOPipelinesExtensionsPackageVersion>0.1.0-e170811-6</SystemIOPipelinesExtensionsPackageVersion>
|
<SystemIOPipelinesExtensionsPackageVersion>0.1.0-alpha-002</SystemIOPipelinesExtensionsPackageVersion>
|
||||||
<SystemIOPipelinesPackageVersion>0.1.0-e170811-6</SystemIOPipelinesPackageVersion>
|
<SystemIOPipelinesPackageVersion>0.1.0-alpha-002</SystemIOPipelinesPackageVersion>
|
||||||
<SystemMemoryPackageVersion>4.4.0-preview3-25519-03</SystemMemoryPackageVersion>
|
<SystemMemoryPackageVersion>4.5.0-preview1-25902-08</SystemMemoryPackageVersion>
|
||||||
<SystemNumericsVectorsPackageVersion>4.4.0</SystemNumericsVectorsPackageVersion>
|
<SystemNumericsVectorsPackageVersion>4.5.0-preview1-25902-08</SystemNumericsVectorsPackageVersion>
|
||||||
<SystemReactiveLinqPackageVersion>3.1.1</SystemReactiveLinqPackageVersion>
|
<SystemReactiveLinqPackageVersion>3.1.1</SystemReactiveLinqPackageVersion>
|
||||||
<SystemReflectionEmitPackageVersion>4.3.0</SystemReflectionEmitPackageVersion>
|
<SystemReflectionEmitPackageVersion>4.3.0</SystemReflectionEmitPackageVersion>
|
||||||
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.4.0</SystemRuntimeCompilerServicesUnsafePackageVersion>
|
<SystemRuntimeCompilerServicesUnsafePackageVersion>4.5.0-preview1-25902-08</SystemRuntimeCompilerServicesUnsafePackageVersion>
|
||||||
<SystemThreadingTasksChannelsPackageVersion>0.1.0-e170811-6</SystemThreadingTasksChannelsPackageVersion>
|
<SystemThreadingChannelsPackageVersion>4.5.0-preview1-25902-08</SystemThreadingChannelsPackageVersion>
|
||||||
|
<SystemThreadingTasksExtensionsPackageVersion>4.4.0</SystemThreadingTasksExtensionsPackageVersion>
|
||||||
<XunitPackageVersion>2.3.0</XunitPackageVersion>
|
<XunitPackageVersion>2.3.0</XunitPackageVersion>
|
||||||
<XunitRunnerVisualStudioPackageVersion>2.3.0</XunitRunnerVisualStudioPackageVersion>
|
<XunitRunnerVisualStudioPackageVersion>2.3.0</XunitRunnerVisualStudioPackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Test.Server
|
namespace Microsoft.AspNetCore.SignalR.Test.Server
|
||||||
|
|
@ -11,7 +12,7 @@ namespace Microsoft.AspNetCore.SignalR.Test.Server
|
||||||
{
|
{
|
||||||
public async override Task OnConnectedAsync(ConnectionContext connection)
|
public async override Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
await connection.Transport.Out.WriteAsync(await connection.Transport.In.ReadAsync());
|
await connection.Transport.Writer.WriteAsync(await connection.Transport.Reader.ReadAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace SocialWeather
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
await formatter.WriteAsync(data, ms);
|
await formatter.WriteAsync(data, ms);
|
||||||
|
|
||||||
connection.Transport.Out.TryWrite(ms.ToArray());
|
connection.Transport.Writer.TryWrite(ms.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
@ -34,9 +34,9 @@ namespace SocialWeather
|
||||||
var formatter = _formatterResolver.GetFormatter<WeatherReport>(
|
var formatter = _formatterResolver.GetFormatter<WeatherReport>(
|
||||||
(string)connection.Metadata["formatType"]);
|
(string)connection.Metadata["formatType"]);
|
||||||
|
|
||||||
while (await connection.Transport.In.WaitToReadAsync())
|
while (await connection.Transport.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
if (connection.Transport.In.TryRead(out var buffer))
|
if (connection.Transport.Reader.TryRead(out var buffer))
|
||||||
{
|
{
|
||||||
var stream = new MemoryStream();
|
var stream = new MemoryStream();
|
||||||
await stream.WriteAsync(buffer, 0, buffer.Length);
|
await stream.WriteAsync(buffer, 0, buffer.Length);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -20,9 +20,9 @@ namespace SocketsSample.EndPoints
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (await connection.Transport.In.WaitToReadAsync())
|
while (await connection.Transport.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
if (connection.Transport.In.TryRead(out var buffer))
|
if (connection.Transport.Reader.TryRead(out var buffer))
|
||||||
{
|
{
|
||||||
// We can avoid the copy here but we'll deal with that later
|
// We can avoid the copy here but we'll deal with that later
|
||||||
var text = Encoding.UTF8.GetString(buffer);
|
var text = Encoding.UTF8.GetString(buffer);
|
||||||
|
|
@ -50,7 +50,7 @@ namespace SocketsSample.EndPoints
|
||||||
|
|
||||||
foreach (var c in Connections)
|
foreach (var c in Connections)
|
||||||
{
|
{
|
||||||
tasks.Add(c.Transport.Out.WriteAsync(payload));
|
tasks.Add(c.Transport.Writer.WriteAsync(payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.WhenAll(tasks);
|
return Task.WhenAll(tasks);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
namespace SocketsSample.Hubs
|
namespace SocketsSample.Hubs
|
||||||
|
|
@ -15,7 +15,7 @@ namespace SocketsSample.Hubs
|
||||||
.Take(count);
|
.Take(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadableChannel<int> ChannelCounter(int count, int delay)
|
public ChannelReader<int> ChannelCounter(int count, int delay)
|
||||||
{
|
{
|
||||||
var channel = Channel.CreateUnbounded<int>();
|
var channel = Channel.CreateUnbounded<int>();
|
||||||
|
|
||||||
|
|
@ -23,14 +23,14 @@ namespace SocketsSample.Hubs
|
||||||
{
|
{
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
await channel.Out.WriteAsync(i);
|
await channel.Writer.WriteAsync(i);
|
||||||
await Task.Delay(delay);
|
await Task.Delay(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
});
|
});
|
||||||
|
|
||||||
return channel.In;
|
return channel.Reader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Client.Internal;
|
using Microsoft.AspNetCore.SignalR.Client.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal;
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
|
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
|
||||||
|
|
@ -145,12 +145,12 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
return new Subscription(invocationHandler, invocationList);
|
return new Subscription(invocationHandler, invocationList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ReadableChannel<object>> StreamAsync(string methodName, Type returnType, object[] args, CancellationToken cancellationToken = default)
|
public async Task<ChannelReader<object>> StreamAsync(string methodName, Type returnType, object[] args, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return await StreamAsyncCore(methodName, returnType, args, cancellationToken).ForceAsync();
|
return await StreamAsyncCore(methodName, returnType, args, cancellationToken).ForceAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ReadableChannel<object>> StreamAsyncCore(string methodName, Type returnType, object[] args, CancellationToken cancellationToken)
|
private async Task<ChannelReader<object>> StreamAsyncCore(string methodName, Type returnType, object[] args, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!_startCalled)
|
if (!_startCalled)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,71 +1,71 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Client
|
namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
{
|
{
|
||||||
public static partial class HubConnectionExtensions
|
public static partial class HubConnectionExtensions
|
||||||
{
|
{
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, Array.Empty<object>(), cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, Array.Empty<object>(), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, CancellationToken cancellationToken = default)
|
public static Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6, object arg7, object arg8, object arg9, object arg10, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 }, cancellationToken);
|
return hubConnection.StreamAsync<TResult>(methodName, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 }, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ReadableChannel<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object[] args, CancellationToken cancellationToken = default)
|
public static async Task<ChannelReader<TResult>> StreamAsync<TResult>(this HubConnection hubConnection, string methodName, object[] args, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (hubConnection == null)
|
if (hubConnection == null)
|
||||||
{
|
{
|
||||||
|
|
@ -85,9 +85,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
{
|
{
|
||||||
while (inputChannel.TryRead(out var item))
|
while (inputChannel.TryRead(out var item))
|
||||||
{
|
{
|
||||||
while (!outputChannel.Out.TryWrite((TResult)item))
|
while (!outputChannel.Writer.TryWrite((TResult)item))
|
||||||
{
|
{
|
||||||
if (!await outputChannel.Out.WaitToWriteAsync())
|
if (!await outputChannel.Writer.WaitToWriteAsync())
|
||||||
{
|
{
|
||||||
// Failed to write to the output channel because it was closed. Nothing really we can do but abort here.
|
// Failed to write to the output channel because it was closed. Nothing really we can do but abort here.
|
||||||
return;
|
return;
|
||||||
|
|
@ -101,18 +101,18 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
outputChannel.Out.TryComplete(ex);
|
outputChannel.Writer.TryComplete(ex);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// This will safely no-op if the catch block above ran.
|
// This will safely no-op if the catch block above ran.
|
||||||
outputChannel.Out.TryComplete();
|
outputChannel.Writer.TryComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = RunChannel();
|
_ = RunChannel();
|
||||||
|
|
||||||
return outputChannel.In;
|
return outputChannel.Reader;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Client
|
namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Client.Internal;
|
using Microsoft.AspNetCore.SignalR.Client.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InvocationRequest Stream(CancellationToken cancellationToken, Type resultType, string invocationId,
|
public static InvocationRequest Stream(CancellationToken cancellationToken, Type resultType, string invocationId,
|
||||||
ILoggerFactory loggerFactory, HubConnection hubConnection, out ReadableChannel<object> result)
|
ILoggerFactory loggerFactory, HubConnection hubConnection, out ChannelReader<object> result)
|
||||||
{
|
{
|
||||||
var req = new Streaming(cancellationToken, resultType, invocationId, loggerFactory, hubConnection);
|
var req = new Streaming(cancellationToken, resultType, invocationId, loggerFactory, hubConnection);
|
||||||
result = req.Result;
|
result = req.Result;
|
||||||
|
|
@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadableChannel<object> Result => _channel.In;
|
public ChannelReader<object> Result => _channel.Reader;
|
||||||
|
|
||||||
public override void Complete(CompletionMessage completionMessage)
|
public override void Complete(CompletionMessage completionMessage)
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
if (completionMessage.Result != null)
|
if (completionMessage.Result != null)
|
||||||
{
|
{
|
||||||
Logger.ReceivedUnexpectedComplete(InvocationId);
|
Logger.ReceivedUnexpectedComplete(InvocationId);
|
||||||
_channel.Out.TryComplete(new InvalidOperationException("Server provided a result in a completion response to a streamed invocation."));
|
_channel.Writer.TryComplete(new InvalidOperationException("Server provided a result in a completion response to a streamed invocation."));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(completionMessage.Error))
|
if (!string.IsNullOrEmpty(completionMessage.Error))
|
||||||
|
|
@ -92,22 +92,22 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_channel.Out.TryComplete();
|
_channel.Writer.TryComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Fail(Exception exception)
|
public override void Fail(Exception exception)
|
||||||
{
|
{
|
||||||
Logger.InvocationFailed(InvocationId);
|
Logger.InvocationFailed(InvocationId);
|
||||||
_channel.Out.TryComplete(exception);
|
_channel.Writer.TryComplete(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async ValueTask<bool> StreamItem(object item)
|
public override async ValueTask<bool> StreamItem(object item)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!_channel.Out.TryWrite(item))
|
while (!_channel.Writer.TryWrite(item))
|
||||||
{
|
{
|
||||||
if (!await _channel.Out.WaitToWriteAsync())
|
if (!await _channel.Writer.WaitToWriteAsync())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
|
||||||
|
|
||||||
protected override void Cancel()
|
protected override void Cancel()
|
||||||
{
|
{
|
||||||
_channel.Out.TryComplete(new OperationCanceledException("Invocation terminated"));
|
_channel.Writer.TryComplete(new OperationCanceledException("Invocation terminated"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
{
|
{
|
||||||
public byte[] Decode(byte[] payload)
|
public byte[] Decode(byte[] payload)
|
||||||
{
|
{
|
||||||
var buffer = new ReadOnlyBuffer<byte>(payload);
|
var buffer = new ReadOnlyMemory<byte>(payload);
|
||||||
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message);
|
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message);
|
||||||
|
|
||||||
return Convert.FromBase64String(Encoding.UTF8.GetString(message.ToArray()));
|
return Convert.FromBase64String(Encoding.UTF8.GetString(message.ToArray()));
|
||||||
|
|
|
||||||
|
|
@ -14,20 +14,18 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Encoders
|
||||||
/// Attempts to parse a message from the buffer. Returns 'false' if there is not enough data to complete a message. Throws an
|
/// Attempts to parse a message from the buffer. Returns 'false' if there is not enough data to complete a message. Throws an
|
||||||
/// exception if there is a format error in the provided data.
|
/// exception if there is a format error in the provided data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool TryParseMessage(ref ReadOnlyBuffer<byte> buffer, out ReadOnlyBuffer<byte> payload)
|
public static bool TryParseMessage(ref ReadOnlyMemory<byte> buffer, out ReadOnlyMemory<byte> payload)
|
||||||
{
|
{
|
||||||
payload = default;
|
payload = default(ReadOnlyMemory<byte>);
|
||||||
var span = buffer.Span;
|
|
||||||
|
|
||||||
if (!TryReadLength(span, out var index, out var length))
|
if (!TryReadLength(buffer.Span, out var index, out var length))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var remaining = buffer.Slice(index);
|
var remaining = buffer.Slice(index);
|
||||||
span = remaining.Span;
|
|
||||||
|
|
||||||
if (!TryReadDelimiter(span, LengthPrefixedTextMessageWriter.FieldDelimiter, "length"))
|
if (!TryReadDelimiter(remaining.Span, LengthPrefixedTextMessageWriter.FieldDelimiter, "length"))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
var buffer = ArrayPool<byte>.Shared.Rent(lenNumBytes + payload.Length);
|
var buffer = ArrayPool<byte>.Shared.Rent(lenNumBytes + payload.Length);
|
||||||
var bufferSpan = buffer.AsSpan();
|
var bufferSpan = buffer.AsSpan();
|
||||||
|
|
||||||
new Span<byte>(lenBuffer, lenNumBytes).CopyTo(bufferSpan);
|
new ReadOnlySpan<byte>(lenBuffer, lenNumBytes).CopyTo(bufferSpan);
|
||||||
bufferSpan = bufferSpan.Slice(lenNumBytes);
|
bufferSpan = bufferSpan.Slice(lenNumBytes);
|
||||||
payload.CopyTo(bufferSpan);
|
payload.CopyTo(bufferSpan);
|
||||||
output.Write(buffer, 0, lenNumBytes + payload.Length);
|
output.Write(buffer, 0, lenNumBytes + payload.Length);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
private static int[] _numBitsToShift = new[] { 0, 7, 14, 21, 28 };
|
private static int[] _numBitsToShift = new[] { 0, 7, 14, 21, 28 };
|
||||||
private const int MaxLengthPrefixSize = 5;
|
private const int MaxLengthPrefixSize = 5;
|
||||||
|
|
||||||
public static bool TryParseMessage(ref ReadOnlyBuffer<byte> buffer, out ReadOnlyBuffer<byte> payload)
|
public static bool TryParseMessage(ref ReadOnlyMemory<byte> buffer, out ReadOnlyMemory<byte> payload)
|
||||||
{
|
{
|
||||||
payload = default;
|
payload = default;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Formatters
|
||||||
{
|
{
|
||||||
public static class TextMessageParser
|
public static class TextMessageParser
|
||||||
{
|
{
|
||||||
public static bool TryParseMessage(ref ReadOnlyBuffer<byte> buffer, out ReadOnlyBuffer<byte> payload)
|
public static bool TryParseMessage(ref ReadOnlyMemory<byte> buffer, out ReadOnlyMemory<byte> payload)
|
||||||
{
|
{
|
||||||
payload = default;
|
payload = default;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||||
|
|
||||||
ProtocolType Type { get; }
|
ProtocolType Type { get; }
|
||||||
|
|
||||||
bool TryParseMessages(ReadOnlyBuffer<byte> input, IInvocationBinder binder, out IList<HubMessage> messages);
|
bool TryParseMessages(ReadOnlyMemory<byte> input, IInvocationBinder binder, out IList<HubMessage> messages);
|
||||||
|
|
||||||
void WriteMessage(HubMessage message, Stream output);
|
void WriteMessage(HubMessage message, Stream output);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||||
|
|
||||||
public ProtocolType Type => ProtocolType.Text;
|
public ProtocolType Type => ProtocolType.Text;
|
||||||
|
|
||||||
public bool TryParseMessages(ReadOnlyBuffer<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
|
public bool TryParseMessages(ReadOnlyMemory<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
|
||||||
{
|
{
|
||||||
messages = new List<HubMessage>();
|
messages = new List<HubMessage>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||||
_serializationContext = serializationContext;
|
_serializationContext = serializationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryParseMessages(ReadOnlyBuffer<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
|
public bool TryParseMessages(ReadOnlyMemory<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
|
||||||
{
|
{
|
||||||
messages = new List<HubMessage>();
|
messages = new List<HubMessage>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParseMessage(ReadOnlyBuffer<byte> input, out NegotiationMessage negotiationMessage)
|
public static bool TryParseMessage(ReadOnlyMemory<byte> input, out NegotiationMessage negotiationMessage)
|
||||||
{
|
{
|
||||||
if (!TextMessageParser.TryParseMessage(ref input, out var payload))
|
if (!TextMessageParser.TryParseMessage(ref input, out var payload))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||||
<PackageReference Include="MsgPack.Cli" Version="$(MsgPackCliPackageVersion)" />
|
<PackageReference Include="MsgPack.Cli" Version="$(MsgPackCliPackageVersion)" />
|
||||||
<PackageReference Include="System.Binary" Version="$(SystemBinaryPackageVersion)" />
|
|
||||||
<PackageReference Include="System.Buffers.Primitives" Version="$(SystemBuffersPrimitivesPackageVersion)" />
|
<PackageReference Include="System.Buffers.Primitives" Version="$(SystemBuffersPrimitivesPackageVersion)" />
|
||||||
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
||||||
<PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
<PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Runtime.ExceptionServices;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.SignalR.Features;
|
using Microsoft.AspNetCore.SignalR.Features;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal;
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
|
|
@ -22,12 +22,12 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
{
|
{
|
||||||
private static Action<object> _abortedCallback = AbortConnection;
|
private static Action<object> _abortedCallback = AbortConnection;
|
||||||
|
|
||||||
private readonly WritableChannel<HubMessage> _output;
|
private readonly ChannelWriter<HubMessage> _output;
|
||||||
private readonly ConnectionContext _connectionContext;
|
private readonly ConnectionContext _connectionContext;
|
||||||
private readonly CancellationTokenSource _connectionAbortedTokenSource = new CancellationTokenSource();
|
private readonly CancellationTokenSource _connectionAbortedTokenSource = new CancellationTokenSource();
|
||||||
private readonly TaskCompletionSource<object> _abortCompletedTcs = new TaskCompletionSource<object>();
|
private readonly TaskCompletionSource<object> _abortCompletedTcs = new TaskCompletionSource<object>();
|
||||||
|
|
||||||
public HubConnectionContext(WritableChannel<HubMessage> output, ConnectionContext connectionContext)
|
public HubConnectionContext(ChannelWriter<HubMessage> output, ConnectionContext connectionContext)
|
||||||
{
|
{
|
||||||
_output = output;
|
_output = output;
|
||||||
_connectionContext = connectionContext;
|
_connectionContext = connectionContext;
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
private IHubFeature HubFeature => Features.Get<IHubFeature>();
|
private IHubFeature HubFeature => Features.Get<IHubFeature>();
|
||||||
|
|
||||||
// Used by the HubEndPoint only
|
// Used by the HubEndPoint only
|
||||||
internal ReadableChannel<byte[]> Input => _connectionContext.Transport;
|
internal ChannelReader<byte[]> Input => _connectionContext.Transport;
|
||||||
|
|
||||||
internal ExceptionDispatchInfo AbortException { get; private set; }
|
internal ExceptionDispatchInfo AbortException { get; private set; }
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
|
|
||||||
public virtual HubProtocolReaderWriter ProtocolReaderWriter { get; set; }
|
public virtual HubProtocolReaderWriter ProtocolReaderWriter { get; set; }
|
||||||
|
|
||||||
public virtual WritableChannel<HubMessage> Output => _output;
|
public virtual ChannelWriter<HubMessage> Output => _output;
|
||||||
|
|
||||||
// Currently used only for streaming methods
|
// Currently used only for streaming methods
|
||||||
internal ConcurrentDictionary<string, CancellationTokenSource> ActiveRequestCancellationSources { get; } = new ConcurrentDictionary<string, CancellationTokenSource>();
|
internal ConcurrentDictionary<string, CancellationTokenSource> ActiveRequestCancellationSources { get; } = new ConcurrentDictionary<string, CancellationTokenSource>();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using System.Reflection;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.SignalR.Core;
|
using Microsoft.AspNetCore.SignalR.Core;
|
||||||
using Microsoft.AspNetCore.SignalR.Core.Internal;
|
using Microsoft.AspNetCore.SignalR.Core.Internal;
|
||||||
|
|
@ -84,14 +84,14 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (await output.In.WaitToReadAsync())
|
while (await output.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
while (output.In.TryRead(out var hubMessage))
|
while (output.Reader.TryRead(out var hubMessage))
|
||||||
{
|
{
|
||||||
var buffer = protocolReaderWriter.WriteMessage(hubMessage);
|
var buffer = protocolReaderWriter.WriteMessage(hubMessage);
|
||||||
while (await connection.Transport.Out.WaitToWriteAsync())
|
while (await connection.Transport.Writer.WaitToWriteAsync())
|
||||||
{
|
{
|
||||||
if (connection.Transport.Out.TryWrite(buffer))
|
if (connection.Transport.Writer.TryWrite(buffer))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +117,7 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
await _lifetimeManager.OnDisconnectedAsync(connectionContext);
|
await _lifetimeManager.OnDisconnectedAsync(connectionContext);
|
||||||
|
|
||||||
// Nothing should be writing to the HubConnectionContext
|
// Nothing should be writing to the HubConnectionContext
|
||||||
output.Out.TryComplete();
|
output.Writer.TryComplete();
|
||||||
|
|
||||||
// This should unwind once we complete the output
|
// This should unwind once we complete the output
|
||||||
await writingOutputTask;
|
await writingOutputTask;
|
||||||
|
|
@ -461,7 +461,7 @@ namespace Microsoft.AspNetCore.SignalR
|
||||||
|
|
||||||
private static bool IsChannel(Type type, out Type payloadType)
|
private static bool IsChannel(Type type, out Type payloadType)
|
||||||
{
|
{
|
||||||
var channelType = type.AllBaseTypes().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ReadableChannel<>));
|
var channelType = type.AllBaseTypes().FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ChannelReader<>));
|
||||||
if (channelType == null)
|
if (channelType == null)
|
||||||
{
|
{
|
||||||
payloadType = null;
|
payloadType = null;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Internal
|
namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -21,6 +21,10 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
.GetRuntimeMethods()
|
.GetRuntimeMethods()
|
||||||
.Single(m => m.Name.Equals(nameof(FromObservable)) && m.IsGenericMethod);
|
.Single(m => m.Name.Equals(nameof(FromObservable)) && m.IsGenericMethod);
|
||||||
|
|
||||||
|
private static readonly MethodInfo _getAsyncEnumeratorMethod = typeof(AsyncEnumeratorAdapters)
|
||||||
|
.GetRuntimeMethods()
|
||||||
|
.Single(m => m.Name.Equals(nameof(GetAsyncEnumerator)) && m.IsGenericMethod);
|
||||||
|
|
||||||
public static IAsyncEnumerator<object> FromObservable(object observable, Type observableInterface, CancellationToken cancellationToken)
|
public static IAsyncEnumerator<object> FromObservable(object observable, Type observableInterface, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// TODO: Cache expressions by observable.GetType()?
|
// TODO: Cache expressions by observable.GetType()?
|
||||||
|
|
@ -34,20 +38,19 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
// TODO: Allow bounding and optimizations?
|
// TODO: Allow bounding and optimizations?
|
||||||
var channel = Channel.CreateUnbounded<object>();
|
var channel = Channel.CreateUnbounded<object>();
|
||||||
|
|
||||||
var subscription = observable.Subscribe(new ChannelObserver<T>(channel.Out, cancellationToken));
|
var subscription = observable.Subscribe(new ChannelObserver<T>(channel.Writer, cancellationToken));
|
||||||
|
|
||||||
// Dispose the subscription when the token is cancelled
|
// Dispose the subscription when the token is cancelled
|
||||||
cancellationToken.Register(state => ((IDisposable)state).Dispose(), subscription);
|
cancellationToken.Register(state => ((IDisposable)state).Dispose(), subscription);
|
||||||
|
|
||||||
return channel.In.GetAsyncEnumerator(cancellationToken);
|
return GetAsyncEnumerator(channel.Reader, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IAsyncEnumerator<object> FromChannel(object readableChannelOfT, Type payloadType, CancellationToken cancellationToken)
|
public static IAsyncEnumerator<object> FromChannel(object readableChannelOfT, Type payloadType, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var enumerator = readableChannelOfT
|
var enumerator = _getAsyncEnumeratorMethod
|
||||||
.GetType()
|
.MakeGenericMethod(payloadType)
|
||||||
.GetRuntimeMethod("GetAsyncEnumerator", new[] { typeof(CancellationToken) })
|
.Invoke(null, new object[] { readableChannelOfT, cancellationToken });
|
||||||
.Invoke(readableChannelOfT, new object[] { cancellationToken });
|
|
||||||
|
|
||||||
if (payloadType.IsValueType)
|
if (payloadType.IsValueType)
|
||||||
{
|
{
|
||||||
|
|
@ -68,10 +71,10 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
|
|
||||||
private class ChannelObserver<T> : IObserver<T>
|
private class ChannelObserver<T> : IObserver<T>
|
||||||
{
|
{
|
||||||
private WritableChannel<object> _output;
|
private ChannelWriter<object> _output;
|
||||||
private CancellationToken _cancellationToken;
|
private CancellationToken _cancellationToken;
|
||||||
|
|
||||||
public ChannelObserver(WritableChannel<object> output, CancellationToken cancellationToken)
|
public ChannelObserver(ChannelWriter<object> output, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_output = output;
|
_output = output;
|
||||||
_cancellationToken = cancellationToken;
|
_cancellationToken = cancellationToken;
|
||||||
|
|
@ -125,5 +128,66 @@ namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
public object Current => _input.Current;
|
public object Current => _input.Current;
|
||||||
public Task<bool> MoveNextAsync() => _input.MoveNextAsync();
|
public Task<bool> MoveNextAsync() => _input.MoveNextAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IAsyncEnumerator<T> GetAsyncEnumerator<T>(ChannelReader<T> channel, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return new AsyncEnumerator<T>(channel, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Provides an async enumerator for the data in a channel.</summary>
|
||||||
|
internal class AsyncEnumerator<T> : IAsyncEnumerator<T>
|
||||||
|
{
|
||||||
|
/// <summary>The channel being enumerated.</summary>
|
||||||
|
private readonly ChannelReader<T> _channel;
|
||||||
|
/// <summary>Cancellation token used to cancel the enumeration.</summary>
|
||||||
|
private readonly CancellationToken _cancellationToken;
|
||||||
|
/// <summary>The current element of the enumeration.</summary>
|
||||||
|
private T _current;
|
||||||
|
|
||||||
|
internal AsyncEnumerator(ChannelReader<T> channel, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_channel = channel;
|
||||||
|
_cancellationToken = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => _current;
|
||||||
|
|
||||||
|
public Task<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
ValueTask<T> result = _channel.ReadAsync(_cancellationToken);
|
||||||
|
|
||||||
|
if (result.IsCompletedSuccessfully)
|
||||||
|
{
|
||||||
|
_current = result.Result;
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.AsTask().ContinueWith((t, s) =>
|
||||||
|
{
|
||||||
|
var thisRef = (AsyncEnumerator<T>)s;
|
||||||
|
if (t.IsFaulted && t.Exception.InnerException is ChannelClosedException cce && cce.InnerException == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
thisRef._current = t.GetAwaiter().GetResult();
|
||||||
|
return true;
|
||||||
|
}, this, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Represents an enumerator accessed asynchronously.</summary>
|
||||||
|
/// <typeparam name="T">Specifies the type of the data enumerated.</typeparam>
|
||||||
|
internal interface IAsyncEnumerator<out T>
|
||||||
|
{
|
||||||
|
/// <summary>Asynchronously move the enumerator to the next element.</summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A task that returns true if the enumerator was successfully advanced to the next item,
|
||||||
|
/// or false if no more data was available in the collection.
|
||||||
|
/// </returns>
|
||||||
|
Task<bool> MoveNextAsync();
|
||||||
|
|
||||||
|
/// <summary>Gets the current element being enumerated.</summary>
|
||||||
|
T Current { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Internal
|
namespace Microsoft.AspNetCore.Sockets.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -24,20 +24,19 @@ namespace Microsoft.AspNetCore.Sockets.Internal
|
||||||
public Channel<T> Input { get; }
|
public Channel<T> Input { get; }
|
||||||
public Channel<T> Output { get; }
|
public Channel<T> Output { get; }
|
||||||
|
|
||||||
public override ReadableChannel<T> In => Input;
|
|
||||||
|
|
||||||
public override WritableChannel<T> Out => Output;
|
|
||||||
|
|
||||||
public ChannelConnection(Channel<T> input, Channel<T> output)
|
public ChannelConnection(Channel<T> input, Channel<T> output)
|
||||||
{
|
{
|
||||||
|
Reader = input.Reader;
|
||||||
Input = input;
|
Input = input;
|
||||||
|
|
||||||
|
Writer = output.Writer;
|
||||||
Output = output;
|
Output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Input.Out.TryComplete();
|
Input.Writer.TryComplete();
|
||||||
Output.Out.TryComplete();
|
Output.Writer.TryComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,20 +45,19 @@ namespace Microsoft.AspNetCore.Sockets.Internal
|
||||||
public Channel<TIn> Input { get; }
|
public Channel<TIn> Input { get; }
|
||||||
public Channel<TOut> Output { get; }
|
public Channel<TOut> Output { get; }
|
||||||
|
|
||||||
public override ReadableChannel<TIn> In => Input;
|
|
||||||
|
|
||||||
public override WritableChannel<TOut> Out => Output;
|
|
||||||
|
|
||||||
public ChannelConnection(Channel<TIn> input, Channel<TOut> output)
|
public ChannelConnection(Channel<TIn> input, Channel<TOut> output)
|
||||||
{
|
{
|
||||||
|
Reader = input.Reader;
|
||||||
Input = input;
|
Input = input;
|
||||||
|
|
||||||
|
Writer = output.Writer;
|
||||||
Output = output;
|
Output = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Input.Out.TryComplete();
|
Input.Writer.TryComplete();
|
||||||
Output.Out.TryComplete();
|
Output.Writer.TryComplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// 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.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.SignalR.Internal
|
||||||
|
{
|
||||||
|
public static class ChannelReaderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>Asynchronously reads an item from the channel.</summary>
|
||||||
|
/// <param name="channel">The channel</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the read operation.</param>
|
||||||
|
/// <returns>A <see cref="ValueTask{TResult}"/> that represents the asynchronous read operation.</returns>
|
||||||
|
public static ValueTask<T> ReadAsync<T>(this ChannelReader<T> channel, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return
|
||||||
|
cancellationToken.IsCancellationRequested
|
||||||
|
? new ValueTask<T>(Task.FromCanceled<T>(cancellationToken))
|
||||||
|
: channel.TryRead(out T item)
|
||||||
|
? new ValueTask<T>(item)
|
||||||
|
: ReadAsyncCore(cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return new ValueTask<T>(Task.FromException<T>(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
async ValueTask<T> ReadAsyncCore(CancellationToken ct)
|
||||||
|
{
|
||||||
|
while (await channel.WaitToReadAsync(ct).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
if (channel.TryRead(out T item))
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ChannelClosedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets
|
namespace Microsoft.AspNetCore.Sockets
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Features
|
namespace Microsoft.AspNetCore.Sockets.Features
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="$(MicrosoftAspNetCoreHttpFeaturesPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="$(MicrosoftAspNetCoreHttpFeaturesPackageVersion)" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Channels" Version="$(SystemThreadingTasksChannelsPackageVersion)" />
|
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -8,7 +8,7 @@ using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Http;
|
using Microsoft.AspNetCore.Sockets.Client.Http;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
||||||
|
|
@ -39,8 +39,8 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
private readonly ITransportFactory _transportFactory;
|
private readonly ITransportFactory _transportFactory;
|
||||||
private string _connectionId;
|
private string _connectionId;
|
||||||
private readonly TimeSpan _eventQueueDrainTimeout = TimeSpan.FromSeconds(5);
|
private readonly TimeSpan _eventQueueDrainTimeout = TimeSpan.FromSeconds(5);
|
||||||
private ReadableChannel<byte[]> Input => _transportChannel.In;
|
private ChannelReader<byte[]> Input => _transportChannel.Input;
|
||||||
private WritableChannel<SendMessage> Output => _transportChannel.Out;
|
private ChannelWriter<SendMessage> Output => _transportChannel.Output;
|
||||||
private readonly List<ReceiveCallback> _callbacks = new List<ReceiveCallback>();
|
private readonly List<ReceiveCallback> _callbacks = new List<ReceiveCallback>();
|
||||||
private readonly TransportType _requestedTransportType = TransportType.All;
|
private readonly TransportType _requestedTransportType = TransportType.All;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Client
|
namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -6,7 +6,7 @@ using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Http;
|
using Microsoft.AspNetCore.Sockets.Client.Http;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
Running = Task.WhenAll(_sender, _poller).ContinueWith(t =>
|
Running = Task.WhenAll(_sender, _poller).ContinueWith(t =>
|
||||||
{
|
{
|
||||||
_logger.TransportStopped(_connectionId, t.Exception?.InnerException);
|
_logger.TransportStopped(_connectionId, t.Exception?.InnerException);
|
||||||
_application.Out.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
|
_application.Writer.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
|
||||||
return t;
|
return t;
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
|
|
||||||
|
|
@ -123,9 +123,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
var payload = await response.Content.ReadAsByteArrayAsync();
|
var payload = await response.Content.ReadAsByteArrayAsync();
|
||||||
if (payload.Length > 0)
|
if (payload.Length > 0)
|
||||||
{
|
{
|
||||||
while (!_application.Out.TryWrite(payload))
|
while (!_application.Writer.TryWrite(payload))
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested || !await _application.Out.WaitToWriteAsync(cancellationToken))
|
if (cancellationToken.IsCancellationRequested || !await _application.Writer.WaitToWriteAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
||||||
<PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
<PackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
||||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Channels" Version="$(SystemThreadingTasksChannelsPackageVersion)" />
|
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -7,7 +7,7 @@ using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Http;
|
using Microsoft.AspNetCore.Sockets.Client.Http;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -23,11 +23,11 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
IList<SendMessage> messages = null;
|
IList<SendMessage> messages = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (await application.In.WaitToReadAsync(transportCts.Token))
|
while (await application.Reader.WaitToReadAsync(transportCts.Token))
|
||||||
{
|
{
|
||||||
// Grab as many messages as we can from the channel
|
// Grab as many messages as we can from the channel
|
||||||
messages = new List<SendMessage>();
|
messages = new List<SendMessage>();
|
||||||
while (!transportCts.IsCancellationRequested && application.In.TryRead(out SendMessage message))
|
while (!transportCts.IsCancellationRequested && application.Reader.TryRead(out SendMessage message))
|
||||||
{
|
{
|
||||||
messages.Add(message);
|
messages.Add(message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private Span<byte> ConvertBufferToSpan(ReadableBuffer buffer)
|
private ReadOnlySpan<byte> ConvertBufferToSpan(ReadableBuffer buffer)
|
||||||
{
|
{
|
||||||
if (buffer.IsSingleSpan)
|
if (buffer.IsSingleSpan)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -17,6 +18,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
public class ServerSentEventsTransport : ITransport
|
public class ServerSentEventsTransport : ITransport
|
||||||
{
|
{
|
||||||
|
private static readonly MemoryPool _memoryPool = new MemoryPool();
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly CancellationTokenSource _transportCts = new CancellationTokenSource();
|
private readonly CancellationTokenSource _transportCts = new CancellationTokenSource();
|
||||||
|
|
@ -64,7 +66,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
_logger.TransportStopped(_connectionId, t.Exception?.InnerException);
|
_logger.TransportStopped(_connectionId, t.Exception?.InnerException);
|
||||||
|
|
||||||
_application.Out.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
|
_application.Writer.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
|
||||||
return t;
|
return t;
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
|
|
||||||
|
|
@ -80,7 +82,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||||
|
|
||||||
var stream = await response.Content.ReadAsStreamAsync();
|
var stream = await response.Content.ReadAsStreamAsync();
|
||||||
var pipelineReader = stream.AsPipelineReader(cancellationToken);
|
var pipelineReader = StreamPipeConnection.CreateReader(new PipeOptions(_memoryPool), stream);
|
||||||
var readCancellationRegistration = cancellationToken.Register(
|
var readCancellationRegistration = cancellationToken.Register(
|
||||||
reader => ((IPipeReader)reader).CancelPendingRead(), pipelineReader);
|
reader => ((IPipeReader)reader).CancelPendingRead(), pipelineReader);
|
||||||
try
|
try
|
||||||
|
|
@ -105,7 +107,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
switch (parseResult)
|
switch (parseResult)
|
||||||
{
|
{
|
||||||
case ServerSentEventsMessageParser.ParseResult.Completed:
|
case ServerSentEventsMessageParser.ParseResult.Completed:
|
||||||
_application.Out.TryWrite(buffer);
|
_application.Writer.TryWrite(buffer);
|
||||||
_parser.Reset();
|
_parser.Reset();
|
||||||
break;
|
break;
|
||||||
case ServerSentEventsMessageParser.ParseResult.Incomplete:
|
case ServerSentEventsMessageParser.ParseResult.Incomplete:
|
||||||
|
|
@ -139,7 +141,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
_logger.TransportStopping(_connectionId);
|
_logger.TransportStopping(_connectionId);
|
||||||
_transportCts.Cancel();
|
_transportCts.Cancel();
|
||||||
_application.Out.TryComplete();
|
_application.Writer.TryComplete();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -7,7 +7,7 @@ using System.Diagnostics;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
using Microsoft.AspNetCore.Sockets.Client.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
|
@ -70,8 +70,8 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
_webSocket.Dispose();
|
_webSocket.Dispose();
|
||||||
_logger.TransportStopped(_connectionId, t.Exception?.InnerException);
|
_logger.TransportStopped(_connectionId, t.Exception?.InnerException);
|
||||||
_application.Out.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
|
_application.Writer.TryComplete(t.IsFaulted ? t.Exception.InnerException : null);
|
||||||
return t;
|
return t;
|
||||||
}).Unwrap();
|
}).Unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
{
|
{
|
||||||
_logger.WebSocketClosed(_connectionId, receiveResult.CloseStatus);
|
_logger.WebSocketClosed(_connectionId, receiveResult.CloseStatus);
|
||||||
|
|
||||||
_application.Out.Complete(
|
_application.Writer.Complete(
|
||||||
receiveResult.CloseStatus == WebSocketCloseStatus.NormalClosure
|
receiveResult.CloseStatus == WebSocketCloseStatus.NormalClosure
|
||||||
? null
|
? null
|
||||||
: new InvalidOperationException(
|
: new InvalidOperationException(
|
||||||
|
|
@ -135,9 +135,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
if (!_transportCts.Token.IsCancellationRequested)
|
if (!_transportCts.Token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_logger.MessageToApp(_connectionId, messageBuffer.Length);
|
_logger.MessageToApp(_connectionId, messageBuffer.Length);
|
||||||
while (await _application.Out.WaitToWriteAsync(_transportCts.Token))
|
while (await _application.Writer.WaitToWriteAsync(_transportCts.Token))
|
||||||
{
|
{
|
||||||
if (_application.Out.TryWrite(messageBuffer))
|
if (_application.Writer.TryWrite(messageBuffer))
|
||||||
{
|
{
|
||||||
incomingMessage.Clear();
|
incomingMessage.Clear();
|
||||||
break;
|
break;
|
||||||
|
|
@ -173,9 +173,9 @@ namespace Microsoft.AspNetCore.Sockets.Client
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (await _application.In.WaitToReadAsync(_transportCts.Token))
|
while (await _application.Reader.WaitToReadAsync(_transportCts.Token))
|
||||||
{
|
{
|
||||||
while (_application.In.TryRead(out SendMessage message))
|
while (_application.Reader.TryRead(out SendMessage message))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -93,7 +93,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
connection.TransportCapabilities = TransferMode.Text;
|
connection.TransportCapabilities = TransferMode.Text;
|
||||||
|
|
||||||
// We only need to provide the Input channel since writing to the application is handled through /send.
|
// We only need to provide the Input channel since writing to the application is handled through /send.
|
||||||
var sse = new ServerSentEventsTransport(connection.Application.In, connection.ConnectionId, _loggerFactory);
|
var sse = new ServerSentEventsTransport(connection.Application.Reader, connection.ConnectionId, _loggerFactory);
|
||||||
|
|
||||||
await DoPersistentConnection(socketDelegate, sse, context, connection);
|
await DoPersistentConnection(socketDelegate, sse, context, connection);
|
||||||
}
|
}
|
||||||
|
|
@ -194,7 +194,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
context.Response.RegisterForDispose(timeoutSource);
|
context.Response.RegisterForDispose(timeoutSource);
|
||||||
context.Response.RegisterForDispose(tokenSource);
|
context.Response.RegisterForDispose(tokenSource);
|
||||||
|
|
||||||
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.In, connection.ConnectionId, _loggerFactory);
|
var longPolling = new LongPollingTransport(timeoutSource.Token, connection.Application.Reader, connection.ConnectionId, _loggerFactory);
|
||||||
|
|
||||||
// Start the transport
|
// Start the transport
|
||||||
connection.TransportTask = longPolling.ProcessRequestAsync(context, tokenSource.Token);
|
connection.TransportTask = longPolling.ProcessRequestAsync(context, tokenSource.Token);
|
||||||
|
|
@ -215,7 +215,7 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
if (resultTask == connection.ApplicationTask)
|
if (resultTask == connection.ApplicationTask)
|
||||||
{
|
{
|
||||||
// Complete the transport (notifying it of the application error if there is one)
|
// Complete the transport (notifying it of the application error if there is one)
|
||||||
connection.Transport.Out.TryComplete(connection.ApplicationTask.Exception);
|
connection.Transport.Writer.TryComplete(connection.ApplicationTask.Exception);
|
||||||
|
|
||||||
// Wait for the transport to run
|
// Wait for the transport to run
|
||||||
await connection.TransportTask;
|
await connection.TransportTask;
|
||||||
|
|
@ -408,9 +408,9 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.ReceivedBytes(connection.ConnectionId, buffer.Length);
|
_logger.ReceivedBytes(connection.ConnectionId, buffer.Length);
|
||||||
while (!connection.Application.Out.TryWrite(buffer))
|
while (!connection.Application.Writer.TryWrite(buffer))
|
||||||
{
|
{
|
||||||
if (!await connection.Application.Out.WaitToWriteAsync())
|
if (!await connection.Application.Writer.WaitToWriteAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
@ -13,12 +13,12 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
|
||||||
{
|
{
|
||||||
public class LongPollingTransport : IHttpTransport
|
public class LongPollingTransport : IHttpTransport
|
||||||
{
|
{
|
||||||
private readonly ReadableChannel<byte[]> _application;
|
private readonly ChannelReader<byte[]> _application;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly CancellationToken _timeoutToken;
|
private readonly CancellationToken _timeoutToken;
|
||||||
private readonly string _connectionId;
|
private readonly string _connectionId;
|
||||||
|
|
||||||
public LongPollingTransport(CancellationToken timeoutToken, ReadableChannel<byte[]> application, string connectionId, ILoggerFactory loggerFactory)
|
public LongPollingTransport(CancellationToken timeoutToken, ChannelReader<byte[]> application, string connectionId, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_timeoutToken = timeoutToken;
|
_timeoutToken = timeoutToken;
|
||||||
_application = application;
|
_application = application;
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Formatters
|
||||||
|
|
||||||
if (nextSliceStart >= payload.Length)
|
if (nextSliceStart >= payload.Length)
|
||||||
{
|
{
|
||||||
payload = Span<byte>.Empty;
|
payload = ReadOnlySpan<byte>.Empty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
using Microsoft.AspNetCore.Sockets.Internal.Formatters;
|
||||||
|
|
@ -15,11 +15,11 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
|
||||||
{
|
{
|
||||||
public class ServerSentEventsTransport : IHttpTransport
|
public class ServerSentEventsTransport : IHttpTransport
|
||||||
{
|
{
|
||||||
private readonly ReadableChannel<byte[]> _application;
|
private readonly ChannelReader<byte[]> _application;
|
||||||
private readonly string _connectionId;
|
private readonly string _connectionId;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public ServerSentEventsTransport(ReadableChannel<byte[]> application, string connectionId, ILoggerFactory loggerFactory)
|
public ServerSentEventsTransport(ChannelReader<byte[]> application, string connectionId, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_application = application;
|
_application = application;
|
||||||
_connectionId = connectionId;
|
_connectionId = connectionId;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -7,7 +7,7 @@ using System.Diagnostics;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're done writing
|
// We're done writing
|
||||||
_application.Out.TryComplete();
|
_application.Writer.TryComplete();
|
||||||
|
|
||||||
await socket.CloseOutputAsync(failed ? WebSocketCloseStatus.InternalServerError : WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
await socket.CloseOutputAsync(failed ? WebSocketCloseStatus.InternalServerError : WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
||||||
|
|
||||||
|
|
@ -160,9 +160,9 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.MessageToApplication(_connection.ConnectionId, messageBuffer.Length);
|
_logger.MessageToApplication(_connection.ConnectionId, messageBuffer.Length);
|
||||||
while (await _application.Out.WaitToWriteAsync())
|
while (await _application.Writer.WaitToWriteAsync())
|
||||||
{
|
{
|
||||||
if (_application.Out.TryWrite(messageBuffer))
|
if (_application.Writer.TryWrite(messageBuffer))
|
||||||
{
|
{
|
||||||
incomingMessage.Clear();
|
incomingMessage.Clear();
|
||||||
break;
|
break;
|
||||||
|
|
@ -173,10 +173,10 @@ namespace Microsoft.AspNetCore.Sockets.Internal.Transports
|
||||||
|
|
||||||
private async Task StartSending(WebSocket ws)
|
private async Task StartSending(WebSocket ws)
|
||||||
{
|
{
|
||||||
while (await _application.In.WaitToReadAsync())
|
while (await _application.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
// Get a frame from the application
|
// Get a frame from the application
|
||||||
while (_application.In.TryRead(out var buffer))
|
while (_application.Reader.TryRead(out var buffer))
|
||||||
{
|
{
|
||||||
if (buffer.Length > 0)
|
if (buffer.Length > 0)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="$(MicrosoftAspNetCoreRoutingPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="$(MicrosoftAspNetCoreWebSocketsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="$(MicrosoftAspNetCoreWebSocketsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.SecurityHelper.Sources" PrivateAssets="All" Version="$(MicrosoftExtensionsSecurityHelperSourcesPackageVersion)" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Channels" Version="$(SystemThreadingTasksChannelsPackageVersion)" />
|
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using System.IO;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal;
|
using Microsoft.AspNetCore.Sockets.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Sockets.Features;
|
using Microsoft.AspNetCore.Sockets.Features;
|
||||||
|
|
||||||
|
|
@ -86,21 +86,21 @@ namespace Microsoft.AspNetCore.Sockets
|
||||||
// If the application task is faulted, propagate the error to the transport
|
// If the application task is faulted, propagate the error to the transport
|
||||||
if (ApplicationTask?.IsFaulted == true)
|
if (ApplicationTask?.IsFaulted == true)
|
||||||
{
|
{
|
||||||
Transport.Out.TryComplete(ApplicationTask.Exception.InnerException);
|
Transport.Writer.TryComplete(ApplicationTask.Exception.InnerException);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Transport.Out.TryComplete();
|
Transport.Writer.TryComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the transport task is faulted, propagate the error to the application
|
// If the transport task is faulted, propagate the error to the application
|
||||||
if (TransportTask?.IsFaulted == true)
|
if (TransportTask?.IsFaulted == true)
|
||||||
{
|
{
|
||||||
Application.Out.TryComplete(TransportTask.Exception.InnerException);
|
Application.Writer.TryComplete(TransportTask.Exception.InnerException);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Application.Out.TryComplete();
|
Application.Writer.TryComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
var applicationTask = ApplicationTask ?? Task.CompletedTask;
|
var applicationTask = ApplicationTask ?? Task.CompletedTask;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
||||||
<PackageReference Include="System.Threading.Tasks.Channels" Version="$(SystemThreadingTasksChannelsPackageVersion)" />
|
<PackageReference Include="System.Threading.Channels" Version="$(SystemThreadingChannelsPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace System.Threading.Tasks.Channels
|
namespace System.Threading.Channels
|
||||||
{
|
{
|
||||||
internal static class ChannelExtensions
|
internal static class ChannelExtensions
|
||||||
{
|
{
|
||||||
public static async Task<List<T>> ReadAllAsync<T>(this ReadableChannel<T> channel)
|
public static async Task<List<T>> ReadAllAsync<T>(this ChannelReader<T> channel)
|
||||||
{
|
{
|
||||||
var list = new List<T>();
|
var list = new List<T>();
|
||||||
while (await channel.WaitToReadAsync())
|
while (await channel.WaitToReadAsync())
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using System.IO;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal;
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
|
using Microsoft.AspNetCore.SignalR.Internal.Encoders;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
public TestClient(bool synchronousCallbacks = false, IHubProtocol protocol = null, IInvocationBinder invocationBinder = null, bool addClaimId = false)
|
public TestClient(bool synchronousCallbacks = false, IHubProtocol protocol = null, IInvocationBinder invocationBinder = null, bool addClaimId = false)
|
||||||
{
|
{
|
||||||
var options = new ChannelOptimizations { AllowSynchronousContinuations = synchronousCallbacks };
|
var options = new UnboundedChannelOptions { AllowSynchronousContinuations = synchronousCallbacks };
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>(options);
|
var transportToApplication = Channel.CreateUnbounded<byte[]>(options);
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>(options);
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>(options);
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
using (var memoryStream = new MemoryStream())
|
using (var memoryStream = new MemoryStream())
|
||||||
{
|
{
|
||||||
NegotiationProtocol.WriteMessage(new NegotiationMessage(protocol.Name), memoryStream);
|
NegotiationProtocol.WriteMessage(new NegotiationMessage(protocol.Name), memoryStream);
|
||||||
Application.Out.TryWrite(memoryStream.ToArray());
|
Application.Writer.TryWrite(memoryStream.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +149,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
public async Task<string> SendHubMessageAsync(HubMessage message)
|
public async Task<string> SendHubMessageAsync(HubMessage message)
|
||||||
{
|
{
|
||||||
var payload = _protocolReaderWriter.WriteMessage(message);
|
var payload = _protocolReaderWriter.WriteMessage(message);
|
||||||
await Application.Out.WriteAsync(payload);
|
await Application.Writer.WriteAsync(payload);
|
||||||
return message.InvocationId;
|
return message.InvocationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
if (message == null)
|
if (message == null)
|
||||||
{
|
{
|
||||||
if (!await Application.In.WaitToReadAsync())
|
if (!await Application.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +175,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
public HubMessage TryRead()
|
public HubMessage TryRead()
|
||||||
{
|
{
|
||||||
if (Application.In.TryRead(out var buffer) &&
|
if (Application.Reader.TryRead(out var buffer) &&
|
||||||
_protocolReaderWriter.ReadMessages(buffer, _invocationBinder, out var messages))
|
_protocolReaderWriter.ReadMessages(buffer, _invocationBinder, out var messages))
|
||||||
{
|
{
|
||||||
return messages[0];
|
return messages[0];
|
||||||
|
|
@ -208,4 +208,4 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
||||||
{
|
{
|
||||||
|
|
@ -17,9 +17,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
||||||
|
|
||||||
public IObservable<int> Stream(int count) => TestHubMethodsImpl.Stream(count);
|
public IObservable<int> Stream(int count) => TestHubMethodsImpl.Stream(count);
|
||||||
|
|
||||||
public ReadableChannel<int> StreamException() => TestHubMethodsImpl.StreamException();
|
public ChannelReader<int> StreamException() => TestHubMethodsImpl.StreamException();
|
||||||
|
|
||||||
public ReadableChannel<string> StreamBroken() => TestHubMethodsImpl.StreamBroken();
|
public ChannelReader<string> StreamBroken() => TestHubMethodsImpl.StreamBroken();
|
||||||
|
|
||||||
public async Task CallEcho(string message)
|
public async Task CallEcho(string message)
|
||||||
{
|
{
|
||||||
|
|
@ -40,9 +40,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
||||||
|
|
||||||
public IObservable<int> Stream(int count) => TestHubMethodsImpl.Stream(count);
|
public IObservable<int> Stream(int count) => TestHubMethodsImpl.Stream(count);
|
||||||
|
|
||||||
public ReadableChannel<int> StreamException() => TestHubMethodsImpl.StreamException();
|
public ChannelReader<int> StreamException() => TestHubMethodsImpl.StreamException();
|
||||||
|
|
||||||
public ReadableChannel<string> StreamBroken() => TestHubMethodsImpl.StreamBroken();
|
public ChannelReader<string> StreamBroken() => TestHubMethodsImpl.StreamBroken();
|
||||||
|
|
||||||
public async Task CallEcho(string message)
|
public async Task CallEcho(string message)
|
||||||
{
|
{
|
||||||
|
|
@ -63,9 +63,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
||||||
|
|
||||||
public IObservable<int> Stream(int count) => TestHubMethodsImpl.Stream(count);
|
public IObservable<int> Stream(int count) => TestHubMethodsImpl.Stream(count);
|
||||||
|
|
||||||
public ReadableChannel<int> StreamException() => TestHubMethodsImpl.StreamException();
|
public ChannelReader<int> StreamException() => TestHubMethodsImpl.StreamException();
|
||||||
|
|
||||||
public ReadableChannel<string> StreamBroken() => TestHubMethodsImpl.StreamBroken();
|
public ChannelReader<string> StreamBroken() => TestHubMethodsImpl.StreamBroken();
|
||||||
|
|
||||||
public async Task CallEcho(string message)
|
public async Task CallEcho(string message)
|
||||||
{
|
{
|
||||||
|
|
@ -97,12 +97,12 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
|
||||||
.Take(count);
|
.Take(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadableChannel<int> StreamException()
|
public static ChannelReader<int> StreamException()
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Error occurred while streaming.");
|
throw new InvalidOperationException("Error occurred while streaming.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadableChannel<string> StreamBroken() => null;
|
public static ChannelReader<string> StreamBroken() => null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ITestHub
|
public interface ITestHub
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Client.Tests;
|
using Microsoft.AspNetCore.Client.Tests;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets.Features;
|
using Microsoft.AspNetCore.Sockets.Features;
|
||||||
|
|
@ -268,8 +268,8 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
{
|
{
|
||||||
// The connection is now in the Disconnected state so the Received event for
|
// The connection is now in the Disconnected state so the Received event for
|
||||||
// this message should not be raised
|
// this message should not be raised
|
||||||
channel.Out.TryWrite(Array.Empty<byte>());
|
channel.Writer.TryWrite(Array.Empty<byte>());
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
||||||
|
|
@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
mockTransport.Setup(t => t.StopAsync())
|
mockTransport.Setup(t => t.StopAsync())
|
||||||
.Returns(() =>
|
.Returns(() =>
|
||||||
{
|
{
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
||||||
|
|
@ -330,14 +330,14 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.StartAsync();
|
await connection.StartAsync();
|
||||||
channel.Out.TryWrite(Array.Empty<byte>());
|
channel.Writer.TryWrite(Array.Empty<byte>());
|
||||||
|
|
||||||
// Ensure that the Received callback has been called before attempting the second write
|
// Ensure that the Received callback has been called before attempting the second write
|
||||||
await callbackInvokedTcs.Task.OrTimeout();
|
await callbackInvokedTcs.Task.OrTimeout();
|
||||||
channel.Out.TryWrite(Array.Empty<byte>());
|
channel.Writer.TryWrite(Array.Empty<byte>());
|
||||||
|
|
||||||
// Ensure that SignalR isn't blocked by the receive callback
|
// Ensure that SignalR isn't blocked by the receive callback
|
||||||
Assert.False(channel.In.TryRead(out var message));
|
Assert.False(channel.Reader.TryRead(out var message));
|
||||||
|
|
||||||
closedTcs.SetResult(null);
|
closedTcs.SetResult(null);
|
||||||
|
|
||||||
|
|
@ -369,7 +369,7 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
mockTransport.Setup(t => t.StopAsync())
|
mockTransport.Setup(t => t.StopAsync())
|
||||||
.Returns(() =>
|
.Returns(() =>
|
||||||
{
|
{
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
||||||
|
|
@ -380,10 +380,10 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
connection.OnReceived(_ => blockReceiveCallbackTcs.Task);
|
connection.OnReceived(_ => blockReceiveCallbackTcs.Task);
|
||||||
|
|
||||||
await connection.StartAsync();
|
await connection.StartAsync();
|
||||||
channel.Out.TryWrite(Array.Empty<byte>());
|
channel.Writer.TryWrite(Array.Empty<byte>());
|
||||||
|
|
||||||
// Ensure that SignalR isn't blocked by the receive callback
|
// Ensure that SignalR isn't blocked by the receive callback
|
||||||
Assert.False(channel.In.TryRead(out var message));
|
Assert.False(channel.Reader.TryRead(out var message));
|
||||||
|
|
||||||
await connection.DisposeAsync();
|
await connection.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -413,7 +413,7 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
mockTransport.Setup(t => t.StopAsync())
|
mockTransport.Setup(t => t.StopAsync())
|
||||||
.Returns(() =>
|
.Returns(() =>
|
||||||
{
|
{
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Text);
|
||||||
|
|
@ -427,10 +427,10 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.StartAsync();
|
await connection.StartAsync();
|
||||||
channel.Out.TryWrite(Array.Empty<byte>());
|
channel.Writer.TryWrite(Array.Empty<byte>());
|
||||||
|
|
||||||
// Ensure that SignalR isn't blocked by the receive callback
|
// Ensure that SignalR isn't blocked by the receive callback
|
||||||
Assert.False(channel.In.TryRead(out var message));
|
Assert.False(channel.Reader.TryRead(out var message));
|
||||||
|
|
||||||
await connection.DisposeAsync();
|
await connection.DisposeAsync();
|
||||||
}
|
}
|
||||||
|
|
@ -909,7 +909,7 @@ namespace Microsoft.AspNetCore.Sockets.Client.Tests
|
||||||
mockTransport.Setup(t => t.StopAsync())
|
mockTransport.Setup(t => t.StopAsync())
|
||||||
.Returns(() =>
|
.Returns(() =>
|
||||||
{
|
{
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
});
|
});
|
||||||
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Binary);
|
mockTransport.SetupGet(t => t.Mode).Returns(TransferMode.Binary);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
|
|
||||||
public ProtocolType Type => ProtocolType.Binary;
|
public ProtocolType Type => ProtocolType.Binary;
|
||||||
|
|
||||||
public bool TryParseMessages(ReadOnlyBuffer<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
|
public bool TryParseMessages(ReadOnlyMemory<byte> input, IInvocationBinder binder, out IList<HubMessage> messages)
|
||||||
{
|
{
|
||||||
messages = new List<HubMessage>();
|
messages = new List<HubMessage>();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
|
@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
||||||
|
|
||||||
await longPollingTransport.Running.OrTimeout();
|
await longPollingTransport.Running.OrTimeout();
|
||||||
Assert.True(transportToConnection.In.Completion.IsCompleted);
|
Assert.True(transportToConnection.Reader.Completion.IsCompleted);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -135,9 +135,9 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
var channelConnection = new ChannelConnection<SendMessage, byte[]>(connectionToTransport, transportToConnection);
|
var channelConnection = new ChannelConnection<SendMessage, byte[]>(connectionToTransport, transportToConnection);
|
||||||
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
||||||
|
|
||||||
var data = await transportToConnection.In.ReadAllAsync().OrTimeout();
|
var data = await transportToConnection.Reader.ReadAllAsync().OrTimeout();
|
||||||
await longPollingTransport.Running.OrTimeout();
|
await longPollingTransport.Running.OrTimeout();
|
||||||
Assert.True(transportToConnection.In.Completion.IsCompleted);
|
Assert.True(transportToConnection.Reader.Completion.IsCompleted);
|
||||||
Assert.Equal(2, data.Count);
|
Assert.Equal(2, data.Count);
|
||||||
Assert.Equal(Encoding.UTF8.GetBytes("Hello"), data[0]);
|
Assert.Equal(Encoding.UTF8.GetBytes("Hello"), data[0]);
|
||||||
Assert.Equal(Encoding.UTF8.GetBytes("World"), data[1]);
|
Assert.Equal(Encoding.UTF8.GetBytes("World"), data[1]);
|
||||||
|
|
@ -172,7 +172,7 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
||||||
|
|
||||||
var exception =
|
var exception =
|
||||||
await Assert.ThrowsAsync<HttpRequestException>(async () => await transportToConnection.In.Completion.OrTimeout());
|
await Assert.ThrowsAsync<HttpRequestException>(async () => await transportToConnection.Reader.Completion.OrTimeout());
|
||||||
Assert.Contains(" 500 ", exception.Message);
|
Assert.Contains(" 500 ", exception.Message);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -207,16 +207,16 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
var channelConnection = new ChannelConnection<SendMessage, byte[]>(connectionToTransport, transportToConnection);
|
var channelConnection = new ChannelConnection<SendMessage, byte[]>(connectionToTransport, transportToConnection);
|
||||||
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
||||||
|
|
||||||
await connectionToTransport.Out.WriteAsync(new SendMessage());
|
await connectionToTransport.Writer.WriteAsync(new SendMessage());
|
||||||
|
|
||||||
await Assert.ThrowsAsync<HttpRequestException>(async () => await longPollingTransport.Running.OrTimeout());
|
await Assert.ThrowsAsync<HttpRequestException>(async () => await longPollingTransport.Running.OrTimeout());
|
||||||
|
|
||||||
// The channel needs to be drained for the Completion task to be completed
|
// The channel needs to be drained for the Completion task to be completed
|
||||||
while (transportToConnection.In.TryRead(out var message))
|
while (transportToConnection.Reader.TryRead(out var message))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<HttpRequestException>(async () => await transportToConnection.In.Completion);
|
var exception = await Assert.ThrowsAsync<HttpRequestException>(async () => await transportToConnection.Reader.Completion);
|
||||||
Assert.Contains(" 500 ", exception.Message);
|
Assert.Contains(" 500 ", exception.Message);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -248,12 +248,12 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
var channelConnection = new ChannelConnection<SendMessage, byte[]>(connectionToTransport, transportToConnection);
|
var channelConnection = new ChannelConnection<SendMessage, byte[]>(connectionToTransport, transportToConnection);
|
||||||
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
||||||
|
|
||||||
connectionToTransport.Out.Complete();
|
connectionToTransport.Writer.Complete();
|
||||||
|
|
||||||
await longPollingTransport.Running.OrTimeout();
|
await longPollingTransport.Running.OrTimeout();
|
||||||
|
|
||||||
await longPollingTransport.Running.OrTimeout();
|
await longPollingTransport.Running.OrTimeout();
|
||||||
await connectionToTransport.In.Completion.OrTimeout();
|
await connectionToTransport.Reader.Completion.OrTimeout();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -304,9 +304,9 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
|
|
||||||
// Pull Messages out of the channel
|
// Pull Messages out of the channel
|
||||||
var messages = new List<byte[]>();
|
var messages = new List<byte[]>();
|
||||||
while (await transportToConnection.In.WaitToReadAsync())
|
while (await transportToConnection.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
while (transportToConnection.In.TryRead(out var message))
|
while (transportToConnection.Reader.TryRead(out var message))
|
||||||
{
|
{
|
||||||
messages.Add(message);
|
messages.Add(message);
|
||||||
}
|
}
|
||||||
|
|
@ -358,16 +358,16 @@ namespace Microsoft.AspNetCore.Client.Tests
|
||||||
var tcs2 = new TaskCompletionSource<object>();
|
var tcs2 = new TaskCompletionSource<object>();
|
||||||
|
|
||||||
// Pre-queue some messages
|
// Pre-queue some messages
|
||||||
await connectionToTransport.Out.WriteAsync(new SendMessage(Encoding.UTF8.GetBytes("Hello"), tcs1)).OrTimeout();
|
await connectionToTransport.Writer.WriteAsync(new SendMessage(Encoding.UTF8.GetBytes("Hello"), tcs1)).OrTimeout();
|
||||||
await connectionToTransport.Out.WriteAsync(new SendMessage(Encoding.UTF8.GetBytes("World"), tcs2)).OrTimeout();
|
await connectionToTransport.Writer.WriteAsync(new SendMessage(Encoding.UTF8.GetBytes("World"), tcs2)).OrTimeout();
|
||||||
|
|
||||||
// Start the transport
|
// Start the transport
|
||||||
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, TransferMode.Binary, connectionId: string.Empty);
|
||||||
|
|
||||||
connectionToTransport.Out.Complete();
|
connectionToTransport.Writer.Complete();
|
||||||
|
|
||||||
await longPollingTransport.Running.OrTimeout();
|
await longPollingTransport.Running.OrTimeout();
|
||||||
await connectionToTransport.In.Completion.OrTimeout();
|
await connectionToTransport.Reader.Completion.OrTimeout();
|
||||||
|
|
||||||
Assert.Single(sentRequests);
|
Assert.Single(sentRequests);
|
||||||
Assert.Equal(new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'W', (byte)'o', (byte)'r', (byte)'l', (byte)'d'
|
Assert.Equal(new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', (byte)'W', (byte)'o', (byte)'r', (byte)'l', (byte)'d'
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -106,10 +107,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
[InlineData(new[] { "data: Hello, World\r\n", ":comment\r\n", "\r\n" }, "Hello, World")]
|
[InlineData(new[] { "data: Hello, World\r\n", ":comment\r\n", "\r\n" }, "Hello, World")]
|
||||||
public async Task ParseMessageAcrossMultipleReadsSuccess(string[] messageParts, string expectedMessage)
|
public async Task ParseMessageAcrossMultipleReadsSuccess(string[] messageParts, string expectedMessage)
|
||||||
{
|
{
|
||||||
using (var pipeFactory = new PipeFactory())
|
var parser = new ServerSentEventsMessageParser();
|
||||||
|
using (var pool = new MemoryPool())
|
||||||
{
|
{
|
||||||
var parser = new ServerSentEventsMessageParser();
|
var pipe = new Pipe(new PipeOptions(pool));
|
||||||
var pipe = pipeFactory.Create();
|
|
||||||
|
|
||||||
byte[] message = null;
|
byte[] message = null;
|
||||||
ReadCursor consumed = default, examined = default;
|
ReadCursor consumed = default, examined = default;
|
||||||
|
|
@ -152,9 +153,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
[InlineData("data: B\r\ndata: SGVs", "bG8sIFdvcmxk\r\n\n\n", "There was an error in the frame format")]
|
[InlineData("data: B\r\ndata: SGVs", "bG8sIFdvcmxk\r\n\n\n", "There was an error in the frame format")]
|
||||||
public async Task ParseMessageAcrossMultipleReadsFailure(string encodedMessagePart1, string encodedMessagePart2, string expectedMessage)
|
public async Task ParseMessageAcrossMultipleReadsFailure(string encodedMessagePart1, string encodedMessagePart2, string expectedMessage)
|
||||||
{
|
{
|
||||||
using (var pipeFactory = new PipeFactory())
|
using (var pool = new MemoryPool())
|
||||||
{
|
{
|
||||||
var pipe = pipeFactory.Create();
|
var pipe = new Pipe(new PipeOptions(pool));
|
||||||
|
|
||||||
// Read the first part of the message
|
// Read the first part of the message
|
||||||
await pipe.Writer.WriteAsync(Encoding.UTF8.GetBytes(encodedMessagePart1));
|
await pipe.Writer.WriteAsync(Encoding.UTF8.GetBytes(encodedMessagePart1));
|
||||||
|
|
@ -173,7 +174,6 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
|
|
||||||
var ex = Assert.Throws<FormatException>(() => parser.ParseMessage(result.Buffer, out consumed, out examined, out buffer));
|
var ex = Assert.Throws<FormatException>(() => parser.ParseMessage(result.Buffer, out consumed, out examined, out buffer));
|
||||||
Assert.Equal(expectedMessage, ex.Message);
|
Assert.Equal(expectedMessage, ex.Message);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,9 +181,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
[InlineData("data: foo\r\n\r\n", "data: bar\r\n\r\n")]
|
[InlineData("data: foo\r\n\r\n", "data: bar\r\n\r\n")]
|
||||||
public async Task ParseMultipleMessagesText(string message1, string message2)
|
public async Task ParseMultipleMessagesText(string message1, string message2)
|
||||||
{
|
{
|
||||||
using (var pipeFactory = new PipeFactory())
|
using (var pool = new MemoryPool())
|
||||||
{
|
{
|
||||||
var pipe = pipeFactory.Create();
|
var pipe = new Pipe(new PipeOptions(pool));
|
||||||
|
|
||||||
// Read the first part of the message
|
// Read the first part of the message
|
||||||
await pipe.Writer.WriteAsync(Encoding.UTF8.GetBytes(message1 + message2));
|
await pipe.Writer.WriteAsync(Encoding.UTF8.GetBytes(message1 + message2));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -8,8 +8,9 @@ using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Client.Tests;
|
using Microsoft.AspNetCore.Client.Tests;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
|
@ -42,6 +43,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
mockStream
|
mockStream
|
||||||
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||||
.Returns(copyToAsyncTcs.Task);
|
.Returns(copyToAsyncTcs.Task);
|
||||||
|
mockStream.Setup(s => s.CanRead).Returns(true);
|
||||||
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -83,12 +85,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||||
.Returns<Stream, int, CancellationToken>(async (stream, bufferSize, t) =>
|
.Returns<Stream, int, CancellationToken>(async (stream, bufferSize, t) =>
|
||||||
{
|
{
|
||||||
|
await Task.Yield();
|
||||||
var buffer = Encoding.ASCII.GetBytes("data: 3:abc\r\n\r\n");
|
var buffer = Encoding.ASCII.GetBytes("data: 3:abc\r\n\r\n");
|
||||||
while (!eventStreamCts.IsCancellationRequested)
|
while (!eventStreamCts.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await stream.WriteAsync(buffer, 0, buffer.Length);
|
await stream.WriteAsync(buffer, 0, buffer.Length);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
mockStream.Setup(s => s.CanRead).Returns(true);
|
||||||
|
|
||||||
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
||||||
});
|
});
|
||||||
|
|
@ -109,7 +113,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
|
|
||||||
transportActiveTask = sseTransport.Running;
|
transportActiveTask = sseTransport.Running;
|
||||||
Assert.False(transportActiveTask.IsCompleted);
|
Assert.False(transportActiveTask.IsCompleted);
|
||||||
var message = await transportToConnection.In.ReadAsync().AsTask().OrTimeout();
|
var message = await transportToConnection.Reader.ReadAsync().AsTask().OrTimeout();
|
||||||
Assert.Equal("3:abc", Encoding.ASCII.GetString(message));
|
Assert.Equal("3:abc", Encoding.ASCII.GetString(message));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
@ -140,6 +144,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
var buffer = Encoding.ASCII.GetBytes("data: 3:a");
|
var buffer = Encoding.ASCII.GetBytes("data: 3:a");
|
||||||
await stream.WriteAsync(buffer, 0, buffer.Length);
|
await stream.WriteAsync(buffer, 0, buffer.Length);
|
||||||
});
|
});
|
||||||
|
mockStream.Setup(s => s.CanRead).Returns(true);
|
||||||
|
|
||||||
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
||||||
});
|
});
|
||||||
|
|
@ -182,6 +187,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
mockStream
|
mockStream
|
||||||
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||||
.Returns(copyToAsyncTcs.Task);
|
.Returns(copyToAsyncTcs.Task);
|
||||||
|
mockStream.Setup(s => s.CanRead).Returns(true);
|
||||||
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +207,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
await eventStreamTcs.Task;
|
await eventStreamTcs.Task;
|
||||||
|
|
||||||
var sendTcs = new TaskCompletionSource<object>();
|
var sendTcs = new TaskCompletionSource<object>();
|
||||||
Assert.True(connectionToTransport.Out.TryWrite(new SendMessage(new byte[] { 0x42 }, sendTcs)));
|
Assert.True(connectionToTransport.Writer.TryWrite(new SendMessage(new byte[] { 0x42 }, sendTcs)));
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<HttpRequestException>(() => sendTcs.Task.OrTimeout());
|
var exception = await Assert.ThrowsAsync<HttpRequestException>(() => sendTcs.Task.OrTimeout());
|
||||||
Assert.Contains("500", exception.Message);
|
Assert.Contains("500", exception.Message);
|
||||||
|
|
@ -231,6 +237,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
mockStream
|
mockStream
|
||||||
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
.Setup(s => s.CopyToAsync(It.IsAny<Stream>(), It.IsAny<int>(), It.IsAny<CancellationToken>()))
|
||||||
.Returns(copyToAsyncTcs.Task);
|
.Returns(copyToAsyncTcs.Task);
|
||||||
|
mockStream.Setup(s => s.CanRead).Returns(true);
|
||||||
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
return new HttpResponseMessage { Content = new StreamContent(mockStream.Object) };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -246,7 +253,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId: string.Empty).OrTimeout();
|
new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId: string.Empty).OrTimeout();
|
||||||
await eventStreamTcs.Task.OrTimeout();
|
await eventStreamTcs.Task.OrTimeout();
|
||||||
|
|
||||||
connectionToTransport.Out.TryComplete(null);
|
connectionToTransport.Writer.TryComplete(null);
|
||||||
|
|
||||||
await sseTransport.Running.OrTimeout();
|
await sseTransport.Running.OrTimeout();
|
||||||
}
|
}
|
||||||
|
|
@ -274,7 +281,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
await sseTransport.StartAsync(
|
await sseTransport.StartAsync(
|
||||||
new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId: string.Empty).OrTimeout();
|
new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId: string.Empty).OrTimeout();
|
||||||
|
|
||||||
var message = await transportToConnection.In.ReadAsync().AsTask().OrTimeout();
|
var message = await transportToConnection.Reader.ReadAsync().AsTask().OrTimeout();
|
||||||
Assert.Equal("3:abc", Encoding.ASCII.GetString(message));
|
Assert.Equal("3:abc", Encoding.ASCII.GetString(message));
|
||||||
|
|
||||||
await sseTransport.Running.OrTimeout();
|
await sseTransport.Running.OrTimeout();
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@ using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
using Microsoft.AspNetCore.SignalR.Internal.Formatters;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
|
@ -34,8 +35,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
public Task Closed => _closeTcs.Task;
|
public Task Closed => _closeTcs.Task;
|
||||||
public Task Started => _started.Task;
|
public Task Started => _started.Task;
|
||||||
public Task Disposed => _disposed.Task;
|
public Task Disposed => _disposed.Task;
|
||||||
public ReadableChannel<byte[]> SentMessages => _sentMessages.In;
|
public ChannelReader<byte[]> SentMessages => _sentMessages.Reader;
|
||||||
public WritableChannel<byte[]> ReceivedMessages => _receivedMessages.Out;
|
public ChannelWriter<byte[]> ReceivedMessages => _receivedMessages.Writer;
|
||||||
|
|
||||||
private readonly List<ReceiveCallback> _callbacks = new List<ReceiveCallback>();
|
private readonly List<ReceiveCallback> _callbacks = new List<ReceiveCallback>();
|
||||||
|
|
||||||
|
|
@ -61,9 +62,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
throw new InvalidOperationException("Connection must be started before SendAsync can be called");
|
throw new InvalidOperationException("Connection must be started before SendAsync can be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (await _sentMessages.Out.WaitToWriteAsync(cancellationToken))
|
while (await _sentMessages.Writer.WaitToWriteAsync(cancellationToken))
|
||||||
{
|
{
|
||||||
if (_sentMessages.Out.TryWrite(data))
|
if (_sentMessages.Writer.TryWrite(data))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +101,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
var json = JsonConvert.SerializeObject(jsonObject, Formatting.None);
|
var json = JsonConvert.SerializeObject(jsonObject, Formatting.None);
|
||||||
var bytes = FormatMessageToArray(Encoding.UTF8.GetBytes(json));
|
var bytes = FormatMessageToArray(Encoding.UTF8.GetBytes(json));
|
||||||
|
|
||||||
return _receivedMessages.Out.WriteAsync(bytes);
|
return _receivedMessages.Writer.WriteAsync(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] FormatMessageToArray(byte[] message)
|
private byte[] FormatMessageToArray(byte[] message)
|
||||||
|
|
@ -116,9 +117,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
|
||||||
{
|
{
|
||||||
while (!token.IsCancellationRequested)
|
while (!token.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
while (await _receivedMessages.In.WaitToReadAsync(token))
|
while (await _receivedMessages.Reader.WaitToReadAsync(token))
|
||||||
{
|
{
|
||||||
while (_receivedMessages.In.TryRead(out var message))
|
while (_receivedMessages.Reader.TryRead(out var message))
|
||||||
{
|
{
|
||||||
ReceiveCallback[] callbackCopies;
|
ReceiveCallback[] callbackCopies;
|
||||||
lock (_callbacks)
|
lock (_callbacks)
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Encoders
|
||||||
[InlineData("12:Hello, World;", "Hello, World")]
|
[InlineData("12:Hello, World;", "Hello, World")]
|
||||||
public void ReadTextMessage(string encoded, string payload)
|
public void ReadTextMessage(string encoded, string payload)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyMemory<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
|
|
||||||
Assert.True(LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message));
|
Assert.True(LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message));
|
||||||
Assert.Equal(0, buffer.Length);
|
Assert.Equal(0, buffer.Length);
|
||||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Encoders
|
||||||
public void ReadMultipleMessages()
|
public void ReadMultipleMessages()
|
||||||
{
|
{
|
||||||
const string encoded = "0:;14:Hello,\r\nWorld!;";
|
const string encoded = "0:;14:Hello,\r\nWorld!;";
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyMemory<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
|
|
||||||
var messages = new List<byte[]>();
|
var messages = new List<byte[]>();
|
||||||
while (LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message))
|
while (LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out var message))
|
||||||
|
|
@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Encoders
|
||||||
[InlineData("5:ABCDE")]
|
[InlineData("5:ABCDE")]
|
||||||
public void ReadIncompleteMessages(string encoded)
|
public void ReadIncompleteMessages(string encoded)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyMemory<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
Assert.False(LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _));
|
Assert.False(LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Encoders
|
||||||
[InlineData("5:ABCDEF", "Missing delimiter ';' after payload")]
|
[InlineData("5:ABCDEF", "Missing delimiter ';' after payload")]
|
||||||
public void ReadInvalidMessages(string encoded, string expectedMessage)
|
public void ReadInvalidMessages(string encoded, string expectedMessage)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
ReadOnlyMemory<byte> buffer = Encoding.UTF8.GetBytes(encoded);
|
||||||
var ex = Assert.Throws<FormatException>(() =>
|
var ex = Assert.Throws<FormatException>(() =>
|
||||||
{
|
{
|
||||||
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _);
|
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _);
|
||||||
|
|
@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Encoders
|
||||||
{
|
{
|
||||||
// Invalid because first character is a UTF-8 "continuation" character
|
// Invalid because first character is a UTF-8 "continuation" character
|
||||||
// We need to include the ':' so that
|
// We need to include the ':' so that
|
||||||
ReadOnlyBuffer<byte> buffer = new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F, (byte)':' };
|
ReadOnlyMemory<byte> buffer = new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F, (byte)':' };
|
||||||
var ex = Assert.Throws<FormatException>(() =>
|
var ex = Assert.Throws<FormatException>(() =>
|
||||||
{
|
{
|
||||||
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _);
|
LengthPrefixedTextMessageParser.TryParseMessage(ref buffer, out _);
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests.Internal.Formatters
|
||||||
using (var ms = new MemoryStream())
|
using (var ms = new MemoryStream())
|
||||||
{
|
{
|
||||||
BinaryMessageFormatter.WriteMessage(payload, ms);
|
BinaryMessageFormatter.WriteMessage(payload, ms);
|
||||||
var buffer = new ReadOnlyBuffer<byte>(ms.ToArray());
|
var buffer = new ReadOnlyMemory<byte>(ms.ToArray());
|
||||||
Assert.True(BinaryMessageParser.TryParseMessage(ref buffer, out var roundtripped));
|
Assert.True(BinaryMessageParser.TryParseMessage(ref buffer, out var roundtripped));
|
||||||
Assert.Equal(payload, roundtripped.ToArray());
|
Assert.Equal(payload, roundtripped.ToArray());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[InlineData(new byte[] { 0x0B, 0x41, 0x0A, 0x52, 0x0D, 0x43, 0x0D, 0x0A, 0x3B, 0x44, 0x45, 0x46 }, "A\nR\rC\r\n;DEF")]
|
[InlineData(new byte[] { 0x0B, 0x41, 0x0A, 0x52, 0x0D, 0x43, 0x0D, 0x0A, 0x3B, 0x44, 0x45, 0x46 }, "A\nR\rC\r\n;DEF")]
|
||||||
public void ReadMessage(byte[] encoded, string payload)
|
public void ReadMessage(byte[] encoded, string payload)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> span = encoded;
|
ReadOnlyMemory<byte> span = encoded;
|
||||||
Assert.True(BinaryMessageParser.TryParseMessage(ref span, out var message));
|
Assert.True(BinaryMessageParser.TryParseMessage(ref span, out var message));
|
||||||
Assert.Equal(0, span.Length);
|
Assert.Equal(0, span.Length);
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
})]
|
})]
|
||||||
public void ReadBinaryMessage(byte[] encoded, byte[] payload)
|
public void ReadBinaryMessage(byte[] encoded, byte[] payload)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> span = encoded;
|
ReadOnlyMemory<byte> span = encoded;
|
||||||
Assert.True(BinaryMessageParser.TryParseMessage(ref span, out var message));
|
Assert.True(BinaryMessageParser.TryParseMessage(ref span, out var message));
|
||||||
Assert.Equal(0, span.Length);
|
Assert.Equal(0, span.Length);
|
||||||
Assert.Equal(payload, message.ToArray());
|
Assert.Equal(payload, message.ToArray());
|
||||||
|
|
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[InlineData(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })]
|
[InlineData(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF })]
|
||||||
public void BinaryMessageParserThrowsForMessagesOver2GB(byte[] payload)
|
public void BinaryMessageParserThrowsForMessagesOver2GB(byte[] payload)
|
||||||
{
|
{
|
||||||
var buffer = new ReadOnlyBuffer<byte>(payload);
|
var buffer = new ReadOnlyMemory<byte>(payload);
|
||||||
var ex = Assert.Throws<FormatException>(() => BinaryMessageParser.TryParseMessage(ref buffer, out var message));
|
var ex = Assert.Throws<FormatException>(() => BinaryMessageParser.TryParseMessage(ref buffer, out var message));
|
||||||
Assert.Equal("Messages over 2GB in size are not supported.", ex.Message);
|
Assert.Equal("Messages over 2GB in size are not supported.", ex.Message);
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[InlineData(new byte[] { 0x80 })] // size is cut
|
[InlineData(new byte[] { 0x80 })] // size is cut
|
||||||
public void BinaryMessageParserReturnsFalseForPartialPayloads(byte[] payload)
|
public void BinaryMessageParserReturnsFalseForPartialPayloads(byte[] payload)
|
||||||
{
|
{
|
||||||
var buffer = new ReadOnlyBuffer<byte>(payload);
|
var buffer = new ReadOnlyMemory<byte>(payload);
|
||||||
Assert.False(BinaryMessageParser.TryParseMessage(ref buffer, out var message));
|
Assert.False(BinaryMessageParser.TryParseMessage(ref buffer, out var message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
/* length: */ 0x0E,
|
/* length: */ 0x0E,
|
||||||
/* body: */ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x0D, 0x0A, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21,
|
/* body: */ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x0D, 0x0A, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21,
|
||||||
};
|
};
|
||||||
ReadOnlyBuffer<byte> buffer = encoded;
|
ReadOnlyMemory<byte> buffer = encoded;
|
||||||
|
|
||||||
var messages = new List<byte[]>();
|
var messages = new List<byte[]>();
|
||||||
while (BinaryMessageParser.TryParseMessage(ref buffer, out var message))
|
while (BinaryMessageParser.TryParseMessage(ref buffer, out var message))
|
||||||
|
|
@ -110,7 +110,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[InlineData(new byte[] { 0x09, 0x00, 0x00 })] // Not enough data for payload
|
[InlineData(new byte[] { 0x09, 0x00, 0x00 })] // Not enough data for payload
|
||||||
public void ReadIncompleteMessages(byte[] encoded)
|
public void ReadIncompleteMessages(byte[] encoded)
|
||||||
{
|
{
|
||||||
ReadOnlyBuffer<byte> buffer = encoded;
|
ReadOnlyMemory<byte> buffer = encoded;
|
||||||
Assert.False(BinaryMessageParser.TryParseMessage(ref buffer, out var message));
|
Assert.False(BinaryMessageParser.TryParseMessage(ref buffer, out var message));
|
||||||
Assert.Equal(encoded.Length, buffer.Length);
|
Assert.Equal(encoded.Length, buffer.Length);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ReadMessage()
|
public void ReadMessage()
|
||||||
{
|
{
|
||||||
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC\u001e"));
|
var message = new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes("ABC\u001e"));
|
||||||
|
|
||||||
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
|
|
@ -23,14 +23,14 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TryReadingIncompleteMessage()
|
public void TryReadingIncompleteMessage()
|
||||||
{
|
{
|
||||||
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC"));
|
var message = new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes("ABC"));
|
||||||
Assert.False(TextMessageParser.TryParseMessage(ref message, out var payload));
|
Assert.False(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TryReadingMultipleMessages()
|
public void TryReadingMultipleMessages()
|
||||||
{
|
{
|
||||||
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC\u001eXYZ\u001e"));
|
var message = new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes("ABC\u001eXYZ\u001e"));
|
||||||
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
Assert.True(TextMessageParser.TryParseMessage(ref message, out payload));
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out payload));
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Tests.Internal.Formatters
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IncompleteTrailingMessage()
|
public void IncompleteTrailingMessage()
|
||||||
{
|
{
|
||||||
var message = new ReadOnlyBuffer<byte>(Encoding.UTF8.GetBytes("ABC\u001eXYZ\u001e123"));
|
var message = new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes("ABC\u001eXYZ\u001e123"));
|
||||||
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out var payload));
|
||||||
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
Assert.Equal("ABC", Encoding.UTF8.GetString(payload.ToArray()));
|
||||||
Assert.True(TextMessageParser.TryParseMessage(ref message, out payload));
|
Assert.True(TextMessageParser.TryParseMessage(ref message, out payload));
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests;
|
using Microsoft.AspNetCore.SignalR.Tests;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
|
|
@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
|
|
||||||
AssertMessage(output1);
|
AssertMessage(output1);
|
||||||
|
|
||||||
Assert.False(output2.In.TryRead(out var item));
|
Assert.False(output2.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
|
|
||||||
AssertMessage(output1);
|
AssertMessage(output1);
|
||||||
|
|
||||||
Assert.False(output2.In.TryRead(out var item));
|
Assert.False(output2.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,7 +201,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
|
|
||||||
AssertMessage(output1);
|
AssertMessage(output1);
|
||||||
|
|
||||||
Assert.False(output2.In.TryRead(out var item));
|
Assert.False(output2.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,7 +286,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
|
|
||||||
await manager.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
await manager.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
Assert.False(output.In.TryRead(out var item));
|
Assert.False(output.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,7 +387,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
await manager.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
await manager.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
AssertMessage(output);
|
AssertMessage(output);
|
||||||
Assert.False(output.In.TryRead(out var item));
|
Assert.False(output.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,7 +417,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
await manager2.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
await manager2.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
AssertMessage(output);
|
AssertMessage(output);
|
||||||
Assert.False(output.In.TryRead(out var item));
|
Assert.False(output.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -451,7 +451,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
|
|
||||||
await manager2.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
await manager2.InvokeGroupAsync("name", "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
Assert.False(output.In.TryRead(out var item));
|
Assert.False(output.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -480,7 +480,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
await manager1.InvokeConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
|
await manager1.InvokeConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
AssertMessage(output);
|
AssertMessage(output);
|
||||||
Assert.False(output.In.TryRead(out var item));
|
Assert.False(output.Reader.TryRead(out var item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,10 +499,10 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
using (var client = new TestClient())
|
using (var client = new TestClient())
|
||||||
{
|
{
|
||||||
// Force an exception when writing to connection
|
// Force an exception when writing to connection
|
||||||
var output = new Mock<Channel<HubMessage>>();
|
var writer = new Mock<ChannelWriter<HubMessage>>();
|
||||||
output.Setup(o => o.Out.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception());
|
writer.Setup(o => o.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception());
|
||||||
|
|
||||||
var connection = new HubConnectionContext(output.Object, client.Connection);
|
var connection = new HubConnectionContext(new MockChannel(writer.Object), client.Connection);
|
||||||
|
|
||||||
await manager2.OnConnectedAsync(connection).OrTimeout();
|
await manager2.OnConnectedAsync(connection).OrTimeout();
|
||||||
|
|
||||||
|
|
@ -523,10 +523,10 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
using (var client = new TestClient())
|
using (var client = new TestClient())
|
||||||
{
|
{
|
||||||
// Force an exception when writing to connection
|
// Force an exception when writing to connection
|
||||||
var output = new Mock<Channel<HubMessage>>();
|
var writer = new Mock<ChannelWriter<HubMessage>>();
|
||||||
output.Setup(o => o.Out.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception("Message"));
|
writer.Setup(o => o.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception("Message"));
|
||||||
|
|
||||||
var connection = new HubConnectionContext(output.Object, client.Connection);
|
var connection = new HubConnectionContext(new MockChannel(writer.Object), client.Connection);
|
||||||
|
|
||||||
await manager.OnConnectedAsync(connection).OrTimeout();
|
await manager.OnConnectedAsync(connection).OrTimeout();
|
||||||
|
|
||||||
|
|
@ -549,10 +549,10 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
var output2 = Channel.CreateUnbounded<HubMessage>();
|
var output2 = Channel.CreateUnbounded<HubMessage>();
|
||||||
|
|
||||||
// Force an exception when writing to connection
|
// Force an exception when writing to connection
|
||||||
var output = new Mock<Channel<HubMessage>>();
|
var writer = new Mock<ChannelWriter<HubMessage>>();
|
||||||
output.Setup(o => o.Out.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception());
|
writer.Setup(o => o.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception());
|
||||||
|
|
||||||
var connection1 = new HubConnectionContext(output.Object, client1.Connection);
|
var connection1 = new HubConnectionContext(new MockChannel(writer.Object), client1.Connection);
|
||||||
var connection2 = new HubConnectionContext(output2, client2.Connection);
|
var connection2 = new HubConnectionContext(output2, client2.Connection);
|
||||||
|
|
||||||
await manager.OnConnectedAsync(connection1).OrTimeout();
|
await manager.OnConnectedAsync(connection1).OrTimeout();
|
||||||
|
|
@ -573,7 +573,7 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
|
|
||||||
private void AssertMessage(Channel<HubMessage> channel)
|
private void AssertMessage(Channel<HubMessage> channel)
|
||||||
{
|
{
|
||||||
Assert.True(channel.In.TryRead(out var item));
|
Assert.True(channel.Reader.TryRead(out var item));
|
||||||
var message = Assert.IsType<InvocationMessage>(item);
|
var message = Assert.IsType<InvocationMessage>(item);
|
||||||
Assert.Equal("Hello", message.Target);
|
Assert.Equal("Hello", message.Target);
|
||||||
Assert.Single(message.Arguments);
|
Assert.Single(message.Arguments);
|
||||||
|
|
@ -583,5 +583,13 @@ namespace Microsoft.AspNetCore.SignalR.Redis.Tests
|
||||||
private class MyHub : Hub
|
private class MyHub : Hub
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MockChannel : Channel<HubMessage>
|
||||||
|
{
|
||||||
|
public MockChannel(ChannelWriter<HubMessage> writer = null)
|
||||||
|
{
|
||||||
|
Writer = writer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -29,13 +29,13 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
await manager.InvokeAllAsync("Hello", new object[] { "World" }).OrTimeout();
|
await manager.InvokeAllAsync("Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
Assert.True(output1.In.TryRead(out var item));
|
Assert.True(output1.Reader.TryRead(out var item));
|
||||||
var message = Assert.IsType<InvocationMessage>(item);
|
var message = Assert.IsType<InvocationMessage>(item);
|
||||||
Assert.Equal("Hello", message.Target);
|
Assert.Equal("Hello", message.Target);
|
||||||
Assert.Single(message.Arguments);
|
Assert.Single(message.Arguments);
|
||||||
Assert.Equal("World", (string)message.Arguments[0]);
|
Assert.Equal("World", (string)message.Arguments[0]);
|
||||||
|
|
||||||
Assert.True(output2.In.TryRead(out item));
|
Assert.True(output2.Reader.TryRead(out item));
|
||||||
message = Assert.IsType<InvocationMessage>(item);
|
message = Assert.IsType<InvocationMessage>(item);
|
||||||
Assert.Equal("Hello", message.Target);
|
Assert.Equal("Hello", message.Target);
|
||||||
Assert.Single(message.Arguments);
|
Assert.Single(message.Arguments);
|
||||||
|
|
@ -63,13 +63,13 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
await manager.InvokeAllAsync("Hello", new object[] { "World" }).OrTimeout();
|
await manager.InvokeAllAsync("Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
Assert.True(output1.In.TryRead(out var item));
|
Assert.True(output1.Reader.TryRead(out var item));
|
||||||
var message = Assert.IsType<InvocationMessage>(item);
|
var message = Assert.IsType<InvocationMessage>(item);
|
||||||
Assert.Equal("Hello", message.Target);
|
Assert.Equal("Hello", message.Target);
|
||||||
Assert.Single(message.Arguments);
|
Assert.Single(message.Arguments);
|
||||||
Assert.Equal("World", (string)message.Arguments[0]);
|
Assert.Equal("World", (string)message.Arguments[0]);
|
||||||
|
|
||||||
Assert.False(output2.In.TryRead(out item));
|
Assert.False(output2.Reader.TryRead(out item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,13 +93,13 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
await manager.InvokeGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout();
|
await manager.InvokeGroupAsync("gunit", "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
Assert.True(output1.In.TryRead(out var item));
|
Assert.True(output1.Reader.TryRead(out var item));
|
||||||
var message = Assert.IsType<InvocationMessage>(item);
|
var message = Assert.IsType<InvocationMessage>(item);
|
||||||
Assert.Equal("Hello", message.Target);
|
Assert.Equal("Hello", message.Target);
|
||||||
Assert.Single(message.Arguments);
|
Assert.Single(message.Arguments);
|
||||||
Assert.Equal("World", (string)message.Arguments[0]);
|
Assert.Equal("World", (string)message.Arguments[0]);
|
||||||
|
|
||||||
Assert.False(output2.In.TryRead(out item));
|
Assert.False(output2.Reader.TryRead(out item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +116,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
||||||
await manager.InvokeConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
|
await manager.InvokeConnectionAsync(connection.ConnectionId, "Hello", new object[] { "World" }).OrTimeout();
|
||||||
|
|
||||||
Assert.True(output.In.TryRead(out var item));
|
Assert.True(output.Reader.TryRead(out var item));
|
||||||
var message = Assert.IsType<InvocationMessage>(item);
|
var message = Assert.IsType<InvocationMessage>(item);
|
||||||
Assert.Equal("Hello", message.Target);
|
Assert.Equal("Hello", message.Target);
|
||||||
Assert.Single(message.Arguments);
|
Assert.Single(message.Arguments);
|
||||||
|
|
@ -130,11 +130,11 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
using (var client = new TestClient())
|
using (var client = new TestClient())
|
||||||
{
|
{
|
||||||
// Force an exception when writing to connection
|
// Force an exception when writing to connection
|
||||||
var output = new Mock<Channel<HubMessage>>();
|
var writer = new Mock<ChannelWriter<HubMessage>>();
|
||||||
output.Setup(o => o.Out.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception("Message"));
|
writer.Setup(o => o.WaitToWriteAsync(It.IsAny<CancellationToken>())).Throws(new Exception("Message"));
|
||||||
|
|
||||||
var manager = new DefaultHubLifetimeManager<MyHub>();
|
var manager = new DefaultHubLifetimeManager<MyHub>();
|
||||||
var connection = new HubConnectionContext(output.Object, client.Connection);
|
var connection = new HubConnectionContext(new MockChannel(writer.Object), client.Connection);
|
||||||
|
|
||||||
await manager.OnConnectedAsync(connection).OrTimeout();
|
await manager.OnConnectedAsync(connection).OrTimeout();
|
||||||
|
|
||||||
|
|
@ -168,5 +168,14 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MockChannel: Channel<HubMessage>
|
||||||
|
{
|
||||||
|
|
||||||
|
public MockChannel(ChannelWriter<HubMessage> writer = null)
|
||||||
|
{
|
||||||
|
Writer = writer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.SignalR.Tests
|
namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
|
|
@ -10,7 +11,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
{
|
{
|
||||||
public async override Task OnConnectedAsync(ConnectionContext connection)
|
public async override Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
await connection.Transport.Out.WriteAsync(await connection.Transport.In.ReadAsync());
|
await connection.Transport.Writer.WriteAsync(await connection.Transport.Reader.ReadAsync());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ using System.Runtime.Serialization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal;
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
|
|
@ -259,7 +259,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
using (var client = new TestClient())
|
using (var client = new TestClient())
|
||||||
{
|
{
|
||||||
// TestClient automatically writes negotiate, for this test we want to assume negotiate never gets sent
|
// TestClient automatically writes negotiate, for this test we want to assume negotiate never gets sent
|
||||||
client.Connection.Transport.In.TryRead(out var item);
|
client.Connection.Transport.Reader.TryRead(out var item);
|
||||||
|
|
||||||
var endPointTask = endPoint.OnConnectedAsync(client.Connection);
|
var endPointTask = endPoint.OnConnectedAsync(client.Connection);
|
||||||
|
|
||||||
|
|
@ -285,7 +285,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
using (var client = new TestClient())
|
using (var client = new TestClient())
|
||||||
{
|
{
|
||||||
// TestClient automatically writes negotiate, for this test we want to assume negotiate never gets sent
|
// TestClient automatically writes negotiate, for this test we want to assume negotiate never gets sent
|
||||||
client.Connection.Transport.In.TryRead(out var item);
|
client.Connection.Transport.Reader.TryRead(out var item);
|
||||||
|
|
||||||
await endPoint.OnConnectedAsync(client.Connection).OrTimeout();
|
await endPoint.OnConnectedAsync(client.Connection).OrTimeout();
|
||||||
}
|
}
|
||||||
|
|
@ -521,7 +521,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
await client.SendInvocationAsync(methodName, nonBlocking: true).OrTimeout();
|
await client.SendInvocationAsync(methodName, nonBlocking: true).OrTimeout();
|
||||||
|
|
||||||
// Nothing should have been written
|
// Nothing should have been written
|
||||||
Assert.False(client.Application.In.TryRead(out var buffer));
|
Assert.False(client.Application.Reader.TryRead(out var buffer));
|
||||||
|
|
||||||
// kill the connection
|
// kill the connection
|
||||||
client.Dispose();
|
client.Dispose();
|
||||||
|
|
@ -1595,7 +1595,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
return new CountingObservable(count);
|
return new CountingObservable(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadableChannel<string> CounterChannel(int count)
|
public ChannelReader<string> CounterChannel(int count)
|
||||||
{
|
{
|
||||||
var channel = Channel.CreateUnbounded<string>();
|
var channel = Channel.CreateUnbounded<string>();
|
||||||
|
|
||||||
|
|
@ -1603,17 +1603,17 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
{
|
{
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
await channel.Out.WriteAsync(i.ToString());
|
await channel.Writer.WriteAsync(i.ToString());
|
||||||
}
|
}
|
||||||
channel.Out.Complete();
|
channel.Writer.Complete();
|
||||||
});
|
});
|
||||||
|
|
||||||
return channel.In;
|
return channel.Reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadableChannel<string> BlockingStream()
|
public ChannelReader<string> BlockingStream()
|
||||||
{
|
{
|
||||||
return Channel.CreateUnbounded<string>().In;
|
return Channel.CreateUnbounded<string>().Reader;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CountingObservable : IObservable<string>
|
private class CountingObservable : IObservable<string>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal;
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
using Microsoft.AspNetCore.SignalR.Internal.Protocol;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Protocol.Tests
|
||||||
[MemberData(nameof(HubProtocols))]
|
[MemberData(nameof(HubProtocols))]
|
||||||
public void DefaultHubProtocolResolverTestsCanCreateSupportedProtocols(IHubProtocol protocol)
|
public void DefaultHubProtocolResolverTestsCanCreateSupportedProtocols(IHubProtocol protocol)
|
||||||
{
|
{
|
||||||
var mockConnection = new Mock<HubConnectionContext>(Channel.CreateUnbounded<HubMessage>().Out, new Mock<ConnectionContext>().Object);
|
var mockConnection = new Mock<HubConnectionContext>(Channel.CreateUnbounded<HubMessage>().Writer, new Mock<ConnectionContext>().Object);
|
||||||
Assert.IsType(
|
Assert.IsType(
|
||||||
protocol.GetType(),
|
protocol.GetType(),
|
||||||
new DefaultHubProtocolResolver(Options.Create(new HubOptions())).GetProtocol(protocol.Name, mockConnection.Object));
|
new DefaultHubProtocolResolver(Options.Create(new HubOptions())).GetProtocol(protocol.Name, mockConnection.Object));
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.SignalR.Common.Protocol.Tests
|
||||||
[InlineData("dummy")]
|
[InlineData("dummy")]
|
||||||
public void DefaultHubProtocolResolverThrowsForNotSupportedProtocol(string protocolName)
|
public void DefaultHubProtocolResolverThrowsForNotSupportedProtocol(string protocolName)
|
||||||
{
|
{
|
||||||
var mockConnection = new Mock<HubConnectionContext>(Channel.CreateUnbounded<HubMessage>().Out, new Mock<ConnectionContext>().Object);
|
var mockConnection = new Mock<HubConnectionContext>(Channel.CreateUnbounded<HubMessage>().Writer, new Mock<ConnectionContext>().Object);
|
||||||
var exception = Assert.Throws<NotSupportedException>(
|
var exception = Assert.Throws<NotSupportedException>(
|
||||||
() => new DefaultHubProtocolResolver(Options.Create(new HubOptions())).GetProtocol(protocolName, mockConnection.Object));
|
() => new DefaultHubProtocolResolver(Options.Create(new HubOptions())).GetProtocol(protocolName, mockConnection.Object));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets;
|
using Microsoft.AspNetCore.Sockets;
|
||||||
using Microsoft.AspNetCore.Sockets.Client;
|
using Microsoft.AspNetCore.Sockets.Client;
|
||||||
|
|
@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
var webSocketsTransport = new WebSocketsTransport(loggerFactory);
|
var webSocketsTransport = new WebSocketsTransport(loggerFactory);
|
||||||
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/echo"), channelConnection,
|
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/echo"), channelConnection,
|
||||||
TransferMode.Binary, connectionId: string.Empty);
|
TransferMode.Binary, connectionId: string.Empty);
|
||||||
connectionToTransport.Out.TryComplete();
|
connectionToTransport.Writer.TryComplete();
|
||||||
await webSocketsTransport.Running.OrTimeout(TimeSpan.FromSeconds(10));
|
await webSocketsTransport.Running.OrTimeout(TimeSpan.FromSeconds(10));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/echo"), channelConnection, transferMode, connectionId: string.Empty);
|
await webSocketsTransport.StartAsync(new Uri(_serverFixture.WebSocketsUrl + "/echo"), channelConnection, transferMode, connectionId: string.Empty);
|
||||||
|
|
||||||
var sendTcs = new TaskCompletionSource<object>();
|
var sendTcs = new TaskCompletionSource<object>();
|
||||||
connectionToTransport.Out.TryWrite(new SendMessage(new byte[] { 0x42 }, sendTcs));
|
connectionToTransport.Writer.TryWrite(new SendMessage(new byte[] { 0x42 }, sendTcs));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await sendTcs.Task;
|
await sendTcs.Task;
|
||||||
|
|
@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests
|
||||||
// The echo endpoint closes the connection immediately after sending response which should stop the transport
|
// The echo endpoint closes the connection immediately after sending response which should stop the transport
|
||||||
await webSocketsTransport.Running.OrTimeout();
|
await webSocketsTransport.Running.OrTimeout();
|
||||||
|
|
||||||
Assert.True(transportToConnection.In.TryRead(out var buffer));
|
Assert.True(transportToConnection.Reader.TryRead(out var buffer));
|
||||||
Assert.Equal(new byte[] { 0x42 }, buffer);
|
Assert.Equal(new byte[] { 0x42 }, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,12 +82,12 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
|
|
||||||
connection.ApplicationTask = Task.Run(async () =>
|
connection.ApplicationTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Assert.False(await connection.Transport.In.WaitToReadAsync());
|
Assert.False(await connection.Transport.Reader.WaitToReadAsync());
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.TransportTask = Task.Run(async () =>
|
connection.TransportTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
Assert.False(await connection.Application.In.WaitToReadAsync());
|
Assert.False(await connection.Application.Reader.WaitToReadAsync());
|
||||||
});
|
});
|
||||||
|
|
||||||
connectionManager.CloseConnections();
|
connectionManager.CloseConnections();
|
||||||
|
|
@ -197,7 +197,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
appLifetime.StopApplication();
|
appLifetime.StopApplication();
|
||||||
|
|
||||||
// Connection should be disposed so this should complete immediately
|
// Connection should be disposed so this should complete immediately
|
||||||
Assert.False(await connection.Application.Out.WaitToWriteAsync().OrTimeout());
|
Assert.False(await connection.Application.Writer.WaitToWriteAsync().OrTimeout());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConnectionManager CreateConnectionManager(IApplicationLifetime lifetime = null)
|
private static ConnectionManager CreateConnectionManager(IApplicationLifetime lifetime = null)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -511,7 +511,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var buffer = Encoding.UTF8.GetBytes("Hello World");
|
var buffer = Encoding.UTF8.GetBytes("Hello World");
|
||||||
|
|
||||||
// Write to the transport so the poll yields
|
// Write to the transport so the poll yields
|
||||||
await connection.Transport.Out.WriteAsync(buffer);
|
await connection.Transport.Writer.WriteAsync(buffer);
|
||||||
|
|
||||||
await task;
|
await task;
|
||||||
|
|
||||||
|
|
@ -543,7 +543,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var buffer = Encoding.UTF8.GetBytes("Hello World");
|
var buffer = Encoding.UTF8.GetBytes("Hello World");
|
||||||
|
|
||||||
// Write to the application
|
// Write to the application
|
||||||
await connection.Application.Out.WriteAsync(buffer);
|
await connection.Application.Writer.WriteAsync(buffer);
|
||||||
|
|
||||||
await task;
|
await task;
|
||||||
|
|
||||||
|
|
@ -573,7 +573,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var buffer = Encoding.UTF8.GetBytes("Hello World");
|
var buffer = Encoding.UTF8.GetBytes("Hello World");
|
||||||
|
|
||||||
// Write to the application
|
// Write to the application
|
||||||
await connection.Application.Out.WriteAsync(buffer);
|
await connection.Application.Writer.WriteAsync(buffer);
|
||||||
|
|
||||||
await task;
|
await task;
|
||||||
|
|
||||||
|
|
@ -606,7 +606,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
await task1.OrTimeout();
|
await task1.OrTimeout();
|
||||||
|
|
||||||
// Send a message from the app to complete Task 2
|
// Send a message from the app to complete Task 2
|
||||||
await connection.Transport.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello, World"));
|
await connection.Transport.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello, World"));
|
||||||
|
|
||||||
await task2.OrTimeout();
|
await task2.OrTimeout();
|
||||||
|
|
||||||
|
|
@ -775,7 +775,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") }));
|
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") }));
|
||||||
|
|
||||||
var endPointTask = dispatcher.ExecuteAsync(context, options, app);
|
var endPointTask = dispatcher.ExecuteAsync(context, options, app);
|
||||||
await connection.Transport.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).OrTimeout();
|
await connection.Transport.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).OrTimeout();
|
||||||
|
|
||||||
await endPointTask.OrTimeout();
|
await endPointTask.OrTimeout();
|
||||||
|
|
||||||
|
|
@ -853,7 +853,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
}));
|
}));
|
||||||
|
|
||||||
var endPointTask = dispatcher.ExecuteAsync(context, options, app);
|
var endPointTask = dispatcher.ExecuteAsync(context, options, app);
|
||||||
await connection.Transport.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).OrTimeout();
|
await connection.Transport.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).OrTimeout();
|
||||||
|
|
||||||
await endPointTask.OrTimeout();
|
await endPointTask.OrTimeout();
|
||||||
|
|
||||||
|
|
@ -907,7 +907,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") }));
|
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "name") }));
|
||||||
|
|
||||||
var endPointTask = dispatcher.ExecuteAsync(context, options, app);
|
var endPointTask = dispatcher.ExecuteAsync(context, options, app);
|
||||||
await connection.Transport.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).OrTimeout();
|
await connection.Transport.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello, World")).OrTimeout();
|
||||||
|
|
||||||
await endPointTask.OrTimeout();
|
await endPointTask.OrTimeout();
|
||||||
|
|
||||||
|
|
@ -1110,7 +1110,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
{
|
{
|
||||||
public override Task OnConnectedAsync(ConnectionContext connection)
|
public override Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
connection.Transport.In.WaitToReadAsync().Wait();
|
connection.Transport.Reader.WaitToReadAsync().Wait();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1135,7 +1135,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
{
|
{
|
||||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
while (await connection.Transport.In.WaitToReadAsync())
|
while (await connection.Transport.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Transports;
|
using Microsoft.AspNetCore.Sockets.Internal.Transports;
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
var poll = new LongPollingTransport(CancellationToken.None, channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
|
var poll = new LongPollingTransport(CancellationToken.None, channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
|
||||||
|
|
||||||
Assert.True(channel.Out.TryComplete());
|
Assert.True(channel.Writer.TryComplete());
|
||||||
|
|
||||||
await poll.ProcessRequestAsync(context, context.RequestAborted);
|
await poll.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
|
|
@ -56,9 +56,9 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
|
|
||||||
await channel.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
|
await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
|
||||||
|
|
||||||
Assert.True(channel.Out.TryComplete());
|
Assert.True(channel.Writer.TryComplete());
|
||||||
|
|
||||||
await poll.ProcessRequestAsync(context, context.RequestAborted);
|
await poll.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
|
|
@ -76,11 +76,11 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
|
|
||||||
await channel.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello"));
|
await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello"));
|
||||||
await channel.Out.WriteAsync(Encoding.UTF8.GetBytes(" "));
|
await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes(" "));
|
||||||
await channel.Out.WriteAsync(Encoding.UTF8.GetBytes("World"));
|
await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes("World"));
|
||||||
|
|
||||||
Assert.True(channel.Out.TryComplete());
|
Assert.True(channel.Writer.TryComplete());
|
||||||
|
|
||||||
await poll.ProcessRequestAsync(context, context.RequestAborted);
|
await poll.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -13,12 +13,21 @@ using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Tests
|
namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
{
|
{
|
||||||
public class MapEndPointTests
|
public class MapEndPointTests
|
||||||
{
|
{
|
||||||
|
private ITestOutputHelper _output;
|
||||||
|
|
||||||
|
public MapEndPointTests(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
_output = output;
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void MapEndPointFindsAuthAttributeOnEndPoint()
|
public void MapEndPointFindsAuthAttributeOnEndPoint()
|
||||||
{
|
{
|
||||||
|
|
@ -40,6 +49,10 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
.ConfigureLogging(factory =>
|
||||||
|
{
|
||||||
|
factory.AddXunit(_output, LogLevel.Trace);
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Assert.Equal(1, authCount);
|
Assert.Equal(1, authCount);
|
||||||
|
|
@ -66,6 +79,10 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
.ConfigureLogging(factory =>
|
||||||
|
{
|
||||||
|
factory.AddXunit(_output, LogLevel.Trace);
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Assert.Equal(1, authCount);
|
Assert.Equal(1, authCount);
|
||||||
|
|
@ -92,6 +109,10 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
.ConfigureLogging(factory =>
|
||||||
|
{
|
||||||
|
factory.AddXunit(_output, LogLevel.Trace);
|
||||||
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
Assert.Equal(2, authCount);
|
Assert.Equal(2, authCount);
|
||||||
|
|
@ -102,24 +123,28 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
public async Task MapEndPointWithWebSocketSubProtocolSetsProtocol()
|
public async Task MapEndPointWithWebSocketSubProtocolSetsProtocol()
|
||||||
{
|
{
|
||||||
var host = new WebHostBuilder()
|
var host = new WebHostBuilder()
|
||||||
.UseUrls("http://127.0.0.1:0")
|
.UseUrls("http://127.0.0.1:0")
|
||||||
.UseKestrel()
|
.UseKestrel()
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddSockets();
|
||||||
|
services.AddEndPoint<MyEndPoint>();
|
||||||
|
})
|
||||||
|
.Configure(app =>
|
||||||
|
{
|
||||||
|
app.UseSockets(routes =>
|
||||||
{
|
{
|
||||||
services.AddSockets();
|
routes.MapEndPoint<MyEndPoint>("socket", httpSocketOptions =>
|
||||||
services.AddEndPoint<MyEndPoint>();
|
|
||||||
})
|
|
||||||
.Configure(app =>
|
|
||||||
{
|
|
||||||
app.UseSockets(routes =>
|
|
||||||
{
|
{
|
||||||
routes.MapEndPoint<MyEndPoint>("socket", httpSocketOptions =>
|
httpSocketOptions.WebSockets.SubProtocol = "protocol1";
|
||||||
{
|
|
||||||
httpSocketOptions.WebSockets.SubProtocol = "protocol1";
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
.Build();
|
})
|
||||||
|
.ConfigureLogging(factory =>
|
||||||
|
{
|
||||||
|
factory.AddXunit(_output, LogLevel.Trace);
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
await host.StartAsync();
|
await host.StartAsync();
|
||||||
|
|
||||||
|
|
@ -140,7 +165,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
{
|
{
|
||||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
while (!await connection.Transport.In.WaitToReadAsync())
|
while (!await connection.Transport.Reader.WaitToReadAsync())
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>
|
||||||
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.0</TargetFrameworks>
|
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.0</TargetFrameworks>
|
||||||
|
|
||||||
<RuntimeIdentifier Condition="'$(TargetFramework)' != 'netcoreapp2.0'">win7-x64</RuntimeIdentifier>
|
<RuntimeIdentifier Condition="'$(TargetFramework)' != 'netcoreapp2.0'">win7-x64</RuntimeIdentifier>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="$(MicrosoftAspNetCoreServerKestrelPackageVersion)" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonPackageVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
var sse = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
|
var sse = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
|
||||||
|
|
||||||
Assert.True(channel.Out.TryComplete());
|
Assert.True(channel.Writer.TryComplete());
|
||||||
|
|
||||||
await sse.ProcessRequestAsync(context, context.RequestAborted);
|
await sse.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
context.Features.Set<IHttpBufferingFeature>(feature);
|
context.Features.Set<IHttpBufferingFeature>(feature);
|
||||||
var sse = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
|
var sse = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
|
||||||
|
|
||||||
Assert.True(channel.Out.TryComplete());
|
Assert.True(channel.Writer.TryComplete());
|
||||||
|
|
||||||
await sse.ProcessRequestAsync(context, context.RequestAborted);
|
await sse.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SSEWritesMessages()
|
public async Task SSEWritesMessages()
|
||||||
{
|
{
|
||||||
var channel = Channel.CreateUnbounded<byte[]>(new ChannelOptimizations
|
var channel = Channel.CreateUnbounded<byte[]>(new UnboundedChannelOptions
|
||||||
{
|
{
|
||||||
AllowSynchronousContinuations = true
|
AllowSynchronousContinuations = true
|
||||||
});
|
});
|
||||||
|
|
@ -62,11 +62,11 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
|
|
||||||
var task = sse.ProcessRequestAsync(context, context.RequestAborted);
|
var task = sse.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
await channel.Out.WriteAsync(Encoding.ASCII.GetBytes("Hello"));
|
await channel.Writer.WriteAsync(Encoding.ASCII.GetBytes("Hello"));
|
||||||
|
|
||||||
Assert.Equal(":\r\ndata: Hello\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray()));
|
Assert.Equal(":\r\ndata: Hello\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray()));
|
||||||
|
|
||||||
channel.Out.TryComplete();
|
channel.Writer.TryComplete();
|
||||||
|
|
||||||
await task.OrTimeout();
|
await task.OrTimeout();
|
||||||
}
|
}
|
||||||
|
|
@ -83,9 +83,9 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
context.Response.Body = ms;
|
context.Response.Body = ms;
|
||||||
|
|
||||||
await channel.Out.WriteAsync(Encoding.UTF8.GetBytes(message));
|
await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes(message));
|
||||||
|
|
||||||
Assert.True(channel.Out.TryComplete());
|
Assert.True(channel.Writer.TryComplete());
|
||||||
|
|
||||||
await sse.ProcessRequestAsync(context, context.RequestAborted);
|
await sse.ProcessRequestAsync(context, context.RequestAborted);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
||||||
|
|
@ -22,8 +22,8 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
var clientToServer = Channel.CreateUnbounded<WebSocketMessage>();
|
var clientToServer = Channel.CreateUnbounded<WebSocketMessage>();
|
||||||
var serverToClient = Channel.CreateUnbounded<WebSocketMessage>();
|
var serverToClient = Channel.CreateUnbounded<WebSocketMessage>();
|
||||||
|
|
||||||
var clientSocket = new WebSocketChannel(serverToClient.In, clientToServer.Out);
|
var clientSocket = new WebSocketChannel(serverToClient.Reader, clientToServer.Writer);
|
||||||
var serverSocket = new WebSocketChannel(clientToServer.In, serverToClient.Out);
|
var serverSocket = new WebSocketChannel(clientToServer.Reader, serverToClient.Writer);
|
||||||
|
|
||||||
Client = clientSocket;
|
Client = clientSocket;
|
||||||
return Task.FromResult<WebSocket>(serverSocket);
|
return Task.FromResult<WebSocket>(serverSocket);
|
||||||
|
|
@ -35,14 +35,14 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
|
|
||||||
public class WebSocketChannel : WebSocket
|
public class WebSocketChannel : WebSocket
|
||||||
{
|
{
|
||||||
private readonly ReadableChannel<WebSocketMessage> _input;
|
private readonly ChannelReader<WebSocketMessage> _input;
|
||||||
private readonly WritableChannel<WebSocketMessage> _output;
|
private readonly ChannelWriter<WebSocketMessage> _output;
|
||||||
|
|
||||||
private WebSocketCloseStatus? _closeStatus;
|
private WebSocketCloseStatus? _closeStatus;
|
||||||
private string _closeStatusDescription;
|
private string _closeStatusDescription;
|
||||||
private WebSocketState _state;
|
private WebSocketState _state;
|
||||||
|
|
||||||
public WebSocketChannel(ReadableChannel<WebSocketMessage> input, WritableChannel<WebSocketMessage> output)
|
public WebSocketChannel(ChannelReader<WebSocketMessage> input, ChannelWriter<WebSocketMessage> output)
|
||||||
{
|
{
|
||||||
_input = input;
|
_input = input;
|
||||||
_output = output;
|
_output = output;
|
||||||
|
|
@ -209,4 +209,4 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
public string CloseStatusDescription { get; set; }
|
public string CloseStatusDescription { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -6,58 +6,68 @@ using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks.Channels;
|
using System.Threading.Channels;
|
||||||
|
using Microsoft.AspNetCore.SignalR.Internal;
|
||||||
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
using Microsoft.AspNetCore.SignalR.Tests.Common;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal;
|
using Microsoft.AspNetCore.Sockets.Internal;
|
||||||
using Microsoft.AspNetCore.Sockets.Internal.Transports;
|
using Microsoft.AspNetCore.Sockets.Internal.Transports;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Sockets.Tests
|
namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
{
|
{
|
||||||
public class WebSocketsTests
|
public class WebSocketsTests : LoggedTest
|
||||||
{
|
{
|
||||||
|
public WebSocketsTests(ITestOutputHelper output)
|
||||||
|
: base(output)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(WebSocketMessageType.Text)]
|
[InlineData(WebSocketMessageType.Text)]
|
||||||
[InlineData(WebSocketMessageType.Binary)]
|
[InlineData(WebSocketMessageType.Binary)]
|
||||||
public async Task ReceivedFramesAreWrittenToChannel(WebSocketMessageType webSocketMessageType)
|
public async Task ReceivedFramesAreWrittenToChannel(WebSocketMessageType webSocketMessageType)
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
// Give the server socket to the transport and run it
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
|
{
|
||||||
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
|
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
// Run the client socket
|
// Give the server socket to the transport and run it
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
||||||
|
|
||||||
// Send a frame, then close
|
// Run the client socket
|
||||||
await feature.Client.SendAsync(
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
buffer: new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello")),
|
|
||||||
messageType: webSocketMessageType,
|
|
||||||
endOfMessage: true,
|
|
||||||
cancellationToken: CancellationToken.None);
|
|
||||||
await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
|
||||||
|
|
||||||
var buffer = await applicationSide.In.ReadAsync();
|
// Send a frame, then close
|
||||||
Assert.Equal("Hello", Encoding.UTF8.GetString(buffer));
|
await feature.Client.SendAsync(
|
||||||
|
buffer: new ArraySegment<byte>(Encoding.UTF8.GetBytes("Hello")),
|
||||||
|
messageType: webSocketMessageType,
|
||||||
|
endOfMessage: true,
|
||||||
|
cancellationToken: CancellationToken.None);
|
||||||
|
await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
||||||
|
|
||||||
Assert.True(applicationSide.Out.TryComplete());
|
var buffer = await applicationSide.Reader.ReadAsync();
|
||||||
|
Assert.Equal("Hello", Encoding.UTF8.GetString(buffer));
|
||||||
|
|
||||||
// The transport should finish now
|
Assert.True(applicationSide.Writer.TryComplete());
|
||||||
await transport;
|
|
||||||
|
|
||||||
// The connection should close after this, which means the client will get a close frame.
|
// The transport should finish now
|
||||||
var clientSummary = await client;
|
await transport;
|
||||||
|
|
||||||
Assert.Equal(WebSocketCloseStatus.NormalClosure, clientSummary.CloseResult.CloseStatus);
|
// The connection should close after this, which means the client will get a close frame.
|
||||||
|
var clientSummary = await client;
|
||||||
|
|
||||||
|
Assert.Equal(WebSocketCloseStatus.NormalClosure, clientSummary.CloseResult.CloseStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,256 +76,276 @@ namespace Microsoft.AspNetCore.Sockets.Tests
|
||||||
[InlineData(TransferMode.Binary, WebSocketMessageType.Binary)]
|
[InlineData(TransferMode.Binary, WebSocketMessageType.Binary)]
|
||||||
public async Task WebSocketTransportSetsMessageTypeBasedOnTransferModeFeature(TransferMode transferMode, WebSocketMessageType expectedMessageType)
|
public async Task WebSocketTransportSetsMessageTypeBasedOnTransferModeFeature(TransferMode transferMode, WebSocketMessageType expectedMessageType)
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null) { TransferMode = transferMode };
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
var ws = new WebSocketsTransport(new WebSocketOptions(),
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
|
||||||
|
|
||||||
// Give the server socket to the transport and run it
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
|
{
|
||||||
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null) { TransferMode = transferMode };
|
||||||
|
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
// Run the client socket
|
// Give the server socket to the transport and run it
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
||||||
|
|
||||||
// Write to the output channel, and then complete it
|
// Run the client socket
|
||||||
await applicationSide.Out.WriteAsync(Encoding.UTF8.GetBytes("Hello"));
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
Assert.True(applicationSide.Out.TryComplete());
|
|
||||||
|
|
||||||
// The client should finish now, as should the server
|
// Write to the output channel, and then complete it
|
||||||
var clientSummary = await client;
|
await applicationSide.Writer.WriteAsync(Encoding.UTF8.GetBytes("Hello"));
|
||||||
await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
Assert.True(applicationSide.Writer.TryComplete());
|
||||||
await transport;
|
|
||||||
|
|
||||||
Assert.Equal(1, clientSummary.Received.Count);
|
// The client should finish now, as should the server
|
||||||
Assert.True(clientSummary.Received[0].EndOfMessage);
|
var clientSummary = await client;
|
||||||
Assert.Equal(expectedMessageType, clientSummary.Received[0].MessageType);
|
await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
||||||
Assert.Equal("Hello", Encoding.UTF8.GetString(clientSummary.Received[0].Buffer));
|
await transport;
|
||||||
|
|
||||||
|
Assert.Equal(1, clientSummary.Received.Count);
|
||||||
|
Assert.True(clientSummary.Received[0].EndOfMessage);
|
||||||
|
Assert.Equal(expectedMessageType, clientSummary.Received[0].MessageType);
|
||||||
|
Assert.Equal("Hello", Encoding.UTF8.GetString(clientSummary.Received[0].Buffer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TransportFailsWhenClientDisconnectsAbnormally()
|
public async Task TransportFailsWhenClientDisconnectsAbnormally()
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
async Task CompleteApplicationAfterTransportCompletes()
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
{
|
{
|
||||||
// Wait until the transport completes so that we can end the application
|
async Task CompleteApplicationAfterTransportCompletes()
|
||||||
await applicationSide.In.WaitToReadAsync();
|
{
|
||||||
|
// Wait until the transport completes so that we can end the application
|
||||||
|
await applicationSide.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
// Complete the application so that the connection unwinds without aborting
|
// Complete the application so that the connection unwinds without aborting
|
||||||
applicationSide.Out.TryComplete();
|
applicationSide.Writer.TryComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
|
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
|
// Give the server socket to the transport and run it
|
||||||
|
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
||||||
|
|
||||||
|
// Run the client socket
|
||||||
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
|
|
||||||
|
// When the close frame is received, we complete the application so the send
|
||||||
|
// loop unwinds
|
||||||
|
_ = CompleteApplicationAfterTransportCompletes();
|
||||||
|
|
||||||
|
// Terminate the client to server channel with an exception
|
||||||
|
feature.Client.SendAbort();
|
||||||
|
|
||||||
|
// Wait for the transport
|
||||||
|
await Assert.ThrowsAsync<WebSocketException>(() => transport).OrTimeout();
|
||||||
|
|
||||||
|
var summary = await client.OrTimeout();
|
||||||
|
Assert.Equal(WebSocketCloseStatus.InternalServerError, summary.CloseResult.CloseStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
|
||||||
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
|
||||||
|
|
||||||
// Give the server socket to the transport and run it
|
|
||||||
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
|
||||||
|
|
||||||
// Run the client socket
|
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
|
||||||
|
|
||||||
// When the close frame is received, we complete the application so the send
|
|
||||||
// loop unwinds
|
|
||||||
_ = CompleteApplicationAfterTransportCompletes();
|
|
||||||
|
|
||||||
// Terminate the client to server channel with an exception
|
|
||||||
feature.Client.SendAbort();
|
|
||||||
|
|
||||||
// Wait for the transport
|
|
||||||
await Assert.ThrowsAsync<WebSocketException>(() => transport).OrTimeout();
|
|
||||||
|
|
||||||
var summary = await client.OrTimeout();
|
|
||||||
Assert.Equal(WebSocketCloseStatus.InternalServerError, summary.CloseResult.CloseStatus);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ClientReceivesInternalServerErrorWhenTheApplicationFails()
|
public async Task ClientReceivesInternalServerErrorWhenTheApplicationFails()
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
// Give the server socket to the transport and run it
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
|
{
|
||||||
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
|
var ws = new WebSocketsTransport(new WebSocketOptions(), transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
// Run the client socket
|
// Give the server socket to the transport and run it
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
var transport = ws.ProcessSocketAsync(await feature.AcceptAsync());
|
||||||
|
|
||||||
// Fail in the app
|
// Run the client socket
|
||||||
Assert.True(applicationSide.Out.TryComplete(new InvalidOperationException("Catastrophic failure.")));
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
var clientSummary = await client.OrTimeout();
|
|
||||||
Assert.Equal(WebSocketCloseStatus.InternalServerError, clientSummary.CloseResult.CloseStatus);
|
|
||||||
|
|
||||||
// Close from the client
|
// Fail in the app
|
||||||
await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
Assert.True(applicationSide.Writer.TryComplete(new InvalidOperationException("Catastrophic failure.")));
|
||||||
|
var clientSummary = await client.OrTimeout();
|
||||||
|
Assert.Equal(WebSocketCloseStatus.InternalServerError, clientSummary.CloseResult.CloseStatus);
|
||||||
|
|
||||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => transport.OrTimeout());
|
// Close from the client
|
||||||
Assert.Equal("Catastrophic failure.", ex.Message);
|
await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None);
|
||||||
|
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => transport.OrTimeout());
|
||||||
|
Assert.Equal("Catastrophic failure.", ex.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TransportClosesOnCloseTimeoutIfClientDoesNotSendCloseFrame()
|
public async Task TransportClosesOnCloseTimeoutIfClientDoesNotSendCloseFrame()
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var options = new WebSocketOptions()
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
{
|
{
|
||||||
CloseTimeout = TimeSpan.FromSeconds(1)
|
var options = new WebSocketOptions()
|
||||||
};
|
{
|
||||||
|
CloseTimeout = TimeSpan.FromSeconds(1)
|
||||||
|
};
|
||||||
|
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
var serverSocket = await feature.AcceptAsync();
|
var serverSocket = await feature.AcceptAsync();
|
||||||
// Give the server socket to the transport and run it
|
// Give the server socket to the transport and run it
|
||||||
var transport = ws.ProcessSocketAsync(serverSocket);
|
var transport = ws.ProcessSocketAsync(serverSocket);
|
||||||
|
|
||||||
// End the app
|
// End the app
|
||||||
applicationSide.Dispose();
|
applicationSide.Dispose();
|
||||||
|
|
||||||
await transport.OrTimeout(TimeSpan.FromSeconds(10));
|
await transport.OrTimeout(TimeSpan.FromSeconds(10));
|
||||||
|
|
||||||
// Now we're closed
|
// Now we're closed
|
||||||
Assert.Equal(WebSocketState.Aborted, serverSocket.State);
|
Assert.Equal(WebSocketState.Aborted, serverSocket.State);
|
||||||
|
|
||||||
serverSocket.Dispose();
|
serverSocket.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TransportFailsOnTimeoutWithErrorWhenApplicationFailsAndClientDoesNotSendCloseFrame()
|
public async Task TransportFailsOnTimeoutWithErrorWhenApplicationFailsAndClientDoesNotSendCloseFrame()
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var options = new WebSocketOptions
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
{
|
{
|
||||||
CloseTimeout = TimeSpan.FromSeconds(1)
|
var options = new WebSocketOptions
|
||||||
};
|
{
|
||||||
|
CloseTimeout = TimeSpan.FromSeconds(1)
|
||||||
|
};
|
||||||
|
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
var serverSocket = await feature.AcceptAsync();
|
var serverSocket = await feature.AcceptAsync();
|
||||||
// Give the server socket to the transport and run it
|
// Give the server socket to the transport and run it
|
||||||
var transport = ws.ProcessSocketAsync(serverSocket);
|
var transport = ws.ProcessSocketAsync(serverSocket);
|
||||||
|
|
||||||
// Run the client socket
|
// Run the client socket
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
|
|
||||||
// fail the client to server channel
|
// fail the client to server channel
|
||||||
applicationToTransport.Out.TryComplete(new Exception());
|
applicationToTransport.Writer.TryComplete(new Exception());
|
||||||
|
|
||||||
await Assert.ThrowsAsync<Exception>(() => transport).OrTimeout();
|
await Assert.ThrowsAsync<Exception>(() => transport).OrTimeout();
|
||||||
|
|
||||||
Assert.Equal(WebSocketState.Aborted, serverSocket.State);
|
Assert.Equal(WebSocketState.Aborted, serverSocket.State);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ServerGracefullyClosesWhenApplicationEndsThenClientSendsCloseFrame()
|
public async Task ServerGracefullyClosesWhenApplicationEndsThenClientSendsCloseFrame()
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var options = new WebSocketOptions
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
{
|
{
|
||||||
// We want to verify behavior without timeout affecting it
|
var options = new WebSocketOptions
|
||||||
CloseTimeout = TimeSpan.FromSeconds(20)
|
{
|
||||||
};
|
// We want to verify behavior without timeout affecting it
|
||||||
|
CloseTimeout = TimeSpan.FromSeconds(20)
|
||||||
|
};
|
||||||
|
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
var serverSocket = await feature.AcceptAsync();
|
var serverSocket = await feature.AcceptAsync();
|
||||||
// Give the server socket to the transport and run it
|
// Give the server socket to the transport and run it
|
||||||
var transport = ws.ProcessSocketAsync(serverSocket);
|
var transport = ws.ProcessSocketAsync(serverSocket);
|
||||||
|
|
||||||
// Run the client socket
|
// Run the client socket
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
|
|
||||||
// close the client to server channel
|
// close the client to server channel
|
||||||
applicationToTransport.Out.TryComplete();
|
applicationToTransport.Writer.TryComplete();
|
||||||
|
|
||||||
_ = await client.OrTimeout();
|
_ = await client.OrTimeout();
|
||||||
|
|
||||||
await feature.Client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).OrTimeout();
|
await feature.Client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).OrTimeout();
|
||||||
|
|
||||||
await transport.OrTimeout();
|
await transport.OrTimeout();
|
||||||
|
|
||||||
Assert.Equal(WebSocketCloseStatus.NormalClosure, serverSocket.CloseStatus);
|
Assert.Equal(WebSocketCloseStatus.NormalClosure, serverSocket.CloseStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ServerGracefullyClosesWhenClientSendsCloseFrameThenApplicationEnds()
|
public async Task ServerGracefullyClosesWhenClientSendsCloseFrameThenApplicationEnds()
|
||||||
{
|
{
|
||||||
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
using (StartLog(out var loggerFactory))
|
||||||
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
|
||||||
|
|
||||||
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
|
||||||
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
|
||||||
using (var feature = new TestWebSocketConnectionFeature())
|
|
||||||
{
|
{
|
||||||
var options = new WebSocketOptions
|
var transportToApplication = Channel.CreateUnbounded<byte[]>();
|
||||||
|
var applicationToTransport = Channel.CreateUnbounded<byte[]>();
|
||||||
|
|
||||||
|
using (var transportSide = ChannelConnection.Create<byte[]>(applicationToTransport, transportToApplication))
|
||||||
|
using (var applicationSide = ChannelConnection.Create<byte[]>(transportToApplication, applicationToTransport))
|
||||||
|
using (var feature = new TestWebSocketConnectionFeature())
|
||||||
{
|
{
|
||||||
// We want to verify behavior without timeout affecting it
|
var options = new WebSocketOptions
|
||||||
CloseTimeout = TimeSpan.FromSeconds(20)
|
{
|
||||||
};
|
// We want to verify behavior without timeout affecting it
|
||||||
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
CloseTimeout = TimeSpan.FromSeconds(20)
|
||||||
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory: new LoggerFactory());
|
};
|
||||||
|
var connectionContext = new DefaultConnectionContext(string.Empty, null, null);
|
||||||
|
var ws = new WebSocketsTransport(options, transportSide, connectionContext, loggerFactory);
|
||||||
|
|
||||||
var serverSocket = await feature.AcceptAsync();
|
var serverSocket = await feature.AcceptAsync();
|
||||||
// Give the server socket to the transport and run it
|
// Give the server socket to the transport and run it
|
||||||
var transport = ws.ProcessSocketAsync(serverSocket);
|
var transport = ws.ProcessSocketAsync(serverSocket);
|
||||||
|
|
||||||
// Run the client socket
|
// Run the client socket
|
||||||
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
var client = feature.Client.ExecuteAndCaptureFramesAsync();
|
||||||
|
|
||||||
await feature.Client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).OrTimeout();
|
await feature.Client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).OrTimeout();
|
||||||
|
|
||||||
// close the client to server channel
|
// close the client to server channel
|
||||||
applicationToTransport.Out.TryComplete();
|
applicationToTransport.Writer.TryComplete();
|
||||||
|
|
||||||
_ = await client.OrTimeout();
|
_ = await client.OrTimeout();
|
||||||
|
|
||||||
await transport.OrTimeout();
|
await transport.OrTimeout();
|
||||||
|
|
||||||
Assert.Equal(WebSocketCloseStatus.NormalClosure, serverSocket.CloseStatus);
|
Assert.Equal(WebSocketCloseStatus.NormalClosure, serverSocket.CloseStatus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue