Massimiliano
Massimiliano

Reputation: 58

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

Upvotes: 2

Views: 2409

Answers (3)

ferry133
ferry133

Reputation: 1

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);
}

Upvotes: 0

Herr Derb
Herr Derb

Reputation: 5387

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();

}

Upvotes: 0

trustin
trustin

Reputation: 12351

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.)

Upvotes: 1

Related Questions