package org.caudexorigo.http.netty4; import io.netty.buffer.ByteBuf; import io.netty.buffer.EmptyByteBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; 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.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.util.ReferenceCountUtil; import org.caudexorigo.ErrorAnalyser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class HttpAction { private static final CharSequence CONTENT_LENGTH_ENTITY = HttpHeaders.newEntity(Names.CONTENT_LENGTH); private static final CharSequence DATE_ENTITY = HttpHeaders.newEntity(Names.DATE); private static Logger log = LoggerFactory.getLogger(HttpAction.class); public HttpAction() { super(); } public abstract void service(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response); protected void process(ChannelHandlerContext ctx, FullHttpRequest request, RequestObserver requestObserver) { observeBegin(ctx, request, requestObserver); if (isZeroCopy()) { ByteBuf buf = new EmptyByteBuf(ctx.alloc()); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); try { service(ctx, request, response); } catch (Throwable t) { if (response != null) { ReferenceCountUtil.release(response); } throw new RuntimeException(t); } observeEnd(ctx, request, response, requestObserver); ReferenceCountUtil.release(response); } else { FullHttpResponse response = buildResponse(ctx); try { doProcess(ctx, request, response); } catch (Throwable t) { if (response != null) { ReferenceCountUtil.release(response); } throw new RuntimeException(t); } observeEnd(ctx, request, response, requestObserver); } } void doProcess(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) { boolean is_keep_alive = HttpHeaders.isKeepAlive(request); service(ctx, request, response); commitResponse(ctx, response, is_keep_alive); } protected FullHttpResponse buildResponse(ChannelHandlerContext ctx) { ByteBuf buf = ctx.alloc().buffer(); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); return response; } protected boolean isZeroCopy() { return false; } void commitResponse(ChannelHandlerContext ctx, FullHttpResponse response, boolean is_keep_alive) { response.headers().set(CONTENT_LENGTH_ENTITY, String.valueOf(response.content().readableBytes())); response.headers().set(DATE_ENTITY, HttpDateFormat.getCurrentHttpDate()); ChannelFuture future = ctx.writeAndFlush(response); // implicit response.release(); // Decide whether to close the connection or not. if (!is_keep_alive) { // Close the connection when the whole content is written out. future.addListener(ChannelFutureListener.CLOSE); } } protected void observeBegin(ChannelHandlerContext ctx, HttpRequest request, RequestObserver requestObserver) { try { requestObserver.begin(ctx, request); } catch (Throwable t) { Throwable r = ErrorAnalyser.findRootCause(t); log.error(r.getMessage(), r); } } protected void observeEnd(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response, RequestObserver requestObserver) { try { requestObserver.end(ctx, request, response); } catch (Throwable t) { Throwable r = ErrorAnalyser.findRootCause(t); log.error(r.getMessage(), r); } } }