Socket overview
Updated at 1709804456000EzyPlatform utilizes ezyfox-server for the socket component, which provides a comprehensive set of features similar to ezyfox server, including:
- Zones
- Plugins
- Apps
Some default zones correspond to files in ezyplatform:
- admin:
socket/settings/zones/admin-zone-settings.xml
- Specifically designed for apps and plugins serving administrators. - chat:
socket/settings/zones/chat-zone-settings.xml
- Specifically designed for apps and plugins serving chat functionality. - game:
socket/settings/zones/game-zone-settings.xml
- Specifically designed for apps and plugins serving gaming. - user:
socket/settings/zones/user-zone-settings.xml
- Specifically designed for apps and plugins serving user-related features.
Authentication
By default, users and administrators using the socket are authenticated through the SocketLoginController
class. This class relies on the access token of users and administrators for authentication. If the token is invalid and does not allow anonymous users, access will be denied.
You can also implement your custom authentication class by inheriting from the SocketLoginController
class, for example:
@EzyEventHandler(USER_LOGIN) public class UserLoginController extends SocketLoginController { private final SocketMessageService messageService; private final SocketAnonymousUserService anonymousUserService; public UserLoginController( SocketAdminService adminService, SocketUserService userService, SocketMessageService messageService, SocketAnonymousUserService anonymousUserService ) { super(adminService, userService); this.messageService = messageService; this.anonymousUserService = anonymousUserService; } @Override protected SocketUserData doOnMissingAccessToken( EzyPluginContext context, EzyUserLoginEvent event ) { SocketUserData anonymousUserData = handleAnonymousLogin( event ); event.setUserProperty(SocketUserData.class, anonymousUserData); event.setUserProperty(KEY_AUTHENTICATED, true); return anonymousUserData; } private SocketUserData handleAnonymousLogin( EzyUserLoginEvent event ) { String uuid = getUuid(event); String password = event.getPassword(); AnonymousUserModel anonymousUser = anonymousUserService .validateAnonymousUserByUuidAndPassword(uuid, password); event.setUsername(extractAnonymousUserUsername(anonymousUser)); return SocketUserData.builder() .userId(anonymousUser.getId()) .userUuid(anonymousUser.getUuid()) .socketUserType(SocketUserType.ANONYMOUS) .build(); } @Override protected SocketUserData handleUserLogin( EzyUserLoginEvent event, String userAccessToken ) { SocketUserData userData = super.handleUserLogin( event, userAccessToken ); synchronizeUserAnonymousMessages( userData.getUserId(), event.getPassword(), getUuid(event) ); return userData; } private void synchronizeUserAnonymousMessages( long userId, String password, String uuid ) { if (isBlank(uuid)) { return; } try { AnonymousUserModel anonymousUser = anonymousUserService .validateAnonymousUserByUuidAndPassword(uuid, password); if (anonymousUser.getUserId() <= 0) { messageService.synchronizeMessages( userId, anonymousUser.getId(), uuid ); } } catch (AnonymousUserWrongPasswordException e) { logger.info("wrong user anonymous password", e); } } public static String getUuid(EzyUserLoginEvent event) { Object data = event.getData(); if (!(data instanceof EzyObject)) { return null; } EzyObject object = (EzyObject) data; return object.get(COOKIE_NAME_UUID, String.class); } protected String extractAnonymousUserUsername( AnonymousUserModel anonymousUser ) { return SocketUserType.ANONYMOUS + "#" + anonymousUser.getId(); } }
This UserLoginController
class handles authentication for anonymous users.
Example of javascript client
You can use any ezyfox server client SDK to connect to the socket. Below is an example using the javascript client SDK:
ezychat.createSocket = function() { var zoneName = 'chat'; var appName = 'ezychat'; var socket = {}; socket.Commands = { MESSAGE: 'message', }; socket.onConnected = function(callback) { socket.connectedCallback = callback; }; socket.onConnectionFailed = function(callback) { socket.connectionFailedCallback = callback; }; socket.emitConnected = function() { socket.connectedCallback(); socket.connecting = true; }; socket.emitConnectionFailed = function() { socket.connectionFailedCallback(); }; socket.callbacks = {}; socket.send = function(cmd, data) { if (socket.app) { socket.app.send(cmd, data); } }; var connectionFailureHandler = new EzyConnectionFailureHandler(); connectionFailureHandler.postHandle = function(event) { socket.emitConnectionFailed(); }; var disconnectionHandler = new EzyDisconnectionHandler(); var handshakeHandler = new EzyHandshakeHandler(); handshakeHandler.getLoginRequest = function() { var accessToken = ezyweb.getCookie('accessToken'); var uuid = ezyweb.getCookie(ezychat.clientUuidKey); if (accessToken) { var data = {} data[ezychat.clientUuidKey] = uuid; data['accessToken'] = accessToken; return [ zoneName, 'username', ezyweb.getCookie(ezychat.clientPasswordKey), data, ]; } var data = {} data[ezychat.clientUuidKey] = uuid; return [ zoneName, 'username', ezyweb.getCookie(ezychat.clientPasswordKey), data ]; }; var userLoginHandler = new EzyLoginSuccessHandler(); userLoginHandler.handleLoginSuccess = function(data) { socket.me = this.client.me; socket.me.userType = data.socketUserType; socket.me.userUuid = data.socketUserUuid; socket.me.userUuidWithType = `${data.socketUserType}#${data.socketUserUuid}`; this.client.sendRequest(EzyCommand.APP_ACCESS, [appName]); }; var appAccessHandler = new EzyAppAccessHandler(); appAccessHandler.postHandle = function(app, data) { socket.app = app; socket.emitConnected(); } var config = new EzyClientConfig(); config.zoneName = zoneName; config.reconnect.maxReconnectCount = 32768; var clients = EzyClients.getInstance(); var client = clients.newClient( config ); var setup = client.setup; setup.addEventHandler( EzyEventType.CONNECTION_FAILURE, connectionFailureHandler ); setup.addEventHandler( EzyEventType.DISCONNECTION, disconnectionHandler ); setup.addDataHandler( EzyCommand.HANDSHAKE, handshakeHandler ); setup.addDataHandler( EzyCommand.LOGIN, userLoginHandler ); setup.addDataHandler( EzyCommand.APP_ACCESS, appAccessHandler ); setupApp = setup.setupApp( appName ); socket.on = function(cmd, callback) { setupApp.addDataHandler( cmd, function(app, data) { callback(data); } ); }; socket.on( socket.Commands.MESSAGE, (data) => { ezychat.appendMessage(data); } ); socket.onConnected(() => { ezychat.addChannelIfNotExists(); ezychat.canNotConnectServerShowed = false; }); socket.onConnectionFailed(() => { if (ezychat.isShortToastAvailable() && !ezychat.canNotConnectServerShowed ) { ezychat.canNotConnectServerShowed = true; shortToast( ezyweb.messages.error, ezyweb.messages.cannot_connect_to_server, 'text-danger' ); } }); socket.connect = function() { if (!socket.connecting) { client.connect(ezyweb.websocketUrl || 'ws://localhost:2208/ws'); } }; socket.appSend = function(uuid, targetChannelId, message) { socket.app.sendRequest( socket.Commands.MESSAGE, {message: message, channelId: targetChannelId}, ); }; return socket; } ezychat.socket = ezychat.socket || ezychat.createSocket();