简体   繁体   中英

Netty convert HttpRequest to ByteBuf

In my application I need to receive a byte array on a socket, parse it as a HttpRequest to perform some check and, if the checks passes, get back to the byte array and do some more work.

The application is based on NETTY (this is a requirement).

My first idea was to create a pipeline like this:

  1. HttpRequestDecoder (decode from ByteBuf to HttpRequest )
  2. MyHttpRequestHandler (do my own checks on the HttpRequest )
  3. HttpRequestEncoder (encode the HttpRequest to a ByteBuf )
  4. MyButeBufHandler (do my works with the ByteBuf )

However the HttpRequestEncoder extends the ChannelOutboundHandlerAdapter so it doesn't get called for the inbound data.

How can I accomplish this task? It would be nice to avoid decoding and re-encoding the request.

Regards, Massimiliano

Use an EmbeddedChannel in MyHttpRequestHandler .

EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestEncoder()); ch.writeOutbound(msg); ByteBuf encoded = ch.readOutbound();

You'll have to keep the EmbeddedChannel as a member variable of MyHttpRequestEncoder because HttpRequestEncoder is stateful. Also, please close the EmbeddedChannel when you finished using it (probably in your channelInactive() method.)

I just had to encode and decode some HttpObjects and struggled a bit with it. The hint that the decoder/encoder are stateful is very valuable.

That's why I thought I'll add my findings here. Maybe it's helpful to someone else.

I declared an RequestEncoder and a ResponseDecoder as a class member, but it still didn't work correctly. Until I remembered that the specific handler I was using the en/decoders within was shared...

That's how I got it to work in the end. My sequenceNr is to distinct between the different requests. I create one encoder and one decoder per request and save them in a HashMap. With my sequenceNr, I'm able to always get the same decoder/encoder for the same request. Don't forget to close and remove the de/encoder channels from the Map after processing the LastContent object.

@ChannelHandler.Sharable
public class HttpTunnelingServerHandler extends ChannelDuplexHandler {
private final Map<Integer, EmbeddedChannel> decoders = Collections.synchronizedMap(new HashMap<Integer, EmbeddedChannel>());
private final Map<Integer, EmbeddedChannel> encoders = Collections.synchronizedMap(new HashMap<Integer, EmbeddedChannel>());
.
.
//Encoding
if (!encoders.containsKey(currentResponse.getSequenceNr())) {
        encoders.put(currentResponse.getSequenceNr(), new EmbeddedChannel(new HttpResponseEncoder()));
    }
    EmbeddedChannel encoderChannel = encoders.get(currentResponse.getSequenceNr());
    encoderChannel.writeOutbound(recievedHttpObject);
    ByteBuf encoded = (ByteBuf) encoderChannel.readOutbound();
.
.
//Decoding
if (!decoders.containsKey(sequenceNr)) {
        decoders.put(sequenceNr, new EmbeddedChannel(new HttpRequestDecoder()));
        }
        EmbeddedChannel decoderChannel = decoders.get(sequenceNr);
        decoderChannel.writeInbound(bb);
        HttpObject httpObject = (HttpObject) decoderChannel.readInbound();

}

How about the put the EmbeddedChannel as the handler channel's attribute, instead of HashMap. Isn't it the same what you claim to solve the stateful encoder/decoder?

@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
      ctx.channel().attr(EMBEDED_CH).set( new EmbeddedChannel(new HttpRequestDecoder()));
      super.channelActive(ctx);
}
    
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
      EmbeddedChannel embedCh = ctx.channel().attr(EMBEDED_CH).get();
      if (embedCh != null) {
          embedCh.close();
      }

      super.channelInactive(ctx);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM