// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information. using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Microsoft.AspNet.SignalR.Hubs { /// /// Common base class to simplify the implementation of IHubPipelineModules. /// A module can intercept and customize various stages of hub processing such as connecting, reconnecting, disconnecting, /// invoking server-side hub methods, invoking client-side hub methods, authorizing hub clients and rejoining hub groups. /// A module can be activated by calling . /// The combined modules added to the are invoked via the /// interface. /// public abstract class HubPipelineModule : IHubPipelineModule { /// /// Wraps a function that invokes a server-side hub method. Even if a client has not been authorized to connect /// to a hub, it will still be authorized to invoke server-side methods on that hub unless it is prevented in /// by not executing the invoke parameter. /// /// A function that invokes a server-side hub method. /// A wrapped function that invokes a server-side hub method. public virtual Func> BuildIncoming(Func> invoke) { return context => { if (OnBeforeIncoming(context)) { return invoke(context).OrEmpty() .Then(result => OnAfterIncoming(result, context)) .Catch(ex => OnIncomingError(ex, context)); } return TaskAsyncHelper.FromResult(null); }; } /// /// Wraps a function that is called when a client connects to the for each /// the client connects to. By default, this results in the 's /// OnConnected method being invoked. /// /// A function to be called when a client connects to a hub. /// A wrapped function to be called when a client connects to a hub. public virtual Func BuildConnect(Func connect) { return hub => { if (OnBeforeConnect(hub)) { return connect(hub).OrEmpty().Then(h => OnAfterConnect(h), hub); } return TaskAsyncHelper.Empty; }; } /// /// Wraps a function that is called when a client reconnects to the for each /// the client connects to. By default, this results in the 's /// OnReconnected method being invoked. /// /// A function to be called when a client reconnects to a hub. /// A wrapped function to be called when a client reconnects to a hub. public virtual Func BuildReconnect(Func reconnect) { return (hub) => { if (OnBeforeReconnect(hub)) { return reconnect(hub).OrEmpty().Then(h => OnAfterReconnect(h), hub); } return TaskAsyncHelper.Empty; }; } /// /// Wraps a function that is called when a client disconnects from the for each /// the client was connected to. By default, this results in the 's /// OnDisconnected method being invoked. /// /// A function to be called when a client disconnects from a hub. /// A wrapped function to be called when a client disconnects from a hub. public virtual Func BuildDisconnect(Func disconnect) { return hub => { if (OnBeforeDisconnect(hub)) { return disconnect(hub).OrEmpty().Then(h => OnAfterDisconnect(h), hub); } return TaskAsyncHelper.Empty; }; } /// /// Wraps a function to be called before a client subscribes to signals belonging to the hub described by the /// . By default, the will look for attributes on the /// to help determine if the client is authorized to subscribe to method invocations for the /// described hub. /// The function returns true if the client is authorized to subscribe to client-side hub method /// invocations; false, otherwise. /// /// /// A function that dictates whether or not the client is authorized to connect to the described Hub. /// /// /// A wrapped function that dictates whether or not the client is authorized to connect to the described Hub. /// public virtual Func BuildAuthorizeConnect(Func authorizeConnect) { return (hubDescriptor, request) => { if (OnBeforeAuthorizeConnect(hubDescriptor, request)) { return authorizeConnect(hubDescriptor, request); } return false; }; } /// /// Wraps a function that determines which of the groups belonging to the hub described by the /// the client should be allowed to rejoin. /// By default, clients will rejoin all the groups they were in prior to reconnecting. /// /// A function that determines which groups the client should be allowed to rejoin. /// A wrapped function that determines which groups the client should be allowed to rejoin. public virtual Func, IList> BuildRejoiningGroups(Func, IList> rejoiningGroups) { return rejoiningGroups; } /// /// Wraps a function that invokes a client-side hub method. /// /// A function that invokes a client-side hub method. /// A wrapped function that invokes a client-side hub method. public virtual Func BuildOutgoing(Func send) { return context => { if (OnBeforeOutgoing(context)) { return send(context).OrEmpty().Then(ctx => OnAfterOutgoing(ctx), context); } return TaskAsyncHelper.Empty; }; } /// /// This method is called before the AuthorizeConnect components of any modules added later to the /// are executed. If this returns false, then those later-added modules will not run and the client will not be allowed /// to subscribe to client-side invocations of methods belonging to the hub defined by the . /// /// A description of the hub the client is trying to subscribe to. /// The connect request of the client trying to subscribe to the hub. /// true, if the client is authorized to connect to the hub, false otherwise. protected virtual bool OnBeforeAuthorizeConnect(HubDescriptor hubDescriptor, IRequest request) { return true; } /// /// This method is called before the connect components of any modules added later to the are /// executed. If this returns false, then those later-added modules and the method will /// not be run. /// /// The hub the client has connected to. /// /// true, if the connect components of later added modules and the method should be executed; /// false, otherwise. /// protected virtual bool OnBeforeConnect(IHub hub) { return true; } /// /// This method is called after the connect components of any modules added later to the are /// executed and after is executed, if at all. /// /// The hub the client has connected to. protected virtual void OnAfterConnect(IHub hub) { } /// /// This method is called before the reconnect components of any modules added later to the are /// executed. If this returns false, then those later-added modules and the method will /// not be run. /// /// The hub the client has reconnected to. /// /// true, if the reconnect components of later added modules and the method should be executed; /// false, otherwise. /// protected virtual bool OnBeforeReconnect(IHub hub) { return true; } /// /// This method is called after the reconnect components of any modules added later to the are /// executed and after is executed, if at all. /// /// The hub the client has reconnected to. protected virtual void OnAfterReconnect(IHub hub) { } /// /// This method is called before the outgoing components of any modules added later to the are /// executed. If this returns false, then those later-added modules and the client-side hub method invocation(s) will not /// be executed. /// /// A description of the client-side hub method invocation. /// /// true, if the outgoing components of later added modules and the client-side hub method invocation(s) should be executed; /// false, otherwise. /// protected virtual bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) { return true; } /// /// This method is called after the outgoing components of any modules added later to the are /// executed. This does not mean that all the clients have received the hub method invocation, but it does indicate indicate /// a hub invocation message has successfully been published to a message bus. /// /// A description of the client-side hub method invocation. protected virtual void OnAfterOutgoing(IHubOutgoingInvokerContext context) { } /// /// This method is called before the disconnect components of any modules added later to the are /// executed. If this returns false, then those later-added modules and the method will /// not be run. /// /// The hub the client has disconnected from. /// /// true, if the disconnect components of later added modules and the method should be executed; /// false, otherwise. /// protected virtual bool OnBeforeDisconnect(IHub hub) { return true; } /// /// This method is called after the disconnect components of any modules added later to the are /// executed and after is executed, if at all. /// /// The hub the client has disconnected from. protected virtual void OnAfterDisconnect(IHub hub) { } /// /// This method is called before the incoming components of any modules added later to the are /// executed. If this returns false, then those later-added modules and the server-side hub method invocation will not /// be executed. Even if a client has not been authorized to connect to a hub, it will still be authorized to invoke /// server-side methods on that hub unless it is prevented in by not /// executing the invoke parameter or prevented in by returning false. /// /// A description of the server-side hub method invocation. /// /// true, if the incoming components of later added modules and the server-side hub method invocation should be executed; /// false, otherwise. /// protected virtual bool OnBeforeIncoming(IHubIncomingInvokerContext context) { return true; } /// /// This method is called after the incoming components of any modules added later to the /// and the server-side hub method have completed execution. /// /// The return value of the server-side hub method /// A description of the server-side hub method invocation. /// The possibly new or updated return value of the server-side hub method protected virtual object OnAfterIncoming(object result, IHubIncomingInvokerContext context) { return result; } /// /// This is called when an uncaught exception is thrown by a server-side hub method or the incoming component of a /// module added later to the . Observing the exception using this method will not prevent /// it from bubbling up to other modules. /// /// The exception that was thrown during the server-side invocation. /// A description of the server-side hub method invocation. protected virtual void OnIncomingError(Exception ex, IHubIncomingInvokerContext context) { } } }