Reputation: 58
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:
HttpRequestDecoder
(decode from ByteBuf
to HttpRequest
)MyHttpRequestHandler
(do my own checks on the HttpRequest
)HttpRequestEncoder
(encode the HttpRequest
to a ByteBuf
)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
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
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
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