Http Response Handler
Trustin Lee (이희승)
trustin at gmail.com
Mon Oct 12 22:28:00 EDT 2009
Hi David,
Another late response. Sorry!
On Tue, Sep 29, 2009 at 11:18 PM, Hoyt, David <hoyt6 at llnl.gov> wrote:
> My problem was that I needed access to the message ASAP and the aggregator waited until the chunk size was full (or so it seemed). I decreased the chunk size to my known packet size and that worked better (in terms of getting the packets as quickly as possible), but it wasn't working in conjunction with FrameDecoder (which expects a ChannelBuffer not an HttpMessage or HttpChunk) which is what I ultimately needed. What I really needed was something that allowed me to tunnel my protocol through http. In the end, my solution worked for me.
>
> One question I did have, though, is if the normal http handler has an option to follow redirects (http 301s) and if there's a way to set the number of allowed hops - mine currently doesn't support this.
I believe it's up to the client. The client should be able to detect
the cyclic infinite redirection. That's all I guess.
HTH
— Trustin Lee, http://gleamynode.net/
> -----Original Message-----
> From: netty-users-bounces at lists.jboss.org [mailto:netty-users-bounces at lists.jboss.org] On Behalf Of Trustin Lee (???)
> Sent: Tuesday, September 29, 2009 1:39 AM
> To: Netty Users
> Subject: Re: Http Response Handler
>
> 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
>>
>>
>
> _______________________________________________
> netty-users mailing list
> netty-users at lists.jboss.org
> https://*lists.jboss.org/mailman/listinfo/netty-users
>
>
> _______________________________________________
> 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