/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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.jboss.errai.bus.server.servlet.websocket;
import org.jboss.errai.bus.client.api.QueueSession;
import org.jboss.errai.bus.client.protocols.BusCommand;
import org.jboss.errai.bus.server.api.MessageQueue;
import org.jboss.errai.bus.server.io.DirectDeliveryHandler;
import org.jboss.errai.bus.server.io.QueueChannel;
import org.jboss.errai.bus.server.io.websockets.WebSocketServerHandler;
import org.jboss.errai.bus.server.io.websockets.WebSocketTokenManager;
import org.jboss.errai.bus.server.service.ErraiService;
import org.jboss.errai.bus.server.util.LocalContext;
import org.jboss.errai.common.client.protocols.MessageParts;
import org.jboss.errai.marshalling.client.api.json.EJObject;
import org.jboss.errai.marshalling.client.api.json.EJString;
import org.jboss.errai.marshalling.client.api.json.EJValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* Responsible for establishing a WebSocket connection with clients.
*
* @author Michel Werren
*/
public class WebSocketNegotiationHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketNegotiationHandler.class.getName());
@SuppressWarnings("rawtypes")
public static QueueSession establishNegotiation(EJValue val, QueueChannel queueChannel, ErraiService service)
throws IOException {
QueueSession session = null;
final EJObject ejObject = val.isObject();
if (ejObject == null) {
return null;
}
final String commandType = ejObject.get(MessageParts.CommandType.name()).isString().stringValue();
// this client apparently wants to connect.
if (BusCommand.Associate.name().equals(commandType)) {
final String sessionKey = ejObject.get(MessageParts.ConnectionSessionKey.name()).isString().stringValue();
// has this client already attempted a connection, and is in a wait verify
// state
if (sessionKey != null && (session = service.getBus().getSessionBySessionId(sessionKey)) != null) {
final LocalContext localCometSession = LocalContext.get(session);
if (localCometSession.hasAttribute(WebSocketServerHandler.SESSION_ATTR_WS_STATUS)
&& WebSocketServerHandler.WEBSOCKET_ACTIVE.equals(localCometSession.getAttribute(String.class,
WebSocketServerHandler.SESSION_ATTR_WS_STATUS))) {
// set the session queue into direct channel mode.
final MessageQueue queue = service.getBus().getQueueBySession(sessionKey);
queue.setDeliveryHandler(DirectDeliveryHandler.createFor(queueChannel));
LOGGER.debug("set direct delivery handler on session: {}", session.getSessionId());
//See ERRAI-873: In case a connection failure has occurred make sure
//to resend a successful negotiation message.
sendMessage(queueChannel, WebSocketNegotiationMessage.getSuccessfulNegotiation());
return session;
}
// check the activation key matches.
final EJString activationKey = ejObject.get(MessageParts.WebSocketToken.name()).isString();
if (activationKey == null || !WebSocketTokenManager.verifyOneTimeToken(session, activationKey.stringValue())) {
// nope. go away!
final String error = "bad negotiation key";
LOGGER.debug("activation key not match for session: {}", session.getSessionId());
sendMessage(queueChannel, WebSocketNegotiationMessage.getFailedNegotiation(error));
}
else {
// the key matches. now we send the reverse challenge to prove this
// client is actually
// already talking to the bus over the COMET channel.
final String reverseToken = WebSocketTokenManager.getNewOneTimeToken(session);
localCometSession.setAttribute(WebSocketServerHandler.SESSION_ATTR_WS_STATUS,
WebSocketServerHandler.WEBSOCKET_AWAIT_ACTIVATION);
// send the challenge.
LOGGER.debug("reverse challange for session: {}", session.getSessionId());
sendMessage(queueChannel, WebSocketNegotiationMessage.getReverseChallenge(reverseToken));
return null;
}
sendMessage(queueChannel, WebSocketNegotiationMessage.getSuccessfulNegotiation());
}
else {
final String error = "bad session id";
LOGGER.debug("bad session id");
sendMessage(queueChannel, WebSocketNegotiationMessage.getFailedNegotiation(error));
}
}
else {
final String error = "bad command";
LOGGER.debug("bad command");
sendMessage(queueChannel, WebSocketNegotiationMessage.getFailedNegotiation(error));
}
return null;
}
public static void sendMessage(final QueueChannel channel, final String message) throws IOException {
channel.write(message);
}
}