// 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.Collections.Generic; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils { public class MethodHub : TestHub { public Task GroupRemoveMethod(string groupName) { return Groups.RemoveAsync(Context.ConnectionId, groupName); } public Task ClientSendMethod(string userId, string message) { return Clients.User(userId).InvokeAsync("Send", message); } public Task ConnectionSendMethod(string connectionId, string message) { return Clients.Client(connectionId).InvokeAsync("Send", message); } public Task SendToMultipleClients(string message, IReadOnlyList connectionIds) { return Clients.Clients(connectionIds).InvokeAsync("Send", message); } public Task GroupAddMethod(string groupName) { return Groups.AddAsync(Context.ConnectionId, groupName); } public Task GroupSendMethod(string groupName, string message) { return Clients.Group(groupName).InvokeAsync("Send", message); } public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList excludedIds) { return Clients.GroupExcept(groupName, excludedIds).InvokeAsync("Send", message); } public Task SendToOthersInGroup(string groupName, string message) { return Clients.OthersInGroup(groupName).InvokeAsync("Send", message); } public Task BroadcastMethod(string message) { return Clients.All.InvokeAsync("Broadcast", message); } public Task BroadcastItem() { return Clients.All.InvokeAsync("Broadcast", new Result { Message = "test", paramName = "param" }); } public Task SendArray() { return Clients.All.InvokeAsync("Array", new int[] { 1, 2, 3 }); } public Task TaskValueMethod() { return Task.FromResult(42); } public int ValueMethod() { return 43; } [HubMethodName("RenamedMethod")] public int ATestMethodThatIsRenamedByTheAttribute() { return 43; } public string Echo(string data) { return data; } public void VoidMethod() { } public string ConcatString(byte b, int i, char c, string s) { return $"{b}, {i}, {c}, {s}"; } public Task SendAnonymousObject() { return Clients.Client(Context.ConnectionId).InvokeAsync("Send", new { }); } public override Task OnDisconnectedAsync(Exception e) { return Task.CompletedTask; } public void MethodThatThrows() { throw new InvalidOperationException("BOOM!"); } public Task MethodThatYieldsFailedTask() { return Task.FromException(new InvalidOperationException("BOOM!")); } public static void StaticMethod() { } [Authorize("test")] public void AuthMethod() { } public Task SendToAllExcept(string message, IReadOnlyList excludedIds) { return Clients.AllExcept(excludedIds).InvokeAsync("Send", message); } public bool HasHttpContext() { return Context.Connection.GetHttpContext() != null; } public Task SendToOthers(string message) { return Clients.Others.InvokeAsync("Send", message); } public Task SendToCaller(string message) { return Clients.Caller.InvokeAsync("Send", message); } } public abstract class TestHub : Hub { public override Task OnConnectedAsync() { var tcs = (TaskCompletionSource)Context.Connection.Metadata["ConnectedTask"]; tcs?.TrySetResult(true); return base.OnConnectedAsync(); } } public class DynamicTestHub : DynamicHub { public override Task OnConnectedAsync() { var tcs = (TaskCompletionSource)Context.Connection.Metadata["ConnectedTask"]; tcs?.TrySetResult(true); return base.OnConnectedAsync(); } public string Echo(string data) { return data; } public Task ClientSendMethod(string userId, string message) { return Clients.User(userId).Send(message); } public Task ConnectionSendMethod(string connectionId, string message) { return Clients.Client(connectionId).Send(message); } public Task SendToMultipleClients(string message, IReadOnlyList connectionIds) { return Clients.Clients(connectionIds).Send(message); } public Task GroupAddMethod(string groupName) { return Groups.AddAsync(Context.ConnectionId, groupName); } public Task GroupSendMethod(string groupName, string message) { return Clients.Group(groupName).Send(message); } public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList excludedIds) { return Clients.GroupExcept(groupName, excludedIds).Send(message); } public Task SendToOthersInGroup(string groupName, string message) { return Clients.OthersInGroup(groupName).Send(message); } public Task BroadcastMethod(string message) { return Clients.All.Broadcast(message); } public Task SendToAllExcept(string message, IReadOnlyList excludedIds) { return Clients.AllExcept(excludedIds).Send(message); } public Task SendToOthers(string message) { return Clients.Others.Send(message); } public Task SendToCaller(string message) { return Clients.Caller.Send(message); } } public class HubT : Hub { public override Task OnConnectedAsync() { var tcs = (TaskCompletionSource)Context.Connection.Metadata["ConnectedTask"]; tcs?.TrySetResult(true); return base.OnConnectedAsync(); } public string Echo(string data) { return data; } public Task ClientSendMethod(string userId, string message) { return Clients.User(userId).Send(message); } public Task ConnectionSendMethod(string connectionId, string message) { return Clients.Client(connectionId).Send(message); } public Task SendToMultipleClients(string message, IReadOnlyList connectionIds) { return Clients.Clients(connectionIds).Send(message); } public async Task DelayedSend(string connectionId, string message) { await Task.Delay(100); await Clients.Client(connectionId).Send(message); } public Task GroupAddMethod(string groupName) { return Groups.AddAsync(Context.ConnectionId, groupName); } public Task GroupSendMethod(string groupName, string message) { return Clients.Group(groupName).Send(message); } public Task GroupExceptSendMethod(string groupName, string message, IReadOnlyList excludedIds) { return Clients.GroupExcept(groupName, excludedIds).Send(message); } public Task SendToOthersInGroup(string groupName, string message) { return Clients.OthersInGroup(groupName).Send(message); } public Task BroadcastMethod(string message) { return Clients.All.Broadcast(message); } public Task SendToAllExcept(string message, IReadOnlyList excludedIds) { return Clients.AllExcept(excludedIds).Send(message); } public Task SendToOthers(string message) { return Clients.Others.Send(message); } public Task SendToCaller(string message) { return Clients.Caller.Send(message); } } public interface Test { Task Send(string message); Task Broadcast(string message); } public class OnConnectedThrowsHub : Hub { public override Task OnConnectedAsync() { var tcs = new TaskCompletionSource(); tcs.SetException(new InvalidOperationException("Hub OnConnected failed.")); return tcs.Task; } } public class OnDisconnectedThrowsHub : TestHub { public override Task OnDisconnectedAsync(Exception exception) { var tcs = new TaskCompletionSource(); tcs.SetException(new InvalidOperationException("Hub OnDisconnected failed.")); return tcs.Task; } } public class InheritedHub : BaseHub { public override int VirtualMethod(int num) { return num - 10; } public override int VirtualMethodRenamed() { return 34; } } public class BaseHub : TestHub { public string BaseMethod(string message) { return message; } public virtual int VirtualMethod(int num) { return num; } [HubMethodName("RenamedVirtualMethod")] public virtual int VirtualMethodRenamed() { return 43; } } public class InvalidHub : TestHub { public void OverloadedMethod(int num) { } public void OverloadedMethod(string message) { } } public class DisposeTrackingHub : TestHub { private TrackDispose _trackDispose; public DisposeTrackingHub(TrackDispose trackDispose) { _trackDispose = trackDispose; } protected override void Dispose(bool dispose) { if (dispose) { _trackDispose.DisposeCount++; } } } public class ObservableHub : Hub { private readonly Observable _numbers; public ObservableHub(Observable numbers) { _numbers = numbers; } public IObservable Subscribe() => _numbers; } public class AbortHub : Hub { public void Kill() { Context.Connection.Abort(); } } public class Observable : IObservable { public List> Observers = new List>(); public Action> OnSubscribe; public Action> OnDispose; public IDisposable Subscribe(IObserver observer) { lock (Observers) { Observers.Add(observer); } OnSubscribe?.Invoke(observer); return new DisposableAction(() => { lock (Observers) { Observers.Remove(observer); } OnDispose?.Invoke(observer); }); } public void OnNext(T value) { lock (Observers) { foreach (var observer in Observers) { observer.OnNext(value); } } } public void Complete() { lock (Observers) { foreach (var observer in Observers) { observer.OnCompleted(); } } } private class DisposableAction : IDisposable { private readonly Action _action; public DisposableAction(Action action) { _action = action; } public void Dispose() { _action(); } } } public class StreamingHub : TestHub { public IObservable CounterObservable(int count) { return new CountingObservable(count); } public ChannelReader CounterChannel(int count) { var channel = Channel.CreateUnbounded(); var task = Task.Run(async () => { for (int i = 0; i < count; i++) { await channel.Writer.WriteAsync(i.ToString()); } channel.Writer.Complete(); }); return channel.Reader; } public ChannelReader BlockingStream() { return Channel.CreateUnbounded().Reader; } private class CountingObservable : IObservable { private int _count; public CountingObservable(int count) { _count = count; } public IDisposable Subscribe(IObserver observer) { var cts = new CancellationTokenSource(); Task.Run(() => { for (int i = 0; !cts.Token.IsCancellationRequested && i < _count; i++) { observer.OnNext(i.ToString()); } observer.OnCompleted(); }); return new CancellationDisposable(cts); } } } public class SimpleHub : Hub { public override async Task OnConnectedAsync() { await Clients.All.InvokeAsync("Send", $"{Context.ConnectionId} joined"); await base.OnConnectedAsync(); } } public class SimpleTypedHub : Hub { public override async Task OnConnectedAsync() { await Clients.All.Send($"{Context.ConnectionId} joined"); await base.OnConnectedAsync(); } } public interface ITypedHubClient { Task Send(string message); } public class ConnectionLifetimeHub : Hub { private ConnectionLifetimeState _state; public ConnectionLifetimeHub(ConnectionLifetimeState state) { _state = state; } public override Task OnConnectedAsync() { _state.TokenStateInConnected = Context.Connection.ConnectionAbortedToken.IsCancellationRequested; Context.Connection.ConnectionAbortedToken.Register(() => { _state.TokenCallbackTriggered = true; }); return base.OnConnectedAsync(); } public override Task OnDisconnectedAsync(Exception exception) { _state.TokenStateInDisconnected = Context.Connection.ConnectionAbortedToken.IsCancellationRequested; return base.OnDisconnectedAsync(exception); } } public class ConnectionLifetimeState { public bool TokenCallbackTriggered { get; set; } public bool TokenStateInConnected { get; set; } public bool TokenStateInDisconnected { get; set; } } }