package net.tomp2p.message; import java.net.InetAddress; import java.net.InetSocketAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; import net.tomp2p.connection.SignatureFactory; import net.tomp2p.peers.PeerAddress; public class TomP2POutbound extends ChannelOutboundHandlerAdapter { private static final Logger LOG = LoggerFactory.getLogger(TomP2POutbound.class); private final Encoder encoder; private final ByteBufAllocator byteBufAllocator; public TomP2POutbound(SignatureFactory signatureFactory, ByteBufAllocator byteBufAllocator) { this.encoder = new Encoder(signatureFactory); this.byteBufAllocator = byteBufAllocator; } @Override public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception { CompositeByteBuf buf = null; if (!(msg instanceof Message)) { ctx.write(msg, promise); return; } try { boolean done = false; buf = byteBufAllocator.compositeBuffer(); //buf = AlternativeCompositeByteBuf.compBuffer(byteBufAllocator, buf); //null, means create signature, as we did not have created one already done = encoder.write(buf, (Message) msg, null); final Message message = encoder.message(); if (buf.isReadable()) { // this will release the buffer if (ctx.channel() instanceof DatagramChannel) { final PeerAddress recipientAddress; InetSocketAddress recipient; InetSocketAddress sender; if (message.senderSocket() == null) { //in case of a request if (message.recipientRelay() != null) { //in case of sending to a relay (the relayed flag is already set) recipientAddress = message.recipientRelay(); } else if (message.recipientReflected() != null) { //in case we use nat reflection recipientAddress = message.recipientReflected(); } else { recipientAddress = message.recipient(); } recipient = recipientAddress.createUDPSocket(message.sender()); sender = message.sender().createSocket(recipientAddress, 0); } else { //in case of a reply recipient = message.senderSocket(); sender = message.recipientSocket(); } // FIXME quickfix for Android (by Nico) recipient = new InetSocketAddress(InetAddress.getByAddress(recipient.getAddress(). getAddress()), recipient.getPort()); sender = new InetSocketAddress(InetAddress.getByAddress(sender.getAddress().getAddress()), sender.getPort()); DatagramPacket d = new DatagramPacket(buf, recipient, sender); LOG.debug("Send UDP message {}, datagram: {}.", message, d); ctx.writeAndFlush(d, promise); } else { LOG.debug("Send TCP message {} to {}.", message, message.senderSocket()); ctx.writeAndFlush(buf, promise); } if (done) { message.setDone(true); } } else { buf.release(); ctx.write(Unpooled.EMPTY_BUFFER, promise); } buf = null; } catch (Throwable t) { exceptionCaught(ctx, t); } finally { if (buf != null) { buf.release(); } } } @Override public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception { ctx.close(); if (encoder.message() == null) { LOG.error("Exception in encoding when starting.", cause); cause.printStackTrace(); } else if (encoder.message() != null && !encoder.message().isDone()) { LOG.error("Exception in encoding when started.", cause); cause.printStackTrace(); } //if exception is not propagated, we may need to wait for the timeout - check testChangeEntryProtectionKey super.exceptionCaught(ctx, cause); } }