@Test public void testLocalAddressReuse() throws Exception { for (int i = 0; i < 2; i++) { LocalAddress addr = new LocalAddress(LOCAL_ADDR_ID); Bootstrap cb = new Bootstrap(); ServerBootstrap sb = new ServerBootstrap(); cb.eventLoop(new LocalEventLoop()) .channel(new LocalChannel()) .remoteAddress(addr) .handler(new TestHandler()); sb.eventLoop(new LocalEventLoop(), new LocalEventLoop()) .channel(new LocalServerChannel()) .localAddress(addr) .childHandler( new ChannelInitializer<LocalChannel>() { @Override public void initChannel(LocalChannel ch) throws Exception { ch.pipeline().addLast(new TestHandler()); } }); // Start server Channel sc = sb.bind().sync().channel(); // Connect to the server Channel cc = cb.connect().sync().channel(); // Send a message event up the pipeline. cc.pipeline().inboundMessageBuffer().add("Hello, World"); cc.pipeline().fireInboundBufferUpdated(); // Close the channel cc.close().sync(); sb.shutdown(); cb.shutdown(); sc.closeFuture().sync(); Assert.assertTrue( String.format( "Expected null, got channel '%s' for local address '%s'", LocalChannelRegistry.get(addr), addr), LocalChannelRegistry.get(addr) == null); } }
@Override protected void doClose() throws Exception { if (state <= 1) { // Update all internal state before the closeFuture is notified. LocalChannelRegistry.unregister(localAddress); localAddress = null; state = 2; } }
@Override public void connect( final SocketAddress remoteAddress, SocketAddress localAddress, final ChannelPromise promise) { if (!promise.setUncancellable() || !ensureOpen(promise)) { return; } if (state == State.CONNECTED) { Exception cause = new AlreadyConnectedException(); safeSetFailure(promise, cause); pipeline().fireExceptionCaught(cause); return; } if (connectPromise != null) { throw new ConnectionPendingException(); } connectPromise = promise; if (state != State.BOUND) { // Not bound yet and no localAddress specified - get one. if (localAddress == null) { localAddress = new LocalAddress(LocalChannel.this); } } if (localAddress != null) { try { doBind(localAddress); } catch (Throwable t) { safeSetFailure(promise, t); close(voidPromise()); return; } } Channel boundChannel = LocalChannelRegistry.get(remoteAddress); if (!(boundChannel instanceof LocalServerChannel)) { Exception cause = new ChannelException("connection refused"); safeSetFailure(promise, cause); close(voidPromise()); return; } LocalServerChannel serverChannel = (LocalServerChannel) boundChannel; peer = serverChannel.serve(LocalChannel.this); }
@Override protected void doClose() throws Exception { if (state > 2) { // Closed already return; } if (parent() == null) { LocalChannelRegistry.unregister(localAddress); } localAddress = null; state = 3; if (peer.isActive()) { peer.unsafe().close(peer.unsafe().voidFuture()); peer = null; } }
@Override protected void doClose() throws Exception { if (state != State.CLOSED) { // Update all internal state before the closeFuture is notified. if (localAddress != null) { if (parent() == null) { LocalChannelRegistry.unregister(localAddress); } localAddress = null; } state = State.CLOSED; } final LocalChannel peer = this.peer; if (peer != null && peer.isActive()) { // Need to execute the close in the correct EventLoop // See https://github.com/netty/netty/issues/1777 EventLoop eventLoop = peer.eventLoop(); // Also check if the registration was not done yet. In this case we submit the close to the // EventLoop // to make sure it is run after the registration completes. // // See https://github.com/netty/netty/issues/2144 if (eventLoop.inEventLoop() && !registerInProgress) { peer.unsafe().close(unsafe().voidPromise()); } else { peer.eventLoop() .execute( new Runnable() { @Override public void run() { peer.unsafe().close(unsafe().voidPromise()); } }); } this.peer = null; } }
@Override protected void doBind(SocketAddress localAddress) throws Exception { this.localAddress = LocalChannelRegistry.register(this, this.localAddress, localAddress); state = State.BOUND; }
@Override public void connect( final SocketAddress remoteAddress, SocketAddress localAddress, final ChannelFuture future) { if (eventLoop().inEventLoop()) { if (!ensureOpen(future)) { return; } if (state == 2) { Exception cause = new AlreadyConnectedException(); future.setFailure(cause); pipeline().fireExceptionCaught(cause); return; } if (connectFuture != null) { throw new ConnectionPendingException(); } connectFuture = future; if (state != 1) { // Not bound yet and no localAddress specified - get one. if (localAddress == null) { localAddress = new LocalAddress(LocalChannel.this); } } if (localAddress != null) { try { doBind(localAddress); } catch (Throwable t) { future.setFailure(t); pipeline().fireExceptionCaught(t); close(voidFuture()); return; } } Channel boundChannel = LocalChannelRegistry.get(remoteAddress); if (!(boundChannel instanceof LocalServerChannel)) { Exception cause = new ChannelException("connection refused"); future.setFailure(cause); pipeline().fireExceptionCaught(cause); close(voidFuture()); return; } LocalServerChannel serverChannel = (LocalServerChannel) boundChannel; peer = serverChannel.serve(LocalChannel.this); } else { final SocketAddress localAddress0 = localAddress; eventLoop() .execute( new Runnable() { @Override public void run() { connect(remoteAddress, localAddress0, future); } }); } }