WebSocketListener Terminal Server

vtortola edited this page Sep 4, 2014 · 3 revisions

WebSocketListener Terminal Server source code

This example application is a terminal server. There is a web application and a console application. The console application acts as backend for the web application. The web application can create command sessions with the backend through the WebSocket connection.

The application is structured in the following way:

  • A web Single Page Application.

  • Is based on AngularJS.

  • Communicates with the backend using WebSockets.

  • Uses ng-terminal-emulator to interact with the terminals.

  • A console application that acts as backend.

  • It is based on MassTransit.

  • Uses WebSocketListener as communication endpoint.

  • Uses cmd.exe processes to create command terminal sessions.

  • Uses System.Management.Automation to create PowerShell terminal sessions.

The backend application, consists in the followin parts:

  • Events (IConnectionEvent): Objects that represents data being pushed to the frontend.
  • Requests (IConnectionRequest): Objects that represents request being done by the frontend.
  • Handlers (IRequestHandler): Objects that subscribe for "Requests" and may generate "Events".
  • Clis (ICli): Objects that represents command line interpreter sessions.
  • Cli Factories (ICliFactory): Objects able of creating a particular type of Cli session (ie: the abstract factory pattern).
  • Serializer (ISerializer): Object responsible of serializing "Events" and deserializing "Requests".
  • Message bus (IMessageBus): Object responsible of message delivery. It contains the WebSocketListener.
  • User sessions (UserSession): Objects that represents an actual user connection.

Basically, the "Handlers" subscribes in the "Message bus" for one or more "Requests". When those "Requests" happens, they perform actions like creating/closing sessions, creating/closing terminal sessions, etc... Both, the terminal sessions and the handlers themselves can generate "Events".

The "Message bus" object, is a WebSocket server, that gets "Request" objects and publishes them to the message queue, and it is also subscribed to "Events" from the queue and dispatch them to the appropriate WebSocket connections. Each "Request" contains the ID of the connection that generated it, and each "Event" contains a connection ID as destination.

For each WebSocket connection, a WebSocketHandler is created. This object contains the logic of subscription and event forwarding.

Sending events from the queue to the websocket:

unsubs.Add(_queue.SubscribeHandler<IConnectionEvent>(msg =>
{
    lock (_ws)
    {
        using (var wsmsg = _ws.CreateMessageWriter(WebSocketMessageType.Text))
            _serializer.Serialize(msg, wsmsg);
    }
}, con => _ws.IsConnected && con.ConnectionId == connectionId));

Receiving requests from the websocket to the queue:

var msg = await _ws.ReadMessageAsync(_cancellation).ConfigureAwait(false);
if (msg != null)
{
    Type type;
    var queueRequest = _serializer.Deserialize(msg, out type);
    queueRequest.ConnectionId = connectionId;
    _queue.Publish(queueRequest, type);
}

The application logic is totally decoupled from the WebSocket logic, only the WebSocketQueueServer ( the implementation of IMessageBus) and WebSocketHandler know about such WebSocketListener.

The application is taking advantage of the custom HTTP negotiation to provide a cookie during the connection process, so further requests can be identified as the same user.