/*
* Copyright 2010 Red Hat, Inc.
*
* Red Hat licenses this file to you 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.hornetq.core.protocol.stomp;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame;
import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameDecoder;
import org.jboss.netty.util.CharsetUtil;
/**
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (trustin@gmail.com)
*
* @version $Rev$, $Date$
*/
public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
private static final String WEBSOCKET_PATH = "/stomp";
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Object msg = e.getMessage();
if (msg instanceof HttpRequest) {
handleHttpRequest(ctx, (HttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
handleWebSocketFrame(ctx, (WebSocketFrame) msg);
}
}
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) {
// Allow only GET methods.
if (req.getMethod() != HttpMethod.GET) {
sendHttpResponse(
ctx, req, new DefaultHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN));
return;
}
// Serve the WebSocket handshake request.
if (req.getUri().equals(WEBSOCKET_PATH) &&
HttpHeaders.Values.UPGRADE.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.CONNECTION)) &&
HttpHeaders.Values.WEBSOCKET.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.UPGRADE))) {
// Create the WebSocket handshake response.
HttpResponse res = new DefaultHttpResponse(
HttpVersion.HTTP_1_1,
new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
res.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET);
res.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE);
res.addHeader(HttpHeaders.Names.WEBSOCKET_ORIGIN, req.getHeader(HttpHeaders.Names.ORIGIN));
res.addHeader(HttpHeaders.Names.WEBSOCKET_LOCATION, getWebSocketLocation(req));
String protocol = req.getHeader(HttpHeaders.Names.WEBSOCKET_PROTOCOL);
if (protocol != null) {
res.addHeader(HttpHeaders.Names.WEBSOCKET_PROTOCOL, protocol);
}
// Upgrade the connection and send the handshake response.
ChannelPipeline p = ctx.getChannel().getPipeline();
p.remove("http-aggregator");
p.replace("http-decoder", "ws-decoder", new WebSocketFrameDecoder());
ctx.getChannel().write(res);
p.replace("http-encoder", "ws-encoder", new WebSocketStompFrameEncoder());
return;
}
// Send an error page otherwise.
sendHttpResponse(
ctx, req, new DefaultHttpResponse(
HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN));
}
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
// Send the uppercased string back.
Channels.fireMessageReceived(ctx, frame.getBinaryData());
}
private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
// Generate an error page if response status code is not OK (200).
if (res.getStatus().getCode() != 200) {
res.setContent(
ChannelBuffers.copiedBuffer(
res.getStatus().toString(), CharsetUtil.UTF_8));
res.setHeader(
HttpHeaders.Names.CONTENT_LENGTH,
Integer.toString(res.getContent().readableBytes()));
}
// Send the response and close the connection if necessary.
ChannelFuture f = ctx.getChannel().write(res);
if (!HttpHeaders.isKeepAlive(req) || res.getStatus().getCode() != 200) {
f.addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
e.getChannel().close();
}
private String getWebSocketLocation(HttpRequest req) {
return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + WEBSOCKET_PATH;
}
}