org.atmosphere.socketio.cpr.SocketIOAtmosphereInterceptor Maven / Gradle / Ivy
/*
* Copyright 2015 Async-IO.org
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.atmosphere.socketio.cpr;
import org.atmosphere.config.service.AtmosphereInterceptorService;
import org.atmosphere.cpr.Action;
import org.atmosphere.cpr.AsyncIOWriter;
import org.atmosphere.cpr.AsyncIOWriterAdapter;
import org.atmosphere.cpr.AtmosphereConfig;
import org.atmosphere.cpr.AtmosphereHandler;
import org.atmosphere.cpr.AtmosphereInterceptor;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResourceImpl;
import org.atmosphere.cpr.AtmosphereResponse;
import org.atmosphere.socketio.SocketIOSession;
import org.atmosphere.socketio.SocketIOSessionManager;
import org.atmosphere.socketio.SocketIOSessionOutbound;
import org.atmosphere.socketio.transport.JSONPPollingTransport;
import org.atmosphere.socketio.transport.SocketIOPacketImpl;
import org.atmosphere.socketio.transport.SocketIOSessionManagerImpl;
import org.atmosphere.socketio.transport.Transport;
import org.atmosphere.socketio.transport.WebSocketTransport;
import org.atmosphere.socketio.transport.XHRPollingTransport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.atmosphere.socketio.transport.SocketIOSessionManagerImpl.mapper;
/**
* SocketIO implementation.
*
* @author Sebastien Dionne
* @@author Jeanfrancois Arcand
*/
@AtmosphereInterceptorService
public class SocketIOAtmosphereInterceptor implements AtmosphereInterceptor {
public final static String SOCKETIO_PACKET = SocketIOSessionManagerImpl.SocketIOProtocol.class.getName();
private static final Logger logger = LoggerFactory.getLogger(SocketIOAtmosphereInterceptor.class);
private static final int BUFFER_SIZE_DEFAULT = 8192;
private final SocketIOSessionManager sessionManager = new SocketIOSessionManagerImpl();
/**
* See https://github.com/LearnBoost/socket.io/wiki/Configuring-Socket.IO#wiki-server
* The timeout for the server when it should send a new heartbeat to the client.
* In milliseconds.
*/
private long heartbeatInterval = 25000;
/**
* See https://github.com/LearnBoost/socket.io/wiki/Configuring-Socket.IO#wiki-server
* The timeout for the client – when it closes the connection it still has X amounts of seconds to re-open the connection. This value is sent to the client after a successful handshake.
* In milliseconds.
*/
private long timeout = 60000;
/**
* See https://github.com/LearnBoost/socket.io/wiki/Configuring-Socket.IO#wiki-server
* The timeout for the client, we should receive a heartbeat from the server within this interval. This should be greater than the heartbeat interval. This value is sent to the client after a successful handshake.
* In milliseconds.
*/
private long heartbeatTimeout = 60000;
private static int suspendTime = 20000;
private final Map transports = new HashMap();
private String availableTransports;
public static final String BUFFER_SIZE_INIT_PARAM = "socketio-bufferSize";
public static final String SOCKETIO_TRANSPORT = "socketio-transport";
public static final String SOCKETIO_TIMEOUT = "socketio-timeout";
public static final String SOCKETIO_HEARTBEAT = "socketio-heartbeat";
public static final String SOCKETIO_HEARTBEAT_TIMEOUT = "socketio-heartbeat-timeout";
public static final String SOCKETIO_SUSPEND = "socketio-suspendTime";
private SocketIOSessionManager getSessionManager(String version) {
if (version.equals("1")) {
return sessionManager;
}
return null;
}
@Override
public String toString() {
return "SocketIO-Support";
}
@Override
public Action inspect(AtmosphereResource r) {
final AtmosphereRequest request = r.getRequest();
final AtmosphereResponse response = r.getResponse();
final AtmosphereHandler atmosphereHandler = r.getAtmosphereHandler();
try {
// find the transport
String path = request.getPathInfo();
if (path == null || path.length() == 0 || "/".equals(path)) {
logger.debug("Not a SocketIO client");
return Action.CONTINUE;
}
if (path.startsWith("/")) {
path = path.substring(1);
}
String[] parts = path.split("/");
String protocol = null;
String version = null;
// find protocol's version
if (parts.length == 0) {
logger.debug("Not a SocketIO protocol supported");
return Action.CONTINUE;
} else if (parts.length == 1) {
// is protocol's version ?
if (parts[0].length() == 1) {
version = parts[0];
// must be a digit
if (!Character.isDigit(version.charAt(0))) {
version = null;
}
} else {
protocol = parts[0];
}
} else {
// ex :[1, xhr-polling, 7589995670715459]
version = parts[0];
protocol = parts[1];
// must be a digit
if (!Character.isDigit(version.charAt(0))) {
version = null;
protocol = null;
}
}
if (protocol == null && version == null) {
logger.debug("Not a SocketIO protocol supported");
return Action.CONTINUE;
} else if (protocol == null && version != null) {
// create a session and send the available transports to the client
response.setStatus(200);
response.setContentType("plain/text");
SocketIOSession session = getSessionManager(version).createSession((AtmosphereResourceImpl) r, atmosphereHandler);
response.getWriter().print(session.getSessionId() + ":" + (heartbeatTimeout/1000) + ":" + (timeout/1000) + ":" + availableTransports);
return Action.CANCELLED;
} else if (protocol != null && version == null) {
version = "0";
}
final Transport transport = transports.get(protocol + "-" + version);
if (transport != null) {
if (!SocketIOAtmosphereHandler.class.isAssignableFrom(atmosphereHandler.getClass())) {
response.asyncIOWriter(new AsyncIOWriterAdapter() {
@Override
public AsyncIOWriter write(AtmosphereResponse r, String data) throws IOException {
SocketIOSessionOutbound outbound = (SocketIOSessionOutbound)
request.getAttribute(SocketIOAtmosphereHandler.SOCKETIO_SESSION_OUTBOUND);
SocketIOSessionManagerImpl.SocketIOProtocol p = (SocketIOSessionManagerImpl.SocketIOProtocol)
r.request().getAttribute(SOCKETIO_PACKET);
String msg = p == null ? data : mapper.writeValueAsString(p.clearArgs().addArgs(data));
if (outbound != null) {
outbound.sendMessage(new SocketIOPacketImpl(SocketIOPacketImpl.PacketType.EVENT, msg));
} else {
r.getResponse().getOutputStream().write(msg.getBytes(r.getCharacterEncoding()));
}
return this;
}
@Override
public AsyncIOWriter write(AtmosphereResponse r, byte[] data) throws IOException {
SocketIOSessionManagerImpl.SocketIOProtocol p = (SocketIOSessionManagerImpl.SocketIOProtocol)
r.request().getAttribute(SOCKETIO_PACKET);
if (p == null) {
r.getResponse().getOutputStream().write(data);
} else {
write(r, new String(data, r.request().getCharacterEncoding()));
}
return this;
}
@Override
public AsyncIOWriter write(AtmosphereResponse r, byte[] data, int offset, int length) throws IOException {
SocketIOSessionManagerImpl.SocketIOProtocol p = (SocketIOSessionManagerImpl.SocketIOProtocol)
r.request().getAttribute(SOCKETIO_PACKET);
if (p == null) {
r.getResponse().getOutputStream().write(data, offset, length);
} else {
write(r, new String(data, offset, length, r.request().getCharacterEncoding()));
}
return this;
}
@Override
public AsyncIOWriter flush(AtmosphereResponse r) throws IOException {
try {
r.getResponse().getOutputStream().flush();
} catch (IllegalStateException ex) {
r.getResponse().getWriter().flush();
}
return this;
}
@Override
public AsyncIOWriter writeError(AtmosphereResponse r, int errorCode, String message) throws IOException {
((HttpServletResponse) r.getResponse()).sendError(errorCode, message);
return this;
}
});
}
transport.handle((AtmosphereResourceImpl) r, atmosphereHandler, getSessionManager(version));
} else {
logger.error("Protocol not supported : " + protocol);
}
} catch (Exception e) {
logger.error("", e);
}
return Action.CONTINUE;
}
@Override
public void postInspect(AtmosphereResource r) {
}
@Override
public void destroy() {
}
@Override
public void configure(AtmosphereConfig config) {
String s = config.getInitParameter(SOCKETIO_TRANSPORT);
availableTransports = s;
if (availableTransports == null) {
availableTransports = "websocket,xhr-polling,jsonp-polling";
}
String timeoutWebXML = config.getInitParameter(SOCKETIO_TIMEOUT);
if (timeoutWebXML != null) {
timeout = Integer.parseInt(timeoutWebXML);
}
String heartbeatWebXML = config.getInitParameter(SOCKETIO_HEARTBEAT);
if (heartbeatWebXML != null) {
heartbeatInterval = Integer.parseInt(heartbeatWebXML);
}
String heartbeatTimeoutWebXML = config.getInitParameter(SOCKETIO_HEARTBEAT_TIMEOUT);
if (heartbeatTimeoutWebXML != null) {
heartbeatTimeout = Integer.parseInt(heartbeatTimeoutWebXML);
}
String suspendWebXML = config.getInitParameter(SOCKETIO_SUSPEND);
if (suspendWebXML != null) {
suspendTime = Integer.parseInt(suspendWebXML);
}
// VERSION 1
WebSocketTransport websocketTransport1 = new WebSocketTransport();
XHRPollingTransport xhrPollingTransport1 = new XHRPollingTransport(BUFFER_SIZE_DEFAULT);
JSONPPollingTransport jsonpPollingTransport1 = new JSONPPollingTransport(BUFFER_SIZE_DEFAULT);
transports.put(websocketTransport1.getName() + "-1", websocketTransport1);
transports.put(xhrPollingTransport1.getName() + "-1", xhrPollingTransport1);
transports.put(jsonpPollingTransport1.getName() + "-1", jsonpPollingTransport1);
sessionManager.setTimeout(timeout);
sessionManager.setHeartbeatInterval(heartbeatInterval);
sessionManager.setRequestSuspendTime(suspendTime);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy