Http Response Handler

Trustin Lee (이희승) trustin at gmail.com
Tue Sep 29 04:38:33 EDT 2009


Hi David,

Thanks for your participation most of all.  I really appreciate your
effort and please keep up the good work!

FYI though, 1) you don't need to handle HttpChunks if you add
HttpChunkAggregator right next to HttpMessageDecoder, and 2) you don't
need to remove the decoder from the pipeline because you can get the
content of the decoded HTTP message by calling
HttpMessage.getContent() as described in another thread you started.

— Trustin Lee, http://gleamynode.net/

On Sat, Sep 26, 2009 at 5:55 PM, Hoyt, David <hoyt6 at llnl.gov> wrote:
> In hopes it may be of benefit to the community, I've created a simplified version of the http decoder that simply parses out the header info. and then removes itself from the pipeline once it has reached the message contents. This makes it easier to work with frame decoders upstream. It's not tested all that much and I'm sure it doesn't conform to all of the HTTP spec., but it might save someone else some trouble. I attached it to this message and I'll produce a copy of it below as well.
>
> It's not as robust as the netty-supplied one, but it's much simpler to use (I think). You don't have to deal w/ http chunks whatsoever - after it's done reading in the header, you never have to see it again. If you're interested in seeing the header info., you can extend the class and override the processHeaders() method which is invoked right after we've reached the message contents. For example:
>
> @ChannelPipelineCoverage("one")
> private class MyCustomHttpResponseUpstreamHandler extends SimpleHttpResponseUpstreamHandler {
>        @Override
>        protected ChannelBuffer processHeaders(Channel channel, ChannelBuffer buffer) throws Exception {
>                if (getHttpStatus() != STATUS_OK)
>                        throw new IllegalArgumentException();
>
>                Map<String, String> headers = this.getHttpHeaders();
>                if (headers != null && headers.containsKey("Content-Type")) {
>                        if (!"image/jpg".equalsIgnoreCase(headers.get("Content-Type")))
>                                throw new IllegalArgumentException();
>                }
>
>
>                return buffer;
>        }
>
>        @Override
>        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
>                e.getChannel().close();
>        }
> }
>
>
>
> -------
>
> Here's the class:
>
> import java.util.HashMap;
> import java.util.Map;
> import java.util.regex.Matcher;
> import java.util.regex.Pattern;
> import org.jboss.netty.buffer.ChannelBuffer;
> import org.jboss.netty.channel.Channel;
> import org.jboss.netty.channel.ChannelHandlerContext;
> import org.jboss.netty.channel.ChannelPipelineCoverage;
> import org.jboss.netty.channel.Channels;
> import org.jboss.netty.channel.MessageEvent;
> import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
>
> /**
>  * Ultra simple HTTP decoder for Netty that parses out the header and then
>  * conveniently removes itself from the pipeline.
>  */
> @ChannelPipelineCoverage("one")
> public class SimpleHttpResponseUpstreamHandler extends SimpleChannelUpstreamHandler {
>        //<editor-fold defaultstate="collapsed" desc="Constants">
>        public static final Pattern
>                  REGEX_STATUS = Pattern.compile("(.+)\\s+(\\d+)\\s+(.+)", Pattern.CASE_INSENSITIVE)
>                , REGEX_HEADER = Pattern.compile("([^:]+):\\s*(.+)", Pattern.CASE_INSENSITIVE)
>        ;
>
>        public static final byte
>                  CR = (byte)13
>                , LF = (byte)10
>        ;
>
>        public static final int
>                  STATUS_CONTINUE = 100
>                , STATUS_SWITCHING_PROTOCOLS = 101
>                , STATUS_OK = 200
>                , STATUS_CREATED = 201
>                , STATUS_ACCEPTED = 202
>                , STATUS_NON_AUTHORITATIVE_INFORMATION = 203
>                , STATUS_NO_CONTENT = 204
>                , STATUS_RESET_CONTENT = 205
>                , STATUS_PARTIAL_CONTENT = 206
>                , STATUS_MULTIPLE_CHOICES = 300
>                , STATUS_MOVED_PERMANENTLY = 301
>                , STATUS_FOUND = 302
>                , STATUS_SEE_OTHER = 303
>                , STATUS_NOT_MODIFIED = 304
>                , STATUS_USE_PROXY = 305
>                , STATUS_TEMPORARY_REDIRECT = 307
>                , STATUS_BAD_REQUEST = 400
>                , STATUS_UNAUTHORIZED = 401
>                , STATUS_PAYMENT_REQUIRED = 402
>                , STATUS_FORBIDDEN = 403
>                , STATUS_NOT_FOUND = 404
>                , STATUS_NOT_ALLOWED = 405
>                , STATUS_NOT_ACCEPTABLE = 406
>                , STATUS_PROXY_AUTHENTICATION_REQUIRED = 407
>                , STATUS_REQUEST_TIMEOUT = 408
>                , STATUS_CONFLICT = 409
>                , STATUS_GONE = 410
>                , STATUS_LENGTH_REQUIRED = 411
>                , STATUS_PRECONDITION_FAILED = 412
>                , STATUS_REQUEST_ENTITY_TOO_LARGE = 413
>                , STATUS_REQUEST_URI_TOO_LONG = 414
>                , STATUS_UNSUPPORTED_MEDIA_TYPE = 415
>                , STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416
>                , STATUS_EXPECTATION_FAILED = 417
>                , STATUS_INTERNAL_SERVER_ERROR = 500
>                , STATUS_NOT_IMPLEMENTED = 501
>                , STATUS_BAD_GATEWAY = 502
>                , STATUS_SERVICE_UNAVAILABLE = 503
>                , STATUS_GATEWAY_TIMEOUT = 504
>                , STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
>        ;
>        //</editor-fold>
>
>        //<editor-fold defaultstate="collapsed" desc="Variables">
>        private boolean readHeaders = false;
>        private boolean readCR = false;
>        private String statusLine = null;
>        private String httpVersion = null;
>        private String httpStatus = null;
>        private String httpStatusMsg = null;
>        private int status = STATUS_OK;
>        private StringBuilder sb = new StringBuilder(64);
>        private Map<String, String> headers = new HashMap<String, String>(5);
>        //</editor-fold>
>
>        //<editor-fold defaultstate="collapsed" desc="Getters">
>        public final int getHttpStatus() {
>                return status;
>        }
>
>        public final String getHttpVersion() {
>                return httpVersion;
>        }
>
>        public final String getHttpStatusMessage() {
>                return httpStatusMsg;
>        }
>
>        public final String getHttpStatusLine() {
>                return statusLine;
>        }
>
>        public final Map<String, String> getHttpHeaders() {
>                return headers;
>        }
>        //</editor-fold>
>
>        //<editor-fold defaultstate="collapsed" desc="Helper Methods">
>        private String readLine(ChannelBuffer buffer) {
>                byte byteVal;
>                while(buffer.readable()) {
>                        byteVal = buffer.readByte();
>                        if (byteVal == CR) {
>                                readCR = true;
>                                continue;
>                        } else if (readCR && byteVal == LF) {
>                                readCR = false;
>                                String line = sb.toString();
>                                sb = new StringBuilder(64);
>                                return line;
>                        }
>                        sb.append((char)byteVal);
>                }
>                return null;
>        }
>        //</editor-fold>
>
>        //<editor-fold defaultstate="collapsed" desc="Protected Methods">
>        protected ChannelBuffer processHeaders(Channel channel, ChannelBuffer buffer) throws Exception {
>                //Allows subclasses to process header info if they want to
>                return buffer;
>        }
>        //</editor-fold>
>
>        //<editor-fold defaultstate="collapsed" desc="Message">
>        @Override
>        public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
>                Object m = e.getMessage();
>                if (!(m instanceof ChannelBuffer)) {
>                        ctx.sendUpstream(e);
>                        return;
>                }
>
>                ChannelBuffer buffer = (ChannelBuffer) m;
>                if (!buffer.readable()) {
>                        return;
>                }
>
>                if (readHeaders) {
>                        ctx.sendUpstream(e);
>                        return;
>                }
>
>                String line;
>                while((line = readLine(buffer)) != null) {
>                        if (statusLine == null) {
>                                statusLine = line;
>
>                                final Matcher matcher = REGEX_STATUS.matcher(statusLine);
>                                if (!matcher.matches() || matcher.groupCount() != 3)
>                                        throw new IllegalArgumentException("Invalid status line: " + line);
>
>                                httpVersion = matcher.group(1);
>                                httpStatus = matcher.group(2);
>                                httpStatusMsg = matcher.group(3);
>                                status = Integer.parseInt(httpStatus);
>                        } else {
>                                if (!"".equals(line)) {
>                                        //Add header
>                                        final Matcher matcher = REGEX_HEADER.matcher(line);
>                                        if (!matcher.matches() || matcher.groupCount() != 2)
>                                                throw new IllegalArgumentException("Invalid header: " + line);
>
>                                        headers.put(matcher.group(1), matcher.group(2));
>                                } else {
>                                        sb = null;
>                                        readHeaders = true;
>
>                                        //Start of content according to HTTP spec
>                                        //Take this decoder out of the pipeline - skip directly to next guy in the pipeline from now on
>                                        ctx.getPipeline().remove(this);
>
>                                        //Send this upstream
>                                        Channels.fireMessageReceived(ctx.getChannel(), processHeaders(ctx.getChannel(), buffer));
>                                        return;
>                                }
>                        }
>                }
>        }
>        //</editor-fold>
> }
>
> _______________________________________________
> netty-users mailing list
> netty-users at lists.jboss.org
> https://lists.jboss.org/mailman/listinfo/netty-users
>
>



More information about the netty-users mailing list