package org.caudexorigo.http.netty4; import static io.netty.handler.codec.http.HttpHeaders.is100ContinueExpected; import static io.netty.handler.codec.http.HttpResponseStatus.CONTINUE; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.util.ReferenceCountUtil; import java.io.FileNotFoundException; import org.caudexorigo.ErrorAnalyser; import org.caudexorigo.http.netty4.reporting.ResponseFormatter; import org.caudexorigo.http.netty4.reporting.StandardResponseFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HttpProtocolHandler extends ChannelInboundHandlerAdapter { private static Logger log = LoggerFactory.getLogger(HttpProtocolHandler.class); private final ResponseFormatter _rspFmt; private final RequestRouter _requestMapper; private final RequestObserver _requestObserver; 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; } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (log.isDebugEnabled()) { Throwable r = ErrorAnalyser.findRootCause(cause); log.debug(r.getMessage(), r); } ctx.close(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; handleRead(ctx, request); try { ReferenceCountUtil.release(request); } catch (Throwable t) { log.error(t.getMessage(), t); } } else { throw new IllegalArgumentException("Can only handle instances of FullHttpRequest messages"); } } public void handleRead(ChannelHandlerContext ctx, FullHttpRequest request) { if (is100ContinueExpected(request)) { ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE)); } try { HttpAction action = _requestMapper.map(ctx, request); if (action != null) { action.process(ctx, request, _requestObserver); } else { throw new WebException(new FileNotFoundException("No available HttpAction for the request"), HttpResponseStatus.NOT_FOUND.code()); } } catch (Throwable t) { ByteBuf ebuf = ctx.alloc().buffer(); FullHttpResponse eresponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR, ebuf); Throwable w = findWebException(t); HttpAction errorAction; if (w instanceof WebException) { WebException we = (WebException) w; errorAction = new ErrorAction(we, _rspFmt); eresponse.setStatus(HttpResponseStatus.valueOf(we.getHttpStatusCode())); } else { Throwable r = ErrorAnalyser.findRootCause(t); errorAction = new ErrorAction(new WebException(r, HttpResponseStatus.INTERNAL_SERVER_ERROR.code()), _rspFmt); } errorAction.doProcess(ctx, request, eresponse); errorAction.observeEnd(ctx, request, eresponse, _requestObserver); } } private Throwable findWebException(Throwable ex) { if (ex instanceof WebException) { return ex; } else { Throwable inner_ex = ex.getCause(); while (inner_ex != null) { if (inner_ex instanceof WebException) { return inner_ex; } inner_ex = inner_ex.getCause(); } } return ex; } }