Initialize WebSocket protocol and handlers
Closed this issue · 0 comments
The foundation of the WebSocket protocol and the handler classes should be developed in this task. Here comes a short guide on the implementation.
Message Format
The message format of our custom protocol over WebSocket is pretty simple. Currently, JSON is used to transfer data, but switching to another representation such as Protocol Buffers should be easy.
All messages must have the following format:
{
"action": String,
"arguments:" {
...
}
}
-
Action is like an HTTP method but much more specific. For example the value of the action field of authentication messages would be AUTH. Action must not contain whitespaces and must be uppercased, words should be separated by underscores.
Good:- AUTH
- SUBSCIRBE
- NEW_CODE
Bad:
- auth
- new code
-
Arguments is an arbitrary object that contains the body of the message. There are no restrictions on the contents of this object.
Example message:
{
"action": "AUTH",
"arguments": {
"username": "battila7",
"password": "ihavenopassword"
}
}
WebSocketDispatcher
A new class named WebSocketDispatcher
should be created in the io.infectnet.server.controller.websocket
package replacing the WebSocketController
class. This class should be annotated using the same annotations as the WebSocketController
.
WebSocketDispatcher
will implement the Front Controller pattern. All WebSocket messages will be received by this class and will be dispatched to handlers based on the action of the message.
The class should provide methods to register handlers. The following methods should be provided:
public void registerOnConnect(OnConnectHandler handler);
public void registerOnClose(OnCloseHandler handler);
public void registerOnMessage(Action action, OnMessageHandler handler);
Of course, the methods and handlers should be named differently, but the idea is something like this. The handler types should be functional interfaces so method references can be passed as handlers. If handlers are registered as onConnect and onClose handlers, they will be called sequentially. On the other hand, only one handler can be registered per action. registerOnMessage()
should throw an unchecked exception if there's an attempt to register multiple handlers to the same action.
The setup of the WebSocketDispatcher
(registering the handlers) can be done in the ControllerModule
or a completely new module. Then the WebSocketDispatcher
instance should be injected into ApplicationStarter
and registered using the Spark.webSocket(String path, Object handler) method. I had to put emphasis on this method because it is undocumented, although I checked the source code and it should work fine. This way we can pass an instance to the Spark instead of just a Class<?>
.
WebSocketAuthService
This service should expose methods that make it possible for a user to login and disconnect. Also it should store the list of currently logged in users (along with their Session
s) and answer whether a user is logged in or not. Therefore the service should know about Session
s and Remote
s. Example methods to be taken as guidelines:
public User authenticate(Session session, String username, String password) throws AuthenticationFailedException;
Authenticates the specified Session
. Upon success the corresponding User
object is returned and also it is stored in the service's authenticated sessions map.
public void disconnect(Session session);
Removes the specified Session
from the authenticated sessions map.
public Optional<User> isAuthenticated(Session session);
Returns the User
associated with the specified Session
if it's authenticated. An empty Optional
is returned otherwise.
Note, that it is not required to implement these methods as-is. Just use this as a guide towards a good implementation.
Also it's currently unclear, what kind of User
class to use, and whether this class should incorporate the corresponding Session
or not. In my opinion, it's not a good idea to include the Session
in the User
object.
Instead, the Service should provide a
public Optional<Session> getAuthenticatedSessionFor(User user)
method to translate User
s into Session
s. You get the idea.
Please don't forget to use ConcurrentHashMap
for the session-user mapping.
WebSocketAuthController
This controller class should register a handler to the onClose event, and the AUTH action (it can be named differently). This handlers should call the appropriate methods of the WebSocketAuthService
class.
WebSocketGameController
This is the controller that will map handlers to most of the actions. In all handlers, it should be checked if the current Session
is authenticated. WebSocketGameController
is going to talk with the mediator class placed between the server and the actual Game.
Takeaway
It is not required to implement everything here as-is, letter-to-letter. Instead implement it the way you feel it, with more suitable names, and so on. Use this as your guide.
The #5 issue is going to be implemented here just to see how this stuff works out. But the WebSocketGameController
can be a completely empty placeholder class.