package org.caudexorigo.http.netty;
import static org.jboss.netty.handler.codec.http.HttpHeaders.is100ContinueExpected;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.CONTINUE;
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import java.io.FileNotFoundException;
import org.caudexorigo.ErrorAnalyser;
import org.caudexorigo.http.netty.reporting.ResponseFormatter;
import org.caudexorigo.http.netty.reporting.StandardResponseFormatter;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
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.DefaultHttpChunk;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
//import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame;
//import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameDecoder;
//import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameEncoder;
public class HttpProtocolHandler extends SimpleChannelUpstreamHandler
{
private static Logger log = LoggerFactory.getLogger(HttpProtocolHandler.class);
private final ResponseFormatter _rspFmt;
private final RequestRouter _requestMapper;
private final RequestObserver _requestObserver;
// private boolean _hasWebSocketSupport;
// private Map<String, WebSocketHandler> _webSocketHandlers = new ConcurrentHashMap<String, WebSocketHandler>();
public HttpProtocolHandler(RequestRouter requestMapper)
{
_requestMapper = requestMapper;
_rspFmt = new StandardResponseFormatter(false);
_requestObserver = new DefaultObserver();
}
public HttpProtocolHandler(RequestRouter requestMapper, RequestObserver customObserver, ResponseFormatter customResponseFormtter)
{
_requestMapper = requestMapper;
if (customResponseFormtter == null)
{
_rspFmt = new StandardResponseFormatter(false);
}
else
{
_rspFmt = customResponseFormtter;
}
if (customObserver == null)
{
_requestObserver = new DefaultObserver();
}
else
{
_requestObserver = customObserver;
}
}
// public void addWebSocketHandler(String path, WebSocketHandler webSocketHandler)
// {
// if (webSocketHandler == null)
// {
// throw new IllegalArgumentException("WebSocketHandler can not be null");
// }
// if (StringUtils.isBlank(path))
// {
// throw new IllegalArgumentException("WebSocketHandler Path can not be blank");
// }
//
// _webSocketHandlers.put(path, webSocketHandler);
// _hasWebSocketSupport = true;
// }
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
{
Channel ch = e.getChannel();
ch.close();
if (log.isDebugEnabled())
{
Throwable rootCause = ErrorAnalyser.findRootCause(e.getCause());
log.debug(rootCause.getMessage(), rootCause);
}
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
Object msg = e.getMessage();
Channel ch = e.getChannel();
if (msg instanceof HttpRequest)
{
HttpRequest req = (HttpRequest) msg;
if (is100ContinueExpected(req))
{
Channels.write(ctx, Channels.future(ch), new DefaultHttpResponse(HTTP_1_1, CONTINUE));
ctx.setAttachment(new DefaultHttpRequest(HttpVersion.HTTP_1_1, req.getMethod(), req.getUri()));
}
handleHttpRequest(ctx, req);
}
else if (msg instanceof DefaultHttpChunk)
{
DefaultHttpChunk req = (DefaultHttpChunk) msg;
Object a = ctx.getAttachment();
if (a != null)
{
if (a instanceof DefaultHttpRequest)
{
DefaultHttpRequest real_request = (DefaultHttpRequest) a;
real_request.setContent(req.getContent());
handleHttpRequest(ctx, real_request);
ctx.setAttachment(null);
}
}
}
else
{
throw new IllegalArgumentException(String.format("Invalid Object type received by HttpHandler: '%s'", msg.getClass().getCanonicalName()));
}
// else if (msg instanceof WebSocketFrame)
// {
// if (_hasWebSocketSupport)
// {
// Channel channel = ctx.getChannel();
//
// WebSocketHandler ws_handler = getWebsocketHandler(ctx);
//
// if (ws_handler != null)
// {
// ws_handler.handleMessage(channel, (WebSocketFrame) msg);
// }
// }
// }
}
private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest request)
{
// if (_hasWebSocketSupport && _webSocketHandlers.containsKey(request.getUri()) && HttpHeaders.Values.UPGRADE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION)) && HttpHeaders.Values.WEBSOCKET.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.UPGRADE)))
// {
// Channel channel = ctx.getChannel();
// String uri = request.getUri();
// String origin = request.getHeader(HttpHeaders.Names.ORIGIN);
// String location = getWebSocketLocation(request);
//
// if (log.isDebugEnabled())
// {
// log.debug("Received WebSocket upgrade request. Origin: '{}'; Location: '{}'", origin, location);
// }
//
// _webSocketHandlers.get(uri).handleWebSocketOpened(ctx.getChannel());
//
// // Create the WebSocket handshake response.
// HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
// response.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET);
// response.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE);
//
// if (request.containsHeader(HttpHeaders.Names.SEC_WEBSOCKET_KEY1) && request.containsHeader(HttpHeaders.Names.SEC_WEBSOCKET_KEY2))
// {
// // New handshake method with a challenge:
// response.addHeader(HttpHeaders.Names.SEC_WEBSOCKET_ORIGIN, request.getHeader(HttpHeaders.Names.ORIGIN));
// response.addHeader(HttpHeaders.Names.SEC_WEBSOCKET_LOCATION, getWebSocketLocation(request));
// String protocol = response.getHeader(HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL);
// if (protocol != null)
// {
// response.addHeader(HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL, protocol);
// }
//
// // Calculate the answer of the challenge.
// String key1 = request.getHeader(HttpHeaders.Names.SEC_WEBSOCKET_KEY1);
// String key2 = request.getHeader(HttpHeaders.Names.SEC_WEBSOCKET_KEY2);
// int a = (int) (Long.parseLong(key1.replaceAll("[^0-9]", "")) / key1.replaceAll("[^ ]", "").length());
// int b = (int) (Long.parseLong(key2.replaceAll("[^0-9]", "")) / key2.replaceAll("[^ ]", "").length());
// long c = request.getContent().readLong();
// ChannelBuffer input = ChannelBuffers.buffer(16);
// input.writeInt(a);
// input.writeInt(b);
// input.writeLong(c);
//
// try
// {
// ChannelBuffer output = ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array()));
// response.setContent(output);
// }
// catch (NoSuchAlgorithmException e)
// {
// throw new RuntimeException(e);
// }
// }
// else
// {
// response.addHeader(HttpHeaders.Names.WEBSOCKET_ORIGIN, origin);
// response.addHeader(HttpHeaders.Names.WEBSOCKET_LOCATION, location);
// String protocol = request.getHeader(HttpHeaders.Names.WEBSOCKET_PROTOCOL);
// if (protocol != null)
// {
// response.addHeader(HttpHeaders.Names.WEBSOCKET_PROTOCOL, protocol);
// }
// }
//
// channel.write(response);
//
// ChannelPipeline pipeline = channel.getPipeline();
// pipeline.replace("http-encoder", "websocket-encoder", new WebSocket13FrameEncoder(false));
// pipeline.replace("http-decoder", "websocket-decoder", new WebSocket13FrameDecoder(false, false));
//
// ctx.setAttachment("IS_WS::" + uri);
//
// return;
// }
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
try
{
HttpAction action = _requestMapper.map(ctx, request);
observeBegin(ctx, request, response);
if (action != null)
{
action.process(ctx, request, response);
}
else
{
throw new WebException(new FileNotFoundException(), HttpResponseStatus.NOT_FOUND.getCode());
}
}
catch (Throwable t)
{
HttpAction errorAction;
if (t instanceof WebException)
{
WebException we = (WebException) t;
errorAction = new ErrorAction(we, _rspFmt);
}
else
{
errorAction = new ErrorAction(new WebException(t, HttpResponseStatus.INTERNAL_SERVER_ERROR.getCode()), _rspFmt);
}
errorAction.process(ctx, request, response);
}
finally
{
observeEnd(ctx, request, response);
}
}
private void observeBegin(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response)
{
try
{
_requestObserver.begin(ctx, request, response);
}
catch (Throwable t)
{
Throwable r = ErrorAnalyser.findRootCause(t);
log.error(r.getMessage(), r);
}
}
private void observeEnd(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response)
{
try
{
_requestObserver.end(ctx, request, response);
}
catch (Throwable t)
{
Throwable r = ErrorAnalyser.findRootCause(t);
log.error(r.getMessage(), r);
}
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
// if (_hasWebSocketSupport)
// {
// WebSocketHandler ws_handler = getWebsocketHandler(ctx);
//
// if (ws_handler != null)
// {
// ws_handler.handleWebSocketClosed(ctx.getChannel());
// }
// }
super.channelClosed(ctx, e);
}
// private WebSocketHandler getWebsocketHandler(ChannelHandlerContext ctx)
// {
// Object attach = ctx.getAttachment();
// if (attach != null)
// {
// String s_attach = attach.toString();
// if (s_attach.startsWith("IS_WS::"))
// {
// String uri = StringUtils.substringAfter(s_attach, "IS_WS::");
//
// if (StringUtils.isNotBlank(uri))
// {
// WebSocketHandler ws_handler = _webSocketHandlers.get(uri);
// return ws_handler;
// }
// }
// }
//
// return null;
// }
// private String getWebSocketLocation(HttpRequest req)
// {
// return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + req.getUri();
// }
}