From 1a5138e97299510dd12fe73648cfae7477dc7afc Mon Sep 17 00:00:00 2001 From: David Fowler Date: Sun, 2 Oct 2016 03:23:23 -0700 Subject: [PATCH] Added hub sample --- .../SocketsSample/EndPoints/HubEndpoint.cs | 81 +++++++++++++++++ .../EndPoints/JsonRpcEndpoint.cs | 30 +++++-- samples/SocketsSample/Hubs/Chat.cs | 15 ++++ samples/SocketsSample/Hubs/Hub.cs | 64 ++++++++++++++ .../{EndPoints => JsonRpc}/Echo.cs | 0 samples/SocketsSample/Startup.cs | 2 + samples/SocketsSample/wwwroot/hubs.html | 87 +++++++++++++++++++ samples/SocketsSample/wwwroot/index.html | 5 +- 8 files changed, 278 insertions(+), 6 deletions(-) create mode 100644 samples/SocketsSample/EndPoints/HubEndpoint.cs create mode 100644 samples/SocketsSample/Hubs/Chat.cs create mode 100644 samples/SocketsSample/Hubs/Hub.cs rename samples/SocketsSample/{EndPoints => JsonRpc}/Echo.cs (100%) create mode 100644 samples/SocketsSample/wwwroot/hubs.html diff --git a/samples/SocketsSample/EndPoints/HubEndpoint.cs b/samples/SocketsSample/EndPoints/HubEndpoint.cs new file mode 100644 index 0000000000..eaf2f1a5ff --- /dev/null +++ b/samples/SocketsSample/EndPoints/HubEndpoint.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Channels; +using Microsoft.AspNetCore.Sockets; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using SocketsSample.Hubs; + +namespace SocketsSample +{ + public class HubEndpoint : JsonRpcEndpoint + { + private readonly ILogger _logger; + private readonly Bus _bus = new Bus(); + + public HubEndpoint(ILogger logger, ILogger jsonRpcLogger, IServiceProvider serviceProvider) + : base(jsonRpcLogger, serviceProvider) + { + _logger = logger; + } + + public override Task OnConnected(Connection connection) + { + // TODO: Get the list of hubs and signals over the connection + + // Subscribe to the hub + _bus.Subscribe(nameof(Chat), message => OnMessage(connection, message)); + + // Subscribe to the connection id + _bus.Subscribe(connection.ConnectionId, message => OnMessage(connection, message)); + + return base.OnConnected(connection); + } + + protected override bool HandleResponse(string connectionId, JObject response) + { + var ignore = _bus.Publish(connectionId, new Message + { + Payload = Encoding.UTF8.GetBytes(response.ToString()) + }); + + return true; + } + + private Task OnMessage(Connection connection, Message message) + { + return connection.Channel.Output.WriteAsync(message.Payload); + } + + public Task Invoke(string key, string method, object[] args) + { + var obj = new JObject(); + obj["method"] = method; + obj["params"] = new JArray(args.Select(a => JToken.FromObject(a)).ToArray()); + + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("Outgoing RPC invocation method '{methodName}' to {signal}", method, key); + } + + return _bus.Publish(key, new Message + { + Payload = Encoding.UTF8.GetBytes(obj.ToString()) + }); + } + + protected override void Initialize(object endpoint) + { + ((Hub)endpoint).Clients = new HubConnectionContext(endpoint.GetType().Name, this); + base.Initialize(endpoint); + } + + protected override void DiscoverEndpoints() + { + // Register the chat hub + RegisterJsonRPCEndPoint(typeof(Chat)); + } + } +} diff --git a/samples/SocketsSample/EndPoints/JsonRpcEndpoint.cs b/samples/SocketsSample/EndPoints/JsonRpcEndpoint.cs index 1450182084..6628b5007e 100644 --- a/samples/SocketsSample/EndPoints/JsonRpcEndpoint.cs +++ b/samples/SocketsSample/EndPoints/JsonRpcEndpoint.cs @@ -26,6 +26,11 @@ namespace SocketsSample _logger = logger; _serviceProvider = serviceProvider; + DiscoverEndpoints(); + } + + protected virtual void DiscoverEndpoints() + { RegisterJsonRPCEndPoint(typeof(Echo)); } @@ -81,15 +86,28 @@ namespace SocketsSample response["error"] = string.Format("Unknown method '{0}'", request.Value("method")); } - _logger.LogDebug("Sending JSON RPC response: {data}", response); + if (!HandleResponse(connection.ConnectionId, response)) + { + _logger.LogDebug("Sending JSON RPC response: {data}", response); - var writer = new JsonTextWriter(new StreamWriter(stream)); - response.WriteTo(writer); - writer.Flush(); + var writer = new JsonTextWriter(new StreamWriter(stream)); + response.WriteTo(writer); + writer.Flush(); + } } } - private void RegisterJsonRPCEndPoint(Type type) + protected virtual bool HandleResponse(string connectionId, JObject response) + { + return false; + } + + protected virtual void Initialize(object endpoint) + { + + } + + protected void RegisterJsonRPCEndPoint(Type type) { var methods = new List(); @@ -123,6 +141,8 @@ namespace SocketsSample { object value = scope.ServiceProvider.GetService(type) ?? Activator.CreateInstance(type); + Initialize(value); + try { var args = request.Value("params").Zip(parameters, (a, p) => a.ToObject(p.ParameterType)) diff --git a/samples/SocketsSample/Hubs/Chat.cs b/samples/SocketsSample/Hubs/Chat.cs new file mode 100644 index 0000000000..abef1b6029 --- /dev/null +++ b/samples/SocketsSample/Hubs/Chat.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace SocketsSample.Hubs +{ + public class Chat : Hub + { + public void Send(string message) + { + Clients.All.Invoke("Send", message); + } + } +} diff --git a/samples/SocketsSample/Hubs/Hub.cs b/samples/SocketsSample/Hubs/Hub.cs new file mode 100644 index 0000000000..accf7e9b82 --- /dev/null +++ b/samples/SocketsSample/Hubs/Hub.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace SocketsSample.Hubs +{ + public class Hub + { + public IHubConnectionContext Clients { get; set; } + } + + public interface IHubConnectionContext + { + IClientProxy All { get; } + + IClientProxy Client(string connectionId); + } + + public class HubConnectionContext : IHubConnectionContext + { + private readonly HubEndpoint _endPoint; + + public HubConnectionContext(string hubName, HubEndpoint endpoint) + { + _endPoint = endpoint; + All = new HubClientProxy(endpoint, hubName); + } + + public IClientProxy All { get; } + + public IClientProxy Client(string connectionId) + { + return new HubClientProxy(_endPoint, connectionId); + } + } + + public class HubClientProxy : IClientProxy + { + private readonly HubEndpoint _endPoint; + private readonly string _key; + + public HubClientProxy(HubEndpoint endPoint, string key) + { + _endPoint = endPoint; + _key = key; + } + + public Task Invoke(string method, params object[] args) + { + return _endPoint.Invoke(_key, method, args); + } + } + + public interface IClientProxy + { + /// + /// Invokes a method on the connection(s) represented by the instance. + /// + /// name of the method to invoke + /// argumetns to pass to the client + /// A task that represents when the data has been sent to the client. + Task Invoke(string method, params object[] args); + } +} diff --git a/samples/SocketsSample/EndPoints/Echo.cs b/samples/SocketsSample/JsonRpc/Echo.cs similarity index 100% rename from samples/SocketsSample/EndPoints/Echo.cs rename to samples/SocketsSample/JsonRpc/Echo.cs diff --git a/samples/SocketsSample/Startup.cs b/samples/SocketsSample/Startup.cs index 7865200ea2..bf64b38ded 100644 --- a/samples/SocketsSample/Startup.cs +++ b/samples/SocketsSample/Startup.cs @@ -14,6 +14,7 @@ namespace SocketsSample { services.AddRouting(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } @@ -32,6 +33,7 @@ namespace SocketsSample app.UseSockets(d => { + d.MapSocketEndpoint("/hubs"); d.MapSocketEndpoint("/chat"); d.MapSocketEndpoint("/jsonrpc"); }); diff --git a/samples/SocketsSample/wwwroot/hubs.html b/samples/SocketsSample/wwwroot/hubs.html new file mode 100644 index 0000000000..997e955c41 --- /dev/null +++ b/samples/SocketsSample/wwwroot/hubs.html @@ -0,0 +1,87 @@ + + + + + + + + +

WebSockets

+ + + +
    + + \ No newline at end of file diff --git a/samples/SocketsSample/wwwroot/index.html b/samples/SocketsSample/wwwroot/index.html index e410c4c5c9..556cd5831a 100644 --- a/samples/SocketsSample/wwwroot/index.html +++ b/samples/SocketsSample/wwwroot/index.html @@ -13,6 +13,9 @@
  • Web Sockets
  • JSON RPC

    - JSON RPC + \ No newline at end of file