Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Implemented hub pipeline

- Added IHubPipelineModule as an extensibility point
- Added ability to add Modules to the IPipeline on the Host
commit 83fdbfd9baa1f1cc3399d7f210cb062597c8084c 1 parent 3fa839a
@davidfowl davidfowl authored halter73 committed
View
12 SignalR.Hosting.Common/GlobalHost.cs
@@ -1,4 +1,5 @@
using System;
+using SignalR.Hubs;
namespace SignalR
{
@@ -46,5 +47,16 @@ public static IConnectionManager ConnectionManager
return DependencyResolver.Resolve<IConnectionManager>();
}
}
+
+ /// <summary>
+ ///
+ /// </summary>
+ public static IHubPipeline HubPipeline
+ {
+ get
+ {
+ return DependencyResolver.Resolve<IHubPipeline>();
+ }
+ }
}
}
View
15 SignalR.Hosting.Common/Host.cs
@@ -1,4 +1,6 @@
-namespace SignalR.Hosting.Common
+using SignalR.Hubs;
+
+namespace SignalR.Hosting.Common
{
public class Host
{
@@ -41,5 +43,16 @@ public IConfigurationManager Configuration
return DependencyResolver.Resolve<IConfigurationManager>();
}
}
+
+ /// <summary>
+ ///
+ /// </summary>
+ public IHubPipeline HubPipeline
+ {
+ get
+ {
+ return DependencyResolver.Resolve<IHubPipeline>();
+ }
+ }
}
}
View
6 SignalR/ConnectionManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Diagnostics;
using System.Linq;
+using System.Threading.Tasks;
using SignalR.Hubs;
using SignalR.Infrastructure;
@@ -73,13 +74,16 @@ public IHubContext GetHubContext(string hubName)
{
var connection = GetConnection(connectionName: null);
var hubManager = _resolver.Resolve<IHubManager>();
+ var pipelineInvoker = _resolver.Resolve<IHubPipelineInvoker>();
HubDescriptor hubDescriptor = hubManager.EnsureHub(hubName,
_hubResolutionErrorsTotalCounter,
_hubResolutionErrorsPerSecCounter,
_allErrorsTotalCounter,
_allErrorsPerSecCounter);
- return new HubContext(new ClientProxy(connection, hubDescriptor.Name),
+ Func<string, ClientHubInvocation, Task> send = (signal, value) => pipelineInvoker.Send(new HubOutgoingInvokerContext(connection, signal, value));
+
+ return new HubContext(new ClientProxy(send, hubDescriptor.Name),
new GroupManager(connection, hubName));
}
View
35 SignalR/Hubs/ClientHubInvocation.cs
@@ -0,0 +1,35 @@
+using System.Collections.Generic;
+
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public class ClientHubInvocation
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ public string GroupOrConnectionId { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public string Hub { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public string Method { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public object[] Args { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public IDictionary<string, object> State { get; set; }
+ }
+}
View
15 SignalR/Hubs/ClientProxy.cs
@@ -1,16 +1,17 @@
-using System.Dynamic;
+using System;
+using System.Dynamic;
using System.Threading.Tasks;
namespace SignalR.Hubs
{
public class ClientProxy : DynamicObject, IClientProxy
{
- private readonly IConnection _connection;
+ private readonly Func<string, ClientHubInvocation, Task> _send;
private readonly string _hubName;
- public ClientProxy(IConnection connection, string hubName)
+ public ClientProxy(Func<string, ClientHubInvocation, Task> send, string hubName)
{
- _connection = connection;
+ _send = send;
_hubName = hubName;
}
@@ -18,7 +19,7 @@ public ClientProxy(IConnection connection, string hubName)
{
get
{
- return new SignalProxy(_connection, key, _hubName);
+ return new SignalProxy(_send, key, _hubName);
}
}
@@ -36,14 +37,14 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o
public Task Invoke(string method, params object[] args)
{
- var invocation = new
+ var invocation = new ClientHubInvocation
{
Hub = _hubName,
Method = method,
Args = args
};
- return _connection.Send(_hubName, invocation);
+ return _send(_hubName, invocation);
}
}
}
View
147 SignalR/Hubs/HubDispatcher.cs
@@ -19,6 +19,7 @@ public class HubDispatcher : PersistentConnection
private IHubManager _manager;
private IHubRequestParser _requestParser;
private IParameterResolver _binder;
+ private IHubPipelineInvoker _pipelineInvoker;
private readonly List<HubDescriptor> _hubs = new List<HubDescriptor>();
private bool _isDebuggingEnabled;
private PerformanceCounter _allErrorsTotalCounter;
@@ -52,6 +53,7 @@ public override void Initialize(IDependencyResolver resolver)
_manager = resolver.Resolve<IHubManager>();
_binder = resolver.Resolve<IParameterResolver>();
_requestParser = resolver.Resolve<IHubRequestParser>();
+ _pipelineInvoker = resolver.Resolve<IHubPipelineInvoker>();
var counters = resolver.Resolve<IPerformanceCounterWriter>();
_allErrorsTotalCounter = counters.GetCounter(PerformanceCounters.ErrorsAllTotal);
@@ -93,82 +95,144 @@ protected override Task OnReceivedAsync(IRequest request, string connectionId, s
var state = new TrackingDictionary(hubRequest.State);
var hub = CreateHub(request, descriptor, connectionId, state, throwIfFailedToCreate: true);
- Task resultTask;
+ return InvokeHubPipeline(request, connectionId, data, hubRequest, parameterValues, methodDescriptor, state, hub);
+ }
+
+ private Task InvokeHubPipeline(IRequest request, string connectionId, string data, HubRequest hubRequest, IJsonValue[] parameterValues, MethodDescriptor methodDescriptor, TrackingDictionary state, IHub hub)
+ {
+
+ var args = _binder.ResolveMethodParameters(methodDescriptor, parameterValues);
+ var context = new HubInvokerContext(hub, state, methodDescriptor, args);
+
+ // Invoke the pipeline
+ return _pipelineInvoker.Invoke(context)
+ .ContinueWith(task =>
+ {
+ if (task.IsFaulted)
+ {
+ return ProcessResponse(state, null, hubRequest, task.Exception);
+ }
+ else
+ {
+ return ProcessResponse(state, task.Result, hubRequest, null);
+ }
+ })
+ .FastUnwrap();
+
+ }
+
+ public override Task ProcessRequestAsync(HostContext context)
+ {
+ // Generate the proxy
+ if (context.Request.Url.LocalPath.EndsWith("/hubs", StringComparison.OrdinalIgnoreCase))
+ {
+ context.Response.ContentType = "application/x-javascript";
+ return context.Response.EndAsync(_proxyGenerator.GenerateProxy(_url));
+ }
+
+ _isDebuggingEnabled = context.IsDebuggingEnabled();
+
+ return base.ProcessRequestAsync(context);
+ }
+
+ internal static Task Connect(IHub hub)
+ {
+ return ((IConnected)hub).Connect();
+ }
+
+ internal static Task Reconnect(IHub hub, IEnumerable<string> groups)
+ {
+ return ((IConnected)hub).Reconnect(groups);
+ }
+
+ internal static Task Disconnect(IHub hub)
+ {
+ return ((IDisconnect)hub).Disconnect();
+ }
+
+ internal static Task<object> Incoming(IHubIncomingInvokerContext context)
+ {
+ var tcs = new TaskCompletionSource<object>();
try
{
- // Invoke the method
- object result = methodDescriptor.Invoker.Invoke(hub, _binder.ResolveMethodParameters(methodDescriptor, parameterValues));
- Type returnType = result != null ? result.GetType() : methodDescriptor.ReturnType;
+ var result = context.MethodDescriptor.Invoker.Invoke(context.Hub, context.Args);
+ Type returnType = context.MethodDescriptor.ReturnType;
if (typeof(Task).IsAssignableFrom(returnType))
{
var task = (Task)result;
if (!returnType.IsGenericType)
{
- return task.ContinueWith(t => ProcessResponse(state, null, hubRequest, t.Exception))
- .FastUnwrap();
+ task.ContinueWith(tcs);
}
else
{
// Get the <T> in Task<T>
Type resultType = returnType.GetGenericArguments().Single();
- // Get the correct ContinueWith overload
- var continueWith = TaskAsyncHelper.GetContinueWith(task.GetType());
+ Type genericTaskType = typeof(Task<>).MakeGenericType(resultType);
- var taskParameter = Expression.Parameter(continueWith.Type);
- var processResultMethod = typeof(HubDispatcher).GetMethod("ProcessTaskResult", BindingFlags.NonPublic | BindingFlags.Instance).MakeGenericMethod(resultType);
+ // Get the correct ContinueWith overload
+ var parameter = Expression.Parameter(typeof(object));
- var body = Expression.Call(Expression.Constant(this),
- processResultMethod,
- Expression.Constant(state),
- Expression.Constant(hubRequest),
- taskParameter);
+ // TODO: Cache this whole thing
+ var continueWithMethod = typeof(HubDispatcher).GetMethod("ContinueWith", BindingFlags.NonPublic | BindingFlags.Static)
+ .MakeGenericMethod(resultType);
- var lambda = Expression.Lambda(body, taskParameter);
+ Expression body = Expression.Call(continueWithMethod,
+ Expression.Convert(parameter, genericTaskType),
+ Expression.Constant(tcs));
- var call = Expression.Call(Expression.Constant(task, continueWith.Type), continueWith.Method, lambda);
- Func<Task<Task>> continueWithMethod = Expression.Lambda<Func<Task<Task>>>(call).Compile();
- return continueWithMethod.Invoke().FastUnwrap();
+ var continueWithInvoker = Expression.Lambda<Action<object>>(body, parameter).Compile();
+ continueWithInvoker.Invoke(result);
}
}
else
{
- resultTask = ProcessResponse(state, result, hubRequest, null);
+ tcs.TrySetResult(result);
}
}
- catch (TargetInvocationException e)
+ catch (Exception ex)
{
- resultTask = ProcessResponse(state, null, hubRequest, e);
+ tcs.TrySetException(ex);
}
- return resultTask.Then(() => base.OnReceivedAsync(request, connectionId, data))
- .Catch();
+ return tcs.Task;
}
- public override Task ProcessRequestAsync(HostContext context)
+ private static void ContinueWith<T>(Task<T> task, TaskCompletionSource<object> tcs)
{
- // Generate the proxy
- if (context.Request.Url.LocalPath.EndsWith("/hubs", StringComparison.OrdinalIgnoreCase))
+ task.ContinueWith(t =>
{
- context.Response.ContentType = "application/x-javascript";
- return context.Response.EndAsync(_proxyGenerator.GenerateProxy(_url));
- }
-
- _isDebuggingEnabled = context.IsDebuggingEnabled();
+ if (t.IsFaulted)
+ {
+ tcs.TrySetException(t.Exception);
+ }
+ else if (t.IsCanceled)
+ {
+ tcs.TrySetCanceled();
+ }
+ else
+ {
+ tcs.TrySetResult(t.Result);
+ }
+ });
+ }
- return base.ProcessRequestAsync(context);
+ internal static Task Outgoing(IHubOutgoingInvokerContext context)
+ {
+ return context.Connection.Send(context.Signal, context.Invocation);
}
protected override Task OnConnectedAsync(IRequest request, string connectionId)
{
- return ExecuteHubEventAsync<IConnected>(request, connectionId, hub => hub.Connect());
+ return ExecuteHubEventAsync<IConnected>(request, connectionId, hub => _pipelineInvoker.Connect(hub));
}
protected override Task OnReconnectedAsync(IRequest request, IEnumerable<string> groups, string connectionId)
{
- return ExecuteHubEventAsync<IConnected>(request, connectionId, hub => hub.Reconnect(groups));
+ return ExecuteHubEventAsync<IConnected>(request, connectionId, hub => _pipelineInvoker.Reconnect(hub, groups));
}
protected override IEnumerable<string> OnRejoiningGroups(IRequest request, IEnumerable<string> groups, string connectionId)
@@ -188,14 +252,14 @@ protected override IEnumerable<string> OnRejoiningGroups(IRequest request, IEnum
protected override Task OnDisconnectAsync(string connectionId)
{
- return ExecuteHubEventAsync<IDisconnect>(request: null, connectionId: connectionId, action: hub => hub.Disconnect());
+ return ExecuteHubEventAsync<IDisconnect>(request: null, connectionId: connectionId, action: hub => _pipelineInvoker.Disconnect(hub));
}
- private Task ExecuteHubEventAsync<T>(IRequest request, string connectionId, Func<T, Task> action) where T : class
+ private Task ExecuteHubEventAsync<T>(IRequest request, string connectionId, Func<IHub, Task> action) where T : class
{
var operations = GetHubsImplementingInterface(typeof(T))
.Select(hub => CreateHub(request, hub, connectionId))
- .OfType<T>()
+ .Where(hub => hub != null)
.Select(instance => action(instance).Catch().OrEmpty())
.ToList();
@@ -234,9 +298,12 @@ private IHub CreateHub(IRequest request, HubDescriptor descriptor, string connec
if (hub != null)
{
state = state ?? new TrackingDictionary();
+
+ Func<string, ClientHubInvocation, Task> send = (signal, value) => _pipelineInvoker.Send(new HubOutgoingInvokerContext(Connection, signal, value));
+
hub.Context = new HubCallerContext(request, connectionId);
- hub.Caller = new StatefulSignalProxy(Connection, connectionId, descriptor.Name, state);
- hub.Clients = new ClientProxy(Connection, descriptor.Name);
+ hub.Caller = new StatefulSignalProxy(send, connectionId, descriptor.Name, state);
+ hub.Clients = new ClientProxy(send, descriptor.Name);
hub.Groups = new GroupManager(Connection, descriptor.Name);
}
View
41 SignalR/Hubs/Pipeline/HubInvokerContext.cs
@@ -0,0 +1,41 @@
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public class HubInvokerContext : IHubIncomingInvokerContext
+ {
+ public HubInvokerContext(IHub hub, TrackingDictionary state, MethodDescriptor methodDescriptor, object[] args)
+ {
+ Hub = hub;
+ MethodDescriptor = methodDescriptor;
+ Args = args;
+ State = state;
+ }
+
+ public IHub Hub
+ {
+ get;
+ private set;
+ }
+
+ public MethodDescriptor MethodDescriptor
+ {
+ get;
+ private set;
+ }
+
+ public object[] Args
+ {
+ get;
+ private set;
+ }
+
+
+ public TrackingDictionary State
+ {
+ get;
+ private set;
+ }
+ }
+}
View
36 SignalR/Hubs/Pipeline/HubOutgoingInvokerContext.cs
@@ -0,0 +1,36 @@
+namespace SignalR.Hubs
+{
+ public class HubOutgoingInvokerContext : IHubOutgoingInvokerContext
+ {
+ public HubOutgoingInvokerContext(IConnection connection, string signal, ClientHubInvocation invocation)
+ {
+ Connection = connection;
+ Signal = signal;
+ Invocation = invocation;
+ }
+
+ public IConnection Connection
+ {
+ get;
+ private set;
+ }
+
+ public string Group
+ {
+ get;
+ private set;
+ }
+
+ public ClientHubInvocation Invocation
+ {
+ get;
+ private set;
+ }
+
+ public string Signal
+ {
+ get;
+ private set;
+ }
+ }
+}
View
159 SignalR/Hubs/Pipeline/HubPipeline.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SignalR.Hubs
+{
+ public class HubPipeline : IHubPipeline, IHubPipelineInvoker
+ {
+ private readonly Stack<IHubPipelineModule> _modules = new Stack<IHubPipelineModule>();
+
+ private Func<IHubIncomingInvokerContext, Task<object>> _incomingPipeline;
+ private Func<IHub, Task> _connectPipeline;
+ private Func<IHub, IEnumerable<string>, Task> _reconnectPipeline;
+ private Func<IHub, Task> _disconnectPipeline;
+ private Func<IHubOutgoingInvokerContext, Task> _outgoingPipeling;
+
+ public HubPipeline()
+ {
+ // Add one item to the list so we don't have to special case the logic if
+ // there's no builders in the pipeline
+ AddModule(new NoopModule());
+ }
+
+ public IHubPipeline AddModule(IHubPipelineModule builder)
+ {
+ _modules.Push(builder);
+ return this;
+ }
+
+ private void EnsurePipeline()
+ {
+ if (_incomingPipeline == null)
+ {
+ IHubPipelineModule module = _modules.Reverse().Aggregate((a, b) => new ComposedModule(a, b));
+ _incomingPipeline = module.BuildIncoming(HubDispatcher.Incoming);
+ _connectPipeline = module.BuildConnect(HubDispatcher.Connect);
+ _reconnectPipeline = module.BuildReconnect(HubDispatcher.Reconnect);
+ _disconnectPipeline = module.BuildDisconnect(HubDispatcher.Disconnect);
+ _outgoingPipeling = module.BuildOutgoing(HubDispatcher.Outgoing);
+ }
+ }
+
+ public Task<object> Invoke(IHubIncomingInvokerContext context)
+ {
+ EnsurePipeline();
+
+ return _incomingPipeline.Invoke(context);
+ }
+
+ public Task Connect(IHub hub)
+ {
+ EnsurePipeline();
+
+ return _connectPipeline.Invoke(hub);
+ }
+
+ public Task Reconnect(IHub hub, IEnumerable<string> groups)
+ {
+ EnsurePipeline();
+
+ return _reconnectPipeline.Invoke(hub, groups);
+ }
+
+ public Task Disconnect(IHub hub)
+ {
+ EnsurePipeline();
+
+ return _disconnectPipeline.Invoke(hub);
+ }
+
+ public Task Send(IHubOutgoingInvokerContext context)
+ {
+ EnsurePipeline();
+
+ return _outgoingPipeling.Invoke(context);
+ }
+
+ private class NoopModule : IHubPipelineModule
+ {
+ public Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
+ {
+ return invoke;
+ }
+
+ public Func<IHubOutgoingInvokerContext, Task> BuildOutgoing(Func<IHubOutgoingInvokerContext, Task> send)
+ {
+ return send;
+ }
+
+ public Func<IHub, Task> BuildConnect(Func<IHub, Task> connect)
+ {
+ return connect;
+ }
+
+ public Func<IHub, IEnumerable<string>, Task> BuildReconnect(Func<IHub, IEnumerable<string>, Task> reconnect)
+ {
+ return reconnect;
+ }
+
+ public Func<IHub, Task> BuildDisconnect(Func<IHub, Task> disconnect)
+ {
+ return disconnect;
+ }
+ }
+
+ private class ComposedModule : IHubPipelineModule
+ {
+ private readonly IHubPipelineModule _left;
+ private readonly IHubPipelineModule _right;
+
+ public ComposedModule(IHubPipelineModule left, IHubPipelineModule right)
+ {
+ _left = left;
+ _right = right;
+ }
+
+ public Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> callback)
+ {
+ return context =>
+ {
+ return _left.BuildIncoming(_right.BuildIncoming(callback))(context);
+ };
+ }
+
+ public Func<IHub, Task> BuildConnect(Func<IHub, Task> callback)
+ {
+ return hub =>
+ {
+ return _left.BuildConnect(_right.BuildConnect(callback))(hub);
+ };
+ }
+
+ public Func<IHub, IEnumerable<string>, Task> BuildReconnect(Func<IHub, IEnumerable<string>, Task> callback)
+ {
+ return (hub, groups) =>
+ {
+ return _left.BuildReconnect(_right.BuildReconnect(callback))(hub, groups);
+ };
+ }
+
+ public Func<IHub, Task> BuildDisconnect(Func<IHub, Task> callback)
+ {
+ return hub =>
+ {
+ return _left.BuildDisconnect(_right.BuildDisconnect(callback))(hub);
+ };
+ }
+
+ public Func<IHubOutgoingInvokerContext, Task> BuildOutgoing(Func<IHubOutgoingInvokerContext, Task> send)
+ {
+ return context =>
+ {
+ return _left.BuildOutgoing(_right.BuildOutgoing(send))(context);
+ };
+ }
+ }
+ }
+}
View
67 SignalR/Hubs/Pipeline/HubPipelineModule.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace SignalR.Hubs
+{
+ public class HubPipelineModule : IHubPipelineModule
+ {
+ public virtual Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke)
+ {
+ return context =>
+ {
+ OnBeforeInvoke(context);
+ return invoke(context).Then(result =>
+ {
+ OnAfterInvoke(result, context);
+ return result;
+ })
+ .Catch(ex => OnInvokeError(ex));
+ };
+ }
+
+ public virtual Func<IHub, Task> BuildConnect(Func<IHub, Task> connect)
+ {
+ return connect;
+ }
+
+ public virtual Func<IHub, IEnumerable<string>, Task> BuildReconnect(Func<IHub, IEnumerable<string>, Task> reconnect)
+ {
+ return reconnect;
+ }
+
+ public virtual Func<IHub, Task> BuildDisconnect(Func<IHub, Task> disconnect)
+ {
+ return disconnect;
+ }
+
+ public Func<IHubOutgoingInvokerContext, Task> BuildOutgoing(Func<IHubOutgoingInvokerContext, Task> send)
+ {
+ return context =>
+ {
+ OnBeforeOutgoing(context);
+ return send(context);
+ };
+ }
+
+ protected virtual void OnBeforeOutgoing(IHubOutgoingInvokerContext context)
+ {
+
+ }
+
+ protected virtual void OnBeforeInvoke(IHubIncomingInvokerContext context)
+ {
+
+ }
+
+ protected virtual void OnAfterInvoke(object result, IHubIncomingInvokerContext context)
+ {
+
+ }
+
+ protected virtual void OnInvokeError(Exception ex)
+ {
+
+ }
+ }
+}
View
29 SignalR/Hubs/Pipeline/IHubIncomingInvokerContext.cs
@@ -0,0 +1,29 @@
+
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public interface IHubIncomingInvokerContext
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ IHub Hub { get; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ MethodDescriptor MethodDescriptor { get; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ object[] Args { get; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ TrackingDictionary State { get; }
+ }
+}
View
23 SignalR/Hubs/Pipeline/IHubOutgoingInvokerContext.cs
@@ -0,0 +1,23 @@
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public interface IHubOutgoingInvokerContext
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ IConnection Connection { get; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ ClientHubInvocation Invocation { get; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ string Signal { get; }
+ }
+}
View
15 SignalR/Hubs/Pipeline/IHubPipeline.cs
@@ -0,0 +1,15 @@
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public interface IHubPipeline
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="module"></param>
+ /// <returns></returns>
+ IHubPipeline AddModule(IHubPipelineModule module);
+ }
+}
View
47 SignalR/Hubs/Pipeline/IHubPipelineInvoker.cs
@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public interface IHubPipelineInvoker
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns></returns>
+ Task<object> Invoke(IHubIncomingInvokerContext context);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns></returns>
+ Task Send(IHubOutgoingInvokerContext context);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="hub"></param>
+ /// <returns></returns>
+ Task Connect(IHub hub);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="hub"></param>
+ /// <param name="groups"></param>
+ /// <returns></returns>
+ Task Reconnect(IHub hub, IEnumerable<string> groups);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="hub"></param>
+ /// <returns></returns>
+ Task Disconnect(IHub hub);
+ }
+}
View
48 SignalR/Hubs/Pipeline/IHubPipelineModule.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace SignalR.Hubs
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public interface IHubPipelineModule
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="invoke"></param>
+ /// <returns></returns>
+ Func<IHubIncomingInvokerContext, Task<object>> BuildIncoming(Func<IHubIncomingInvokerContext, Task<object>> invoke);
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="send"></param>
+ /// <returns></returns>
+ Func<IHubOutgoingInvokerContext, Task> BuildOutgoing(Func<IHubOutgoingInvokerContext, Task> send);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="connect"></param>
+ /// <returns></returns>
+ Func<IHub, Task> BuildConnect(Func<IHub, Task> connect);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="reconnect"></param>
+ /// <returns></returns>
+ Func<IHub, IEnumerable<string>, Task> BuildReconnect(Func<IHub, IEnumerable<string>, Task> reconnect);
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="disconnect"></param>
+ /// <returns></returns>
+ Func<IHub, Task> BuildDisconnect(Func<IHub, Task> disconnect);
+ }
+}
View
18 SignalR/Hubs/SignalProxy.cs
@@ -1,17 +1,18 @@
-using System.Dynamic;
+using System;
+using System.Dynamic;
using System.Threading.Tasks;
namespace SignalR.Hubs
{
public class SignalProxy : DynamicObject, IClientProxy
{
- protected readonly IConnection _connection;
+ protected readonly Func<string, ClientHubInvocation, Task> _send;
protected readonly string _signal;
protected readonly string _hubName;
- public SignalProxy(IConnection connection, string signal, string hubName)
+ public SignalProxy(Func<string, ClientHubInvocation, Task> send, string signal, string hubName)
{
- _connection = connection;
+ _send = send;
_signal = signal;
_hubName = hubName;
}
@@ -34,16 +35,17 @@ public Task Invoke(string method, params object[] args)
string signal = _hubName + "." + _signal;
- return _connection.Send(signal, invocation);
+ return _send.Invoke(signal, invocation);
}
- protected virtual object GetInvocationData(string method, object[] args)
+ protected virtual ClientHubInvocation GetInvocationData(string method, object[] args)
{
- return new
+ return new ClientHubInvocation
{
Hub = _hubName,
Method = method,
- Args = args
+ Args = args,
+ GroupOrConnectionId = _signal
};
}
}
View
15 SignalR/Hubs/StatefulSignalProxy.cs
@@ -1,4 +1,6 @@
-using System.Dynamic;
+using System;
+using System.Dynamic;
+using System.Threading.Tasks;
namespace SignalR.Hubs
{
@@ -6,8 +8,8 @@ public class StatefulSignalProxy : SignalProxy
{
private readonly TrackingDictionary _state;
- public StatefulSignalProxy(IConnection connection, string signal, string hubName, TrackingDictionary state)
- : base(connection, signal, hubName)
+ public StatefulSignalProxy(Func<string, ClientHubInvocation, Task> send, string signal, string hubName, TrackingDictionary state)
+ : base(send, signal, hubName)
{
_state = state;
}
@@ -23,14 +25,15 @@ public override bool TryGetMember(GetMemberBinder binder, out object result)
result = _state[binder.Name];
return true;
}
-
- protected override object GetInvocationData(string method, object[] args)
+
+ protected override ClientHubInvocation GetInvocationData(string method, object[] args)
{
- return new
+ return new ClientHubInvocation
{
Hub = _hubName,
Method = method,
Args = args,
+ GroupOrConnectionId = _signal,
State = _state.GetChanges()
};
}
View
5 SignalR/Infrastructure/DefaultDependencyResolver.cs
@@ -83,6 +83,11 @@ private void RegisterHubExtensions()
var assemblyLocator = new Lazy<DefaultAssemblyLocator>(() => new DefaultAssemblyLocator());
Register(typeof(IAssemblyLocator), () => assemblyLocator.Value);
+
+ // Setup the default hub pipeline
+ var dispatcher = new Lazy<IHubPipeline>(() => new HubPipeline());
+ Register(typeof(IHubPipeline), () => dispatcher.Value);
+ Register(typeof(IHubPipelineInvoker), () => dispatcher.Value);
}
public virtual object GetService(Type serviceType)
View
10 SignalR/SignalR.csproj
@@ -63,7 +63,13 @@
<Compile Include="Hosting\IWebSocket.cs" />
<Compile Include="Hosting\ResponseExtensions.cs" />
<Compile Include="Hubs\NullClientProxy.cs" />
+ <Compile Include="Hubs\Pipeline\HubOutgoingInvokerContext.cs" />
+ <Compile Include="Hubs\ClientHubInvocation.cs" />
<Compile Include="Hubs\HubContext.cs" />
+ <Compile Include="Hubs\Pipeline\HubPipelineModule.cs" />
+ <Compile Include="Hubs\Pipeline\IHubOutgoingInvokerContext.cs" />
+ <Compile Include="Hubs\Pipeline\IHubPipelineInvoker.cs" />
+ <Compile Include="Hubs\Pipeline\HubInvokerContext.cs" />
<Compile Include="Hubs\HubRequest.cs" />
<Compile Include="Hubs\HubRequestParser.cs" />
<Compile Include="Hubs\HubResponse.cs" />
@@ -72,6 +78,10 @@
<Compile Include="Hubs\IHubRequestParser.cs" />
<Compile Include="Hubs\Lookup\Descriptors\Descriptor.cs" />
<Compile Include="IAckHandler.cs" />
+ <Compile Include="Hubs\Pipeline\HubPipeline.cs" />
+ <Compile Include="Hubs\Pipeline\IHubIncomingInvokerContext.cs" />
+ <Compile Include="Hubs\Pipeline\IHubPipeline.cs" />
+ <Compile Include="Hubs\Pipeline\IHubPipelineModule.cs" />
<Compile Include="Infrastructure\DisposableAction.cs" />
<Compile Include="Infrastructure\IPerformanceCounterWriter.cs" />
<Compile Include="Infrastructure\PerformanceCounterExtensions.cs" />
View
19 samples/SignalR.Hosting.AspNet.Samples/Global.asax.cs
@@ -1,8 +1,11 @@
using System;
+using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Threading;
+using System.Threading.Tasks;
using System.Web.Routing;
+using SignalR.Hubs;
using SignalR.Samples.Hubs.DemoHub;
using SignalR.Samples.Raw;
using SignalR.Samples.Streaming;
@@ -15,6 +18,7 @@ public class Global : System.Web.HttpApplication
protected void Application_Start(object sender, EventArgs e)
{
//GlobalHost.DependencyResolver.UseSqlServer(ConfigurationManager.ConnectionStrings["SignalRSamples"].ConnectionString);
+ GlobalHost.HubPipeline.AddModule(new SamplePipelineModule());
ThreadPool.QueueUserWorkItem(_ =>
{
@@ -43,5 +47,20 @@ protected void Application_Start(object sender, EventArgs e)
RouteTable.Routes.MapConnection<Raw>("raw", "raw/{*operation}");
RouteTable.Routes.MapConnection<Streaming>("streaming", "streaming/{*operation}");
}
+
+ private class SamplePipelineModule : HubPipelineModule
+ {
+ protected override void OnBeforeInvoke(IHubIncomingInvokerContext context)
+ {
+ Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name);
+ base.OnBeforeInvoke(context);
+ }
+
+ protected override void OnBeforeOutgoing(IHubOutgoingInvokerContext context)
+ {
+ Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub);
+ base.OnBeforeOutgoing(context);
+ }
+ }
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.