View Javadoc
1   /*
2    * Copyright 2016 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.kqueue;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.buffer.ByteBufUtil;
21  import io.netty.buffer.Unpooled;
22  import io.netty.channel.AbstractChannel;
23  import io.netty.channel.Channel;
24  import io.netty.channel.ChannelConfig;
25  import io.netty.channel.ChannelException;
26  import io.netty.channel.ChannelFuture;
27  import io.netty.channel.ChannelFutureListener;
28  import io.netty.channel.ChannelMetadata;
29  import io.netty.channel.ChannelOutboundBuffer;
30  import io.netty.channel.ChannelPromise;
31  import io.netty.channel.ConnectTimeoutException;
32  import io.netty.channel.EventLoop;
33  import io.netty.channel.IoEvent;
34  import io.netty.channel.IoEventLoop;
35  import io.netty.channel.IoRegistration;
36  import io.netty.channel.RecvByteBufAllocator;
37  import io.netty.channel.socket.ChannelInputShutdownEvent;
38  import io.netty.channel.socket.ChannelInputShutdownReadComplete;
39  import io.netty.channel.socket.SocketChannelConfig;
40  import io.netty.channel.unix.FileDescriptor;
41  import io.netty.channel.unix.UnixChannel;
42  import io.netty.util.ReferenceCountUtil;
43  import io.netty.util.concurrent.Future;
44  import io.netty.util.internal.UnstableApi;
45  
46  import java.io.IOException;
47  import java.net.ConnectException;
48  import java.net.InetSocketAddress;
49  import java.net.SocketAddress;
50  import java.nio.ByteBuffer;
51  import java.nio.channels.AlreadyConnectedException;
52  import java.nio.channels.ConnectionPendingException;
53  import java.nio.channels.NotYetConnectedException;
54  import java.nio.channels.UnresolvedAddressException;
55  import java.util.concurrent.TimeUnit;
56  
57  import static io.netty.channel.internal.ChannelUtils.WRITE_STATUS_SNDBUF_FULL;
58  import static io.netty.channel.unix.UnixChannelUtil.computeRemoteAddr;
59  import static io.netty.util.internal.ObjectUtil.checkNotNull;
60  
61  abstract class AbstractKQueueChannel extends AbstractChannel implements UnixChannel {
62  
63      private static final ChannelMetadata METADATA = new ChannelMetadata(false);
64      /**
65       * The future of the current connection attempt.  If not null, subsequent
66       * connection attempts will fail.
67       */
68      private ChannelPromise connectPromise;
69      private Future<?> connectTimeoutFuture;
70      private SocketAddress requestedRemoteAddress;
71  
72      // The initial data that was sent by a TCP_FASTOPEN_CONNECT. Its bytes are only removed from the
73      // ChannelOutboundBuffer once the connect is complete, so listeners of the initial write see an active channel.
74      private ChannelOutboundBuffer fastOpenOutboundBuffer;
75      private Object fastOpenData;
76      private long fastOpenBytes;
77  
78      final BsdSocket socket;
79      private IoRegistration registration;
80      private boolean readFilterEnabled;
81      private boolean writeFilterEnabled;
82  
83      boolean readReadyRunnablePending;
84      boolean inputClosedSeenErrorOnRead;
85      protected volatile boolean active;
86      private volatile SocketAddress local;
87      private volatile SocketAddress remote;
88  
89      AbstractKQueueChannel(Channel parent, BsdSocket fd, boolean active) {
90          super(parent);
91          socket = checkNotNull(fd, "fd");
92          this.active = active;
93          if (active) {
94              // Directly cache the remote and local addresses
95              // See https://github.com/netty/netty/issues/2359
96              local = fd.localAddress();
97              remote = fd.remoteAddress();
98          }
99      }
100 
101     AbstractKQueueChannel(Channel parent, BsdSocket fd, SocketAddress remote) {
102         super(parent);
103         socket = checkNotNull(fd, "fd");
104         active = true;
105         // Directly cache the remote and local addresses
106         // See https://github.com/netty/netty/issues/2359
107         this.remote = remote;
108         local = fd.localAddress();
109     }
110 
111     @Override
112     protected boolean isCompatible(EventLoop loop) {
113         return loop instanceof IoEventLoop && ((IoEventLoop) loop).isCompatible(AbstractKQueueUnsafe.class);
114     }
115 
116     static boolean isSoErrorZero(BsdSocket fd) {
117         try {
118             return fd.getSoError() == 0;
119         } catch (IOException e) {
120             throw new ChannelException(e);
121         }
122     }
123 
124     protected final IoRegistration registration() {
125         assert registration != null;
126         return registration;
127     }
128 
129     @Override
130     public final FileDescriptor fd() {
131         return socket;
132     }
133 
134     @Override
135     public boolean isActive() {
136         return active;
137     }
138 
139     @Override
140     public ChannelMetadata metadata() {
141         return METADATA;
142     }
143 
144     /**
145      * Remember that {@code bytes} of {@code data}, the current message of {@code in}, were sent by a
146      * TCP_FASTOPEN_CONNECT.
147      */
148     final void fastOpenDataSent(ChannelOutboundBuffer in, Object data, long bytes) {
149         fastOpenOutboundBuffer = in;
150         fastOpenData = data;
151         fastOpenBytes = bytes;
152     }
153 
154     /**
155      * Removes the bytes that were sent by a TCP_FASTOPEN_CONNECT from the {@link ChannelOutboundBuffer} if the
156      * channel was connected, and forgets about them otherwise. Must be called before the
157      * {@link ChannelOutboundBuffer} is written or its messages are failed.
158      *
159      * @return {@code true} if bytes were removed.
160      */
161     final boolean removeFastOpenData() {
162         ChannelOutboundBuffer in = fastOpenOutboundBuffer;
163         if (in == null) {
164             return false;
165         }
166         Object data = fastOpenData;
167         long bytes = fastOpenBytes;
168         fastOpenOutboundBuffer = null;
169         fastOpenData = null;
170         fastOpenBytes = 0;
171         if (!isActive() || in.current() != data) {
172             return false;
173         }
174         in.removeBytes(bytes);
175         return true;
176     }
177 
178     @Override
179     protected void doClose() throws Exception {
180         // Clear kqueue registrations *before* we close the file descriptor.
181         // Otherwise, the filter removals might hit a reused file descriptor.
182         doDeregister();
183 
184         active = false;
185         // Even if we allow half closed sockets we should give up on reading. Otherwise we may allow a read attempt on a
186         // socket which has not even been connected yet. This has been observed to block during unit tests.
187         inputClosedSeenErrorOnRead = true;
188         socket.close();
189     }
190 
191     @Override
192     protected void doDisconnect() throws Exception {
193         doClose();
194     }
195 
196     void resetCachedAddresses() {
197         local = socket.localAddress();
198         remote = socket.remoteAddress();
199     }
200 
201     @Override
202     public boolean isOpen() {
203         return socket.isOpen();
204     }
205 
206     @Override
207     protected void doDeregister() throws Exception {
208         IoRegistration registration = this.registration;
209         if (registration != null) {
210             // As unregisteredFilters() may have not been called because isOpen() returned false we just set both
211             // filters to false, to ensure a consistent state in all cases.
212             // Make sure we unregister our filters from kqueue!
213             readFilter(false);
214             writeFilter(false);
215             clearRdHup0();
216 
217             registration.cancel();
218             this.registration = null;
219         }
220     }
221 
222     private void clearRdHup0() {
223         submit(KQueueIoOps.newOps(Native.EVFILT_SOCK, Native.EV_DELETE_DISABLE, Native.NOTE_RDHUP));
224     }
225 
226     private void submit(KQueueIoOps ops) {
227         if (!isOpen()) {
228             // If the channel was closed in the meantime we should just drop the ops as otherwise we might end up
229             // affected another channel that re-used the fd.
230             return;
231         }
232         try {
233             registration.submit(ops);
234         } catch (Exception e) {
235             throw new ChannelException(e);
236         }
237     }
238 
239     @Override
240     protected final void doBeginRead() throws Exception {
241         // Channel.read() or ChannelHandlerContext.read() was called
242         final AbstractKQueueUnsafe unsafe = (AbstractKQueueUnsafe) unsafe();
243         unsafe.readPending = true;
244 
245         // We must set the read flag here as it is possible the user didn't read in the last read loop, the
246         // executeReadReadyRunnable could read nothing, and if the user doesn't explicitly call read they will
247         // never get data after this.
248         readFilter(true);
249     }
250 
251     @Override
252     protected void doRegister(ChannelPromise promise) {
253         ((IoEventLoop) eventLoop()).register((AbstractKQueueUnsafe) unsafe()).addListener(f -> {
254             if (f.isSuccess()) {
255                 this.registration = (IoRegistration) f.getNow();
256                 // Just in case the previous EventLoop was shutdown abruptly, or an event is still pending on the old
257                 // EventLoop make sure the readReadyRunnablePending variable is reset so we will be able to execute
258                 // the Runnable on the new EventLoop.
259                 readReadyRunnablePending = false;
260 
261                 submit(KQueueIoOps.newOps(Native.EVFILT_SOCK, Native.EV_ADD, Native.NOTE_RDHUP));
262 
263                 // Add the write event first so we get notified of connection refused on the client side!
264                 if (writeFilterEnabled) {
265                     submit(Native.WRITE_ENABLED_OPS);
266                 }
267                 if (readFilterEnabled) {
268                     submit(Native.READ_ENABLED_OPS);
269                 }
270                 promise.setSuccess();
271             } else {
272                 promise.setFailure(f.cause());
273             }
274         });
275     }
276 
277     @Override
278     protected abstract AbstractKQueueUnsafe newUnsafe();
279 
280     @Override
281     public abstract KQueueChannelConfig config();
282 
283     /**
284      * Returns an off-heap copy of the specified {@link ByteBuf}, and releases the original one.
285      */
286     protected final ByteBuf newDirectBuffer(ByteBuf buf) {
287         return newDirectBuffer(buf, buf);
288     }
289 
290     /**
291      * Returns an off-heap copy of the specified {@link ByteBuf}, and releases the specified holder.
292      * The caller must ensure that the holder releases the original {@link ByteBuf} when the holder is released by
293      * this method.
294      */
295     protected final ByteBuf newDirectBuffer(Object holder, ByteBuf buf) {
296         final int readableBytes = buf.readableBytes();
297         if (readableBytes == 0) {
298             ReferenceCountUtil.release(holder);
299             return Unpooled.EMPTY_BUFFER;
300         }
301 
302         final ByteBufAllocator alloc = alloc();
303         if (alloc.isDirectBufferPooled()) {
304             return newDirectBuffer0(holder, buf, alloc, readableBytes);
305         }
306 
307         final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
308         if (directBuf == null) {
309             return newDirectBuffer0(holder, buf, alloc, readableBytes);
310         }
311 
312         directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
313         ReferenceCountUtil.safeRelease(holder);
314         return directBuf;
315     }
316 
317     private static ByteBuf newDirectBuffer0(Object holder, ByteBuf buf, ByteBufAllocator alloc, int capacity) {
318         final ByteBuf directBuf = alloc.directBuffer(capacity);
319         directBuf.writeBytes(buf, buf.readerIndex(), capacity);
320         ReferenceCountUtil.safeRelease(holder);
321         return directBuf;
322     }
323 
324     protected static void checkResolvable(InetSocketAddress addr) {
325         if (addr.isUnresolved()) {
326             throw new UnresolvedAddressException();
327         }
328     }
329 
330     /**
331      * Read bytes into the given {@link ByteBuf} and return the amount.
332      */
333     protected final int doReadBytes(ByteBuf byteBuf) throws Exception {
334         int writerIndex = byteBuf.writerIndex();
335         int localReadAmount;
336         unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes());
337         if (byteBuf.hasMemoryAddress()) {
338             localReadAmount = socket.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
339         } else {
340             ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes());
341             localReadAmount = socket.read(buf, buf.position(), buf.limit());
342         }
343         if (localReadAmount > 0) {
344             byteBuf.writerIndex(writerIndex + localReadAmount);
345         }
346         return localReadAmount;
347     }
348 
349     protected final int doWriteBytes(ChannelOutboundBuffer in, ByteBuf buf) throws Exception {
350         if (buf.hasMemoryAddress()) {
351             int localFlushedAmount = socket.writeAddress(buf.memoryAddress(), buf.readerIndex(), buf.writerIndex());
352             if (localFlushedAmount > 0) {
353                 in.removeBytes(localFlushedAmount);
354                 return 1;
355             }
356         } else {
357             final ByteBuffer nioBuf = buf.nioBufferCount() == 1?
358                     buf.internalNioBuffer(buf.readerIndex(), buf.readableBytes()) : buf.nioBuffer();
359             int localFlushedAmount = socket.write(nioBuf, nioBuf.position(), nioBuf.limit());
360             if (localFlushedAmount > 0) {
361                 nioBuf.position(nioBuf.position() + localFlushedAmount);
362                 in.removeBytes(localFlushedAmount);
363                 return 1;
364             }
365         }
366         return WRITE_STATUS_SNDBUF_FULL;
367     }
368 
369     final boolean shouldBreakReadReady(ChannelConfig config) {
370         return socket.isInputShutdown() && (inputClosedSeenErrorOnRead || !isAllowHalfClosure(config));
371     }
372 
373     private static boolean isAllowHalfClosure(ChannelConfig config) {
374         if (config instanceof KQueueDomainSocketChannelConfig) {
375             return ((KQueueDomainSocketChannelConfig) config).isAllowHalfClosure();
376         }
377 
378         return config instanceof SocketChannelConfig &&
379                 ((SocketChannelConfig) config).isAllowHalfClosure();
380     }
381 
382     final void clearReadFilter() {
383         // Only clear if registered with an EventLoop as otherwise
384         if (isRegistered()) {
385             final EventLoop loop = eventLoop();
386             final AbstractKQueueUnsafe unsafe = (AbstractKQueueUnsafe) unsafe();
387             if (loop.inEventLoop()) {
388                 unsafe.clearReadFilter0();
389             } else {
390                 // schedule a task to clear the EPOLLIN as it is not safe to modify it directly
391                 loop.execute(new Runnable() {
392                     @Override
393                     public void run() {
394                         if (!unsafe.readPending && !config().isAutoRead()) {
395                             // Still no read triggered so clear it now
396                             unsafe.clearReadFilter0();
397                         }
398                     }
399                 });
400             }
401         } else  {
402             // The EventLoop is not registered atm so just update the flags so the correct value
403             // will be used once the channel is registered
404             readFilterEnabled = false;
405         }
406     }
407 
408     void readFilter(boolean readFilterEnabled) throws IOException {
409         if (this.readFilterEnabled != readFilterEnabled) {
410             this.readFilterEnabled = readFilterEnabled;
411             submit(readFilterEnabled ? Native.READ_ENABLED_OPS : Native.READ_DISABLED_OPS);
412         }
413     }
414 
415     void writeFilter(boolean writeFilterEnabled) throws IOException {
416         if (this.writeFilterEnabled != writeFilterEnabled) {
417             this.writeFilterEnabled = writeFilterEnabled;
418             submit(writeFilterEnabled ? Native.WRITE_ENABLED_OPS : Native.WRITE_DISABLED_OPS);
419         }
420     }
421 
422     @UnstableApi
423     public abstract class AbstractKQueueUnsafe extends AbstractUnsafe implements KQueueIoHandle {
424         boolean readPending;
425         private KQueueRecvByteAllocatorHandle allocHandle;
426 
427         Channel channel() {
428             return AbstractKQueueChannel.this;
429         }
430 
431         @Override
432         public int ident() {
433             return fd().intValue();
434         }
435 
436         @Override
437         public void close() {
438             close(voidPromise());
439         }
440 
441         @Override
442         public void handle(IoRegistration registration, IoEvent event) {
443             KQueueIoEvent kqueueEvent = (KQueueIoEvent) event;
444             final short filter = kqueueEvent.filter();
445             final short flags = kqueueEvent.flags();
446             final int fflags = kqueueEvent.fflags();
447             final long data = kqueueEvent.data();
448 
449             // First check for EPOLLOUT as we may need to fail the connect ChannelPromise before try
450             // to read from the file descriptor.
451             if (filter == Native.EVFILT_WRITE) {
452                 writeReady();
453             } else if (filter == Native.EVFILT_READ) {
454                 // Check READ before EOF to ensure all data is read before shutting down the input.
455                 KQueueRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
456                 readReady(allocHandle);
457             } else if (filter == Native.EVFILT_SOCK && (fflags & Native.NOTE_RDHUP) != 0) {
458                 readEOF();
459                 return;
460             }
461 
462             // Check if EV_EOF was set, this will notify us for connection-reset in which case
463             // we may close the channel directly or try to read more data depending on the state of the
464             // Channel and also depending on the AbstractKQueueChannel subtype.
465             if ((flags & Native.EV_EOF) != 0) {
466                 readEOF();
467             }
468         }
469 
470         abstract void readReady(KQueueRecvByteAllocatorHandle allocHandle);
471 
472         final boolean shouldStopReading(ChannelConfig config) {
473             // Check if there is a readPending which was not processed yet.
474             // This could be for two reasons:
475             // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method
476             // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method
477             //
478             // See https://github.com/netty/netty/issues/2254
479             return !readPending && !config.isAutoRead();
480         }
481 
482         final boolean failConnectPromise(Throwable cause) {
483             if (connectPromise != null) {
484                 // SO_ERROR has been shown to return 0 on macOS if detect an error via read() and the write filter was
485                 // not set before calling connect. This means finishConnect will not detect any error and would
486                 // successfully complete the connectPromise and update the channel state to active (which is incorrect).
487                 ChannelPromise connectPromise = AbstractKQueueChannel.this.connectPromise;
488                 AbstractKQueueChannel.this.connectPromise = null;
489                 if (connectPromise.tryFailure((cause instanceof ConnectException) ? cause
490                                 : new ConnectException("failed to connect").initCause(cause))) {
491                     closeIfClosed();
492                     return true;
493                 }
494             }
495             return false;
496         }
497 
498         private void writeReady() {
499             if (connectPromise != null) {
500                 // pending connect which is now complete so handle it.
501                 finishConnect();
502             } else if (!socket.isOutputShutdown()) {
503                 // directly call super.flush0() to force a flush now
504                 super.flush0();
505             }
506         }
507 
508         /**
509          * Shutdown the input side of the channel.
510          */
511         void shutdownInput(boolean readEOF) {
512             // We need to take special care of calling finishConnect() if readEOF is true and we not
513             // fulfilled the connectPromise yet. If we fail to do so the connectPromise will be failed
514             // with a ClosedChannelException as a close() will happen and so the FD is closed before we
515             // have a chance to call finishConnect() later on. Calling finishConnect() here will ensure
516             // we observe the correct exception in case of a connect failure.
517             if (readEOF && connectPromise != null) {
518                 finishConnect();
519             }
520             if (!socket.isInputShutdown()) {
521                 if (isAllowHalfClosure(config())) {
522                     try {
523                         socket.shutdown(true, false);
524                     } catch (IOException ignored) {
525                         // We attempted to shutdown and failed, which means the input has already effectively been
526                         // shutdown.
527                         fireEventAndClose(ChannelInputShutdownEvent.INSTANCE);
528                         return;
529                     } catch (NotYetConnectedException ignore) {
530                         // We attempted to shutdown and failed, which means the input has already effectively been
531                         // shutdown.
532                     }
533                     if (shouldStopReading(config())) {
534                         clearReadFilter0();
535                     }
536                     pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
537                 } else {
538                     close(voidPromise());
539                     return;
540                 }
541             }
542             if (!readEOF && !inputClosedSeenErrorOnRead) {
543                 inputClosedSeenErrorOnRead = true;
544                 pipeline().fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE);
545             }
546         }
547 
548         private void readEOF() {
549             // This must happen before we attempt to read. This will ensure reading continues until an error occurs.
550             final KQueueRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
551             allocHandle.readEOF();
552 
553             // kqueue reports each filter as its own event, so the EOF may be processed before the EVFILT_WRITE event
554             // that completes the connect. Finish the connect first so the data that the remote peer sent before it
555             // closed the connection is read, and not dropped by shutting down the input.
556             if (connectPromise != null) {
557                 finishConnect();
558             }
559 
560             if (isActive()) {
561                 // If it is still active, we need to call readReady as otherwise we may miss to
562                 // read pending data from the underlying file descriptor.
563                 // See https://github.com/netty/netty/issues/3709
564                 readReady(allocHandle);
565             } else {
566                 // Just to be safe make sure the input marked as closed.
567                 shutdownInput(true);
568             }
569 
570             // Clear the RDHUP flag to prevent continuously getting woken up on this event.
571             clearRdHup0();
572         }
573 
574         @Override
575         public KQueueRecvByteAllocatorHandle recvBufAllocHandle() {
576             if (allocHandle == null) {
577                 allocHandle = new KQueueRecvByteAllocatorHandle(
578                         (RecvByteBufAllocator.ExtendedHandle) super.recvBufAllocHandle());
579             }
580             return allocHandle;
581         }
582 
583         @Override
584         protected final void flush0() {
585             // Flush immediately only when there's no pending flush.
586             // If there's a pending flush operation, event loop will call forceFlush() later,
587             // and thus there's no need to call it now.
588             if (!writeFilterEnabled) {
589                 super.flush0();
590             }
591         }
592 
593         protected final void clearReadFilter0() {
594             assert eventLoop().inEventLoop();
595             try {
596                 readPending = false;
597                 readFilter(false);
598             } catch (IOException e) {
599                 // When this happens there is something completely wrong with either the filedescriptor or epoll,
600                 // so fire the exception through the pipeline and close the Channel.
601                 pipeline().fireExceptionCaught(e);
602                 unsafe().close(unsafe().voidPromise());
603             }
604         }
605 
606         private void fireEventAndClose(Object evt) {
607             pipeline().fireUserEventTriggered(evt);
608             close(voidPromise());
609         }
610 
611         @Override
612         public void connect(
613                 final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
614             // Don't mark the connect promise as uncancellable as in fact we can cancel it as it is using
615             // non-blocking io.
616             if (promise.isDone() || !ensureOpen(promise)) {
617                 return;
618             }
619 
620             try {
621                 if (connectPromise != null) {
622                     throw new ConnectionPendingException();
623                 }
624 
625                 boolean wasActive = isActive();
626                 if (doConnect(remoteAddress, localAddress)) {
627                     fulfillConnectPromise(promise, wasActive);
628                 } else {
629                     connectPromise = promise;
630                     requestedRemoteAddress = remoteAddress;
631 
632                     // Schedule connect timeout.
633                     final int connectTimeoutMillis = config().getConnectTimeoutMillis();
634                     if (connectTimeoutMillis > 0) {
635                         connectTimeoutFuture = eventLoop().schedule(new Runnable() {
636                             @Override
637                             public void run() {
638                                 ChannelPromise connectPromise = AbstractKQueueChannel.this.connectPromise;
639                                 if (connectPromise != null && !connectPromise.isDone()
640                                         && connectPromise.tryFailure(new ConnectTimeoutException(
641                                                 "connection timed out after " + connectTimeoutMillis + " ms: " +
642                                                         remoteAddress))) {
643                                     close(voidPromise());
644                                 }
645                             }
646                         }, connectTimeoutMillis, TimeUnit.MILLISECONDS);
647                     }
648 
649                     promise.addListener(new ChannelFutureListener() {
650                         @Override
651                         public void operationComplete(ChannelFuture future) {
652                             // If the connect future is cancelled we also cancel the timeout and close the
653                             // underlying socket.
654                             if (future.isCancelled()) {
655                                 if (connectTimeoutFuture != null) {
656                                     connectTimeoutFuture.cancel(false);
657                                 }
658                                 connectPromise = null;
659                                 close(voidPromise());
660                             }
661                         }
662                     });
663                 }
664             } catch (Throwable t) {
665                 closeIfClosed();
666                 promise.tryFailure(annotateConnectException(t, remoteAddress));
667             }
668         }
669 
670         private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
671             if (promise == null) {
672                 // Closed via cancellation and the promise has been notified already.
673                 return;
674             }
675             active = true;
676 
677             // Get the state as trySuccess() may trigger an ChannelFutureListener that will close the Channel.
678             // We still need to ensure we call fireChannelActive() in this case.
679             boolean active = isActive();
680 
681             // trySuccess() will return false if a user cancelled the connection attempt.
682             boolean promiseSet = promise.trySuccess();
683 
684             // Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
685             // because what happened is what happened.
686             if (!wasActive && active) {
687                 pipeline().fireChannelActive();
688             }
689 
690             // The connect is complete, so remove the initial data that was sent by TCP_FASTOPEN_CONNECT now and
691             // write what is left of it.
692             if (removeFastOpenData()) {
693                 flush0();
694             }
695 
696             // If a user cancelled the connection attempt, close the channel, which is followed by channelInactive().
697             if (!promiseSet) {
698                 close(voidPromise());
699             }
700         }
701 
702         private void fulfillConnectPromise(ChannelPromise promise, Throwable cause) {
703             // The connect failed, so we don't know if the initial data that was sent by TCP_FASTOPEN_CONNECT
704             // was received.
705             removeFastOpenData();
706             if (promise == null) {
707                 // Closed via cancellation and the promise has been notified already.
708                 return;
709             }
710 
711             // Use tryFailure() instead of setFailure() to avoid the race against cancel().
712             promise.tryFailure(cause);
713             closeIfClosed();
714         }
715 
716         private void finishConnect() {
717             // Note this method is invoked by the event loop only if the connection attempt was
718             // neither cancelled nor timed out.
719 
720             assert eventLoop().inEventLoop();
721 
722             boolean connectStillInProgress = false;
723             try {
724                 boolean wasActive = isActive();
725                 if (!doFinishConnect()) {
726                     connectStillInProgress = true;
727                     return;
728                 }
729                 fulfillConnectPromise(connectPromise, wasActive);
730             } catch (Throwable t) {
731                 fulfillConnectPromise(connectPromise, annotateConnectException(t, requestedRemoteAddress));
732             } finally {
733                 if (!connectStillInProgress) {
734                     // Check for null as the connectTimeoutFuture is only created if a connectTimeoutMillis > 0 is used
735                     // See https://github.com/netty/netty/issues/1770
736                     if (connectTimeoutFuture != null) {
737                         connectTimeoutFuture.cancel(false);
738                     }
739                     connectPromise = null;
740                 }
741             }
742         }
743 
744         private boolean doFinishConnect() throws Exception {
745             if (socket.finishConnect()) {
746                 writeFilter(false);
747                 if (requestedRemoteAddress instanceof InetSocketAddress) {
748                     remote = computeRemoteAddr((InetSocketAddress) requestedRemoteAddress, socket.remoteAddress());
749                 }
750                 requestedRemoteAddress = null;
751                 return true;
752             }
753             writeFilter(true);
754             return false;
755         }
756     }
757 
758     @Override
759     protected void doBind(SocketAddress local) throws Exception {
760         if (local instanceof InetSocketAddress) {
761             checkResolvable((InetSocketAddress) local);
762         }
763         socket.bind(local);
764         this.local = socket.localAddress();
765     }
766 
767     /**
768      * Connect to the remote peer
769      */
770     protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
771         if (localAddress instanceof InetSocketAddress) {
772             checkResolvable((InetSocketAddress) localAddress);
773         }
774 
775         InetSocketAddress remoteSocketAddr = remoteAddress instanceof InetSocketAddress
776                 ? (InetSocketAddress) remoteAddress : null;
777         if (remoteSocketAddr != null) {
778             checkResolvable(remoteSocketAddr);
779         }
780 
781         if (remote != null) {
782             // Check if already connected before trying to connect. This is needed as connect(...) will not return -1
783             // and set errno to EISCONN if a previous connect(...) attempt was setting errno to EINPROGRESS and finished
784             // later.
785             throw new AlreadyConnectedException();
786         }
787 
788         if (localAddress != null) {
789             socket.bind(localAddress);
790         }
791 
792         boolean connected = doConnect0(remoteAddress, localAddress);
793         if (connected) {
794             remote = remoteSocketAddr == null?
795                     remoteAddress : computeRemoteAddr(remoteSocketAddr, socket.remoteAddress());
796         }
797         // We always need to set the localAddress even if not connected yet as the bind already took place.
798         //
799         // See https://github.com/netty/netty/issues/3463
800         local = socket.localAddress();
801         return connected;
802     }
803 
804     protected boolean doConnect0(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
805         boolean success = false;
806         try {
807             boolean connected = socket.connect(remoteAddress);
808             if (!connected) {
809                 writeFilter(true);
810             }
811             success = true;
812             return connected;
813         } finally {
814             if (!success) {
815                 doClose();
816             }
817         }
818     }
819 
820     @Override
821     protected SocketAddress localAddress0() {
822         return local;
823     }
824 
825     @Override
826     protected SocketAddress remoteAddress0() {
827         return remote;
828     }
829 }