Fleck.Extended

Why?

When I started to consume Fleck it just lacked some of the features (which is also what I like about Fleck, it’s simple and bare bone and easy to get started with) I wanted and I also wasn’t sure I would go with Fleck, so I created a small layer sitting on top of Fleck.

Distributed via NuGet

First, install it using the NuGet-package:

install-package Fleck.Extended

Now, create a socket server by creating an instance of DefaultSocketServer:

var location = "ws://my.awesome-domain.com:8181/cool
ISocketServer server = new DefaultSocketServer(location);

ILogger

One difference from Fleck is that you can also provide an implementation of ILogger which is a simple definition/contract distributed via this NuGet-package.

var location = "ws://my.awesome-domain.com:8181/cool
ISocketServer server = new DefaultSocketServer(location, myLogger);

If you provide an ILogger it will hook into Fleck’s FleckLog and if you don’t provide one, it will disable the log. Also, if you provide an ILogger it will check if you have compiled in DEBUG or not. If DEBUG it will set FleckLog.Level = Debug; if not compiled with DEBUG, it will set it to Info.

ConnectionGuard

Another extension provided is the ability to define a simple server.ConnectionGuard : Func<IConnectionInfo, bool> which should return true if the connection is allowed.

server.ConnectionGuard = cnInfo =>
{
    var isAllowed = settings.AllowedOrigins.Contains(cnInfo.Origin);
    if(!isAllowed)
        logger.Info("Connections from Origin '{0}', not allowed!", cnInfo.Origin);

    return isAllowed;
};

IServerEvents

Each command executed from the connected clients will result in one of the IServerEvents-events to be invoked. Available options are:

public interface IServerEvents 
{
    Action<ISessionEvent> OnSessionStarted { set; }
    Action<ISessionEvent> OnSessionEnded { set; }
    Action<ISessionFailedEvent> OnSessionFailed { set; }
    Action<ISessionAction> OnSessionAction { set; }
}

If successful and allowed action is invoked; the OnSessionAction will be invoked.

ISessionActionDispatcher

In the OnSessionAction you could make use of an implementation of:

ISessionActionDispatcher.Dispatch(ISessionAction)

By default, there is a RoutableSessionActionDispatcher that lets you register routes via:

void RegisterRoute(Func<ISessionAction,bool> predicate, Action<ISessionAction> handler)

An example could be:

dispatcher.RegisterRoute(a => a.Verb == "cmd", OnCommandAction);
dispatcher.RegisterRoute(a => a.Verb == "query", OnQueryAction);

ISessionAction

The ISessionAction is created by an ISessionActionFactory that you can swap out on the server, server.SessionActionFactory:

public interface ISessionAction
{
    ISocketSession Session { get; }
    string Verb { get; }
    string Name { get; }
    object Data { get; }
}

The default SessionActionFactory, supports textual messages and the message format should be:

verb:\\name\data

So I could pass something like:

cmd:\\BeginWorkOrder\{id: 1, userid: 2}

In our configuration (see above) this will be dispatched to our registrered OnCommandAction-handler, in which you could make use of the server’s IMessageSerializer, accessible via server.Serializer:

protected virtual void OnCommandAction(ISessionAction a)
{
    var messageType = GetMessageType(a.Name);
    var cmd = (ICommand)server.Serializer.Deserialize(messageType, a.Data);
    ...
    ...
}

ISocketSessionCredentials

Another extension is that ISession which represents a client connection, contains ISessionCredentials and Authenticate and Deauthenticate which you could make use of in your command-handlers. The credentials interface looks like this:

public interface ISocketSessionCredentials 
    : IEquatable<ISocketSessionCredentials>
{
    Guid Id { get; }
    string Identity { get; }
    bool IsAdmin { get; }
    IEnumerable<string> Groups { get; }
}

this could be used like this:

protected virtual void OnCommandAction(ISessionAction a)
{
    var messageType = GetMessageType(a.Name);
    var cmd = (ICommand)server.Serializer.Deserialize(messageType, a.Data);
    ...
    ...
    if (cmd is ISecuredCommand)
    {
        if (!a.Session.IsAuthenticated)
        {
            a.Session.RespondWith(new SecuredCommandNotAuthenticated { SessionId = a.Session.Id });
            return;
        }
        if (cmd is IAdminCommand && !a.Session.Credentials.IsAdmin)
        {
            a.Session.RespondWith(new CommandNotAllowed { SessionId = a.Session.Id });
            return;
        }
    }
}

All that is left is to start the server:

server.Start();

Client side

Just use a web-socket connection and send something matching the format: verb:\\name\data.

If it helps you, use it. If not, don’t.

//Daniel

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s