package net.tomp2p.message; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; import io.netty.handler.codec.DecoderException; import io.netty.util.Attribute; import io.netty.util.AttributeKey; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Queue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.tomp2p.connection2.SignatureFactory; import net.tomp2p.futures.FutureProgres; import net.tomp2p.futures.FutureResponse; import net.tomp2p.message.Message2.Content; import net.tomp2p.message.Message2.Type; import net.tomp2p.peers.Number160; import net.tomp2p.peers.PeerAddress; import net.tomp2p.rpc.SimpleBloomFilter; import net.tomp2p.utils.CacheMap; import net.tomp2p.utils.ConcurrentCacheMap; public class TomP2PCumulationTCP extends ChannelInboundHandlerAdapter { private static final Logger LOG = LoggerFactory.getLogger(TomP2PCumulationTCP.class); private final TomP2PDecoder decoder; private ByteBuf cumulation = null; private int lastId = 0; public TomP2PCumulationTCP(final SignatureFactory signatureFactory) { decoder = new TomP2PDecoder(signatureFactory); } @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { if (!(msg instanceof ByteBuf)) { ctx.fireChannelRead(msg); return; } final ByteBuf buf = (ByteBuf) msg; final InetSocketAddress sender = (InetSocketAddress) ctx.channel().remoteAddress(); try { if (cumulation == null) { cumulation = buf; try { decoding(ctx, sender); } catch (Throwable t) { t.printStackTrace(); } finally { if (!cumulation.isReadable()) { cumulation.release(); cumulation = null; } } } else { try { if (cumulation.writerIndex() > cumulation.maxCapacity() - buf.readableBytes()) { ByteBuf oldCumulation = cumulation; cumulation = ctx.alloc().buffer(oldCumulation.readableBytes() + buf.readableBytes()); cumulation.writeBytes(oldCumulation); oldCumulation.release(); } cumulation.writeBytes(buf); decoding(ctx, sender); } finally { if (!cumulation.isReadable()) { cumulation.release(); cumulation = null; } else { cumulation.discardSomeReadBytes(); } buf.release(); } } } catch (Throwable t) { throw new DecoderException(t); } } private void decoding(final ChannelHandlerContext ctx, final InetSocketAddress sender) { boolean finished = true; boolean moreData = true; while (finished && moreData) { finished = decoder.decode(ctx, cumulation, (InetSocketAddress) ctx.channel().localAddress(), sender); if (finished) { lastId = decoder.message().getMessageId(); moreData = cumulation.readableBytes() > 0; ctx.fireChannelRead(decoder.prepareFinish()); } else { // this id was the same as the last and the last message already finished the parsing. So this message // is finished as well although it may send only partial data. if (lastId == decoder.message().getMessageId()) { finished = true; moreData = cumulation.readableBytes() > 0; ctx.fireChannelRead(decoder.prepareFinish()); } else if (decoder.message().isStreaming()) { ctx.fireChannelRead(decoder.message()); } } } } @Override public void channelInactive(final ChannelHandlerContext ctx) throws Exception { final InetSocketAddress sender = (InetSocketAddress) ctx.channel().remoteAddress(); try { if (cumulation != null) { decoding(ctx, sender); } } catch (Throwable t) { throw new DecoderException(t); } finally { if (cumulation != null) { cumulation.release(); cumulation = null; } ctx.fireChannelInactive(); } } @Override public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { Message2 msg = decoder.message(); // don't use getLocalizedMessage() - // http://stackoverflow.com/questions/8699521/any-way-to-ignore-only-connection-reset-by-peer-ioexceptions if (cause.getMessage().equals("Connection reset by peer")) { return; // ignore } else if(cause.getMessage().endsWith("An existing connection was forcibly closed by the remote host")){ //with windows we see the following message return; // ignore } if (msg == null && decoder.lastContent() == null) { LOG.error("exception in decoding TCP, not started decoding", cause); cause.printStackTrace(); } else if (msg != null && !msg.isDone()) { LOG.error("exception in decoding TCP, decoding started", cause); cause.printStackTrace(); } } }