User Management

User objects are not defined internally in the Rubris libraries. Instead the calling library is responsible for defining its own User implementation.

The User object is set by declaring a com.rubris.api.UserClientHandler. e.g:

  @Token(name = "X-AUTH-SSO", type = Type.Cookie)
  class UserConnectionHandler implements UserClientHandler {
    @Override
    public void onNewClient(SocketAddress remoteAddress,  
        final Map<String, TokenData> tokens,ClientHandle handle) {
       executor.submit(new Runnable(){
           public void run(){
             handle.accept(new User("somename"));
           }
       });
    }
  }

The onNewClient is called once per Engine-IO client creation in the browser. Unlike the connection level handler this call is 1-to-1 with the Engine-IO instances (which are in effect distinct Clients in the browser)

So setting up 2 Engine-IO instances in the same browser window will result in 2 callbacks.

TokenData is used to extract either headers, cookies or URL parameters from the result as the server callback does not have access to a full HTTP object. To specify what type and set of data you want to be parsed from the request set the relevant @Token annotations on the implementing class.

Note: all HTTP headers will be converted to lower-case keys. Cookie and URL params will stay as is.

The UserClientHandler is used to accept or deny the new Client and a User object can be supplied in the accept call. the call is asynchronous and ALL processing in this method should be spun off into another thread unless it is VERY simple. This makes it easy to do longer running token verification checks etc without needing to maintain local caches etc.

The User object is provided to all other api callbacks that declare a User.

The User object

Rubris has no internal User Object. It simply defines a simple interface (com.rubris.api.User) that all instantiated objects must implement as a minimum.

public interface User {

  public String getName();

  public Object getCredentials();

}

This object is constructed by your own code at the point a new client is created and returned back to the library code for use in callbacks interfaces where a User object provides identity (e.g. the PrivateSubscriptionHandler).

The UserClientHandle

As part of the accept call a UserClientHandle is returned which enables the callee to query the status of the client instance that has just been accepted.

  public interface ClientHandle {

    public UserClientHandle accept(User user);

    public void deny();
  }

The UserClientHandle is the main mechanism of interacting with the Client instance. It enables the query of the time of last inbound data, outbound write, and the state of the client in so far as the server is aware of it. By the nature of the protocols involved there is a delay as to consistency with the state of the client.

For a TCP remote client it is necessary to trade off sending data to ensure the client is active, vs not swamping the connection with chatty messages or wasting too many cycles on keeping the information up-to-date with very frequent ping messages.

public interface UserClientHandle {


  /**
   * Closes the  the Session instance for the User. This removes the Client from
   * the server and calls close on any currently connected sockets and
   * subscriptions.
   * Any further connection attempt by the client will result in an error being
   * returned.
   *
   * @return true if the client was closed or false if the client does not exist 
   * any more on the server.
   */
  public boolean destroy();



  /**
   * Gets the User object (if any) associated with this client.
   * @return null if no User or the User object supplied to the 
   * {@link ClientHandle}
   */
  public User getUser();



  /**
   * Represents the last data sent by the client either a normal poll, a ping
   *  packet or a post.
   * The ping is not distinguished individually from other data as we are 
   * actually interested in the client being alive and any inbound data 
   * represents this.
   * This is intended to discourage us focusing on protocol specific data such as
   * pings and allows us to have a simpler concept of inbound data irrespective
   * of  the information.
   */
  public long getLastInbound();



  /**
   * Represents the last successful process of an outbound response.
   *  This includes pongs, http GET and POST and websocket outbound packets.
   */
  public long getLastOutbound();



  /**
   * The client identifier.
   *
   * @return null if the client is no longer valid or the identifier used
   * to represent the client session token.
   */
  public String getClientIdentifier();



  /**
   * Check if the Client is closed. Either
   *  1. No longer exists on the server
   *  2. the client is marked as closed but has not yet been removed as
   *       part of the cleanup.
   */
  public boolean isClosed();

Receiving Client destroyed notifications

Although the UserClientHandle can be used to check the status of the client and the times of data, it is not necessary to use it work out if the Client is still alive. In fact constantly iterating and checking isClosed is not very much use - and the mechanism is only there in case one wants to short circuit activity for a client that we have not yet processed the destroy callback for as shown below.

This should be done using the:

/**
 * Called on the destruction of a Client instance. In this sense the destruction 
 * is a close of the Client on the server.
 *
 * The definition of when a client is destroyed is when the Client instance has
 * no  inbound activity for a time exceeding the
 *  configured {@link ModuleConfig} clientTimeout, or the client is destroyed 
 * programmatically via the {@link UserClientHandle}
 *
 * The clientIdentifier enables the ability to distinguish which user client
 * instance  (if there is more than one) has terminated.
 *
 * @author steve
 *
 */
public interface UserClientDestroyHandler {


  /**
   * Call back when a Client instance is destroyed.
   * @param user - the User object for the client (if any)
   * @param clientIdentifier - the  token identifying the client instance.
   */
  public void onDestroyClient(User user, String clientIdentifier);

}

The callback will receive updates when a Client instance is destroyed on the server. This may be some time after the client has been closed or destroyed (depending on the client timeout values specified below).

Configuring Client timeout values

There are two values that determine the frequency of checking the state of the Client and the amount of time we are willing to tolerate no data arriving from the client.

    server.getConfig().setTimeoutCheck(60000);
    server.getConfig().setClientTimeout(60000);

The timeoutCheck is the frequency of checking and the clientTimeout is the amount of time beyond which the client be destroyed. One may decide to set a very low value on the check and timeout to get immediate feedback, but often this is an anti-pattern. One must take into account how many users we will be checking and what other activity is taking place in the VM. As the check will use clock cycles and often be better spent doing actual work in processing messages.

Activity on the Client is defined as inbound data. The reason for this is that the Engine-IO ping mechanism creates a constant baseline of inbound packets at the frequency defined in the EngineIOConfig.pingTimeout and EngineIOCOnfig.pingInterval. In many ways these timings are complementary to the server. They are solely used by the client to determine the server responsiveness, while the server settings are used to determine the Client activity.

For the client the ping interval sets a ping packet at that time frequency and the timeout is the amount of time to wait for a reply. Failure for the server to reply with a pong packet within the pingTimeout will cause the client to close itself and send a close frame to the server. The server does not take these values into account (apart from the fact that a ping will update the inbound activity time for the client).

The server does NOT send ping packets to the client as this is generally wasteful and pointless, and roundtrip times for the client performance are generally of little use. The activity down from the Client is sufficient to determine aliveness and slow clients will result in blocked sockets or large queue build ups that also give backpressure behaviour without trying to have 2 way acks.

Terminating Users on the Server

On the server side it is sometimes necessary to terminate the user’s connections.

This can be accomplished by using destroy method on the UserClientHandle as shown above.

On termination all the subscriptions and topic handles related to the user will also be unsubscribed from.

Terminating via JMX

Alternatively to implementing a UserHandler an inbuilt remove method is available via JMX using the

com.rubris.io/client/clientManager->Operations->removeclient(String clientIdentifier)

JMX operation.