Getting Started

Create a servlet for handling incoming HTTP connections and call EngineIoServer class’s handleRequest method on receiving an HTTP request.

Example servlet

Example servlet class:

@WebServlet("/engine.io/*")
public class EngineIoServlet extends HttpServlet {

    private final EngineIoServer mEngineIoServer = new EngineIoServer();

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
        mEngineIoServer.handleRequest(request, response);
    }
}

In the example servlet above, a static instance of EngineIoServer is defined and the method service is overridden to call handleRequest.

Listening for connections

Attach a listener to the connection event of EngineIoServer to listen for new connections.

Example:

server.on("connection", new Emitter.Listener() {
    @Override
    public void call(Object... args) {
        EngineIoSocket socket = (EngineIoSocket) args[0];
        // Do something with socket
    }
});

Listening for message from client

Attach a listener to the message event of EngineIoSocket to listen for messages from client.

Example:

socket.on("message", new Emitter.Listener() {
    @Override
    public void call(Object... args) {
        Object message = args[0];
        // message can be either String or byte[]
        // Do something with message.
    }
});

Sending message to client

Call the send method on EngineIoSocket to send packet to remote client.

Example:

socket.send(new Packet<>(Packet.MESSAGE, "foo"));

WebSocket connections

Handling WebSocket connections involves creating an instance of EngineIoWebSocket and passing it in a call to handleWebSocket method of EngineIoServer. The process to do this is different for each server (viz. Tomcat, Jetty).

Generic server

Add an endpoint like the following:

public final class EngineIoEndpoint extends Endpoint {

    private Session mSession;
    private Map<String, String> mQuery;
    private EngineIoWebSocket mEngineIoWebSocket;

    private EngineIoServer mEngineIoServer; // The engine.io server instance

    @Override
    public void onOpen(Session session, EndpointConfig endpointConfig) {
        mSession = session;
        mQuery = ParseQS.decode(session.getQueryString());

        mEngineIoWebSocket = new EngineIoWebSocketImpl();

        /*
         * These cannot be converted to lambda because of runtime type inference
         * by server.
         */
        mSession.addMessageHandler(new MessageHandler.Whole<String>() {
            @Override
            public void onMessage(String message) {
                mEngineIoWebSocket.emit("message", message);
            }
        });
        mSession.addMessageHandler(new MessageHandler.Whole<byte[]>() {
            @Override
            public void onMessage(byte[] message) {
                mEngineIoWebSocket.emit("message", (Object)message);
            }
        });

        mEngineIoServer.handleWebSocket(mEngineIoWebSocket);
    }

    @Override
    public void onClose(Session session, CloseReason closeReason) {
        super.onClose(session, closeReason);

        mEngineIoWebSocket.emit("close");
        mSession = null;
    }

    @Override
    public void onError(Session session, Throwable thr) {
        super.onError(session, thr);

        mEngineIoWebSocket.emit("error", "unknown error", thr.getMessage());
    }

    private class EngineIoWebSocketImpl extends EngineIoWebSocket {

        private RemoteEndpoint.Basic mBasic;

        EngineIoWebSocketImpl() {
            mBasic = mSession.getBasicRemote();
        }

        @Override
        public Map<String, String> getQuery() {
            return mQuery;
        }

        @Override
        public void write(String message) throws IOException {
            mBasic.sendText(message);
        }

        @Override
        public void write(byte[] message) throws IOException {
            mBasic.sendBinary(ByteBuffer.wrap(message));
        }

        @Override
        public void close() {
            try {
                mSession.close();
            } catch (IOException ignore) {
            }
        }
    }
}

The endpoint can be registered by annotation or a ServerApplicationConfig class like the following:

public final class ApplicationServerConfig implements ServerApplicationConfig {

    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
        final HashSet<ServerEndpointConfig> result = new HashSet<>();
        result.add(ServerEndpointConfig.Builder
                .create(EngineIoEndpoint.class, "/engine.io/")
                .build());

        return result;
    }

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
        return null;
    }
}

Jetty server

For Jetty server, add the Jetty WebSocket Adapter dependency. Then, the following code can be used to listen for WebSocket connections:

ServletContextHandler servletContextHandler;    // The jetty servlet context handler

WebSocketUpgradeFilter webSocketUpgradeFilter = WebSocketUpgradeFilter.configureContext(servletContextHandler);
webSocketUpgradeFilter.addMapping(new ServletPathSpec("/engine.io/*"), new WebSocketCreator() {
    @Override
    public Object createWebSocket(ServletUpgradeRequest servletUpgradeRequest, ServletUpgradeResponse servletUpgradeResponse) {
        return new JettyWebSocketHandler(EngineIoServlet.getEngineIoServer());
    }
});

Async Polling

To enable async polling, mark the servlet with asyncSupported set to true. This can be done with annotations as @WebServlet(value = "/engine.io/*", asyncSupported = true). Or with XML as <async-supported>true</async-supported>. Enabling async is recommended as the server is bombarded with GET requests if async is disabled.