View Javadoc
1   /*
2    * Copyright 2020 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.handler.codec.quic;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.buffer.Unpooled;
21  import io.netty.channel.Channel;
22  import io.netty.channel.ChannelFuture;
23  import io.netty.channel.ChannelId;
24  import io.netty.channel.ChannelMetadata;
25  import io.netty.channel.ChannelOutboundBuffer;
26  import io.netty.channel.ChannelPipeline;
27  import io.netty.channel.ChannelPromise;
28  import io.netty.channel.DefaultChannelId;
29  import io.netty.channel.DefaultChannelPipeline;
30  import io.netty.channel.EventLoop;
31  import io.netty.channel.PendingWriteQueue;
32  import io.netty.channel.RecvByteBufAllocator;
33  import io.netty.channel.VoidChannelPromise;
34  import io.netty.channel.socket.ChannelInputShutdownEvent;
35  import io.netty.channel.socket.ChannelInputShutdownReadComplete;
36  import io.netty.channel.socket.ChannelOutputShutdownException;
37  import io.netty.util.DefaultAttributeMap;
38  import io.netty.util.ReferenceCountUtil;
39  import io.netty.util.concurrent.PromiseNotifier;
40  import io.netty.util.internal.StringUtil;
41  import io.netty.util.internal.logging.InternalLogger;
42  import io.netty.util.internal.logging.InternalLoggerFactory;
43  import org.jetbrains.annotations.Nullable;
44  
45  import java.net.SocketAddress;
46  import java.nio.channels.ClosedChannelException;
47  import java.util.concurrent.RejectedExecutionException;
48  
49  /**
50   * {@link QuicStreamChannel} implementation that uses <a href="https://github.com/cloudflare/quiche">quiche</a>.
51   */
52  final class QuicheQuicStreamChannel extends DefaultAttributeMap implements QuicStreamChannel {
53      private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
54      private static final InternalLogger LOGGER = InternalLoggerFactory.getInstance(QuicheQuicStreamChannel.class);
55      private final QuicheQuicChannel parent;
56      private final ChannelId id;
57      private final ChannelPipeline pipeline;
58      private final QuicStreamChannelUnsafe unsafe;
59      private final ChannelPromise closePromise;
60      private final PendingWriteQueue queue;
61  
62      private final QuicStreamChannelConfig config;
63      private final QuicStreamAddress address;
64  
65      private boolean readable;
66      private boolean readPending;
67      private boolean inRecv;
68      private boolean inWriteQueued;
69      private boolean finReceived;
70      private boolean finSent;
71  
72      private volatile boolean registered;
73      private volatile boolean writable = true;
74      private volatile boolean active = true;
75      private volatile boolean inputShutdown;
76      private volatile boolean outputShutdown;
77      private volatile QuicStreamPriority priority;
78      private volatile long capacity;
79  
80      QuicheQuicStreamChannel(QuicheQuicChannel parent, long streamId) {
81          this.parent = parent;
82          this.id = DefaultChannelId.newInstance();
83          unsafe = new QuicStreamChannelUnsafe();
84          this.pipeline = new DefaultChannelPipeline(this) {
85              // TODO: add some overrides maybe ?
86          };
87          config = new QuicheQuicStreamChannelConfig(this);
88          this.address = new QuicStreamAddress(streamId);
89          this.closePromise = newPromise();
90          queue = new PendingWriteQueue(this);
91          // Local created unidirectional streams have the input shutdown by spec. There will never be any data for
92          // these to be read.
93          if (parent.streamType(streamId) == QuicStreamType.UNIDIRECTIONAL && parent.isStreamLocalCreated(streamId)) {
94              inputShutdown = true;
95          }
96      }
97  
98      @Override
99      public QuicStreamAddress localAddress() {
100         return address;
101     }
102 
103     @Override
104     public QuicStreamAddress remoteAddress() {
105         return address;
106     }
107 
108     @Override
109     public boolean isLocalCreated() {
110         return parent().isStreamLocalCreated(streamId());
111     }
112 
113     @Override
114     public QuicStreamType type() {
115         return parent().streamType(streamId());
116     }
117 
118     @Override
119     public long streamId() {
120         return address.streamId();
121     }
122 
123     @Override
124     public QuicStreamPriority priority() {
125         return priority;
126     }
127 
128     @Override
129     public ChannelFuture updatePriority(QuicStreamPriority priority, ChannelPromise promise) {
130         if (eventLoop().inEventLoop()) {
131             updatePriority0(priority, promise);
132         } else {
133             eventLoop().execute(() -> updatePriority0(priority, promise));
134         }
135         return promise;
136     }
137 
138     private void updatePriority0(QuicStreamPriority priority, ChannelPromise promise) {
139         assert eventLoop().inEventLoop();
140         if (!promise.setUncancellable()) {
141             return;
142         }
143         try {
144             parent().streamPriority(streamId(), (byte) priority.urgency(), priority.isIncremental());
145         } catch (Throwable cause) {
146             promise.setFailure(cause);
147             return;
148         }
149         this.priority = priority;
150         promise.setSuccess();
151     }
152 
153     @Override
154     public boolean isInputShutdown() {
155         return inputShutdown;
156     }
157 
158     @Override
159     public ChannelFuture shutdownOutput(ChannelPromise promise) {
160         if (eventLoop().inEventLoop()) {
161             shutdownOutput0(promise);
162         } else {
163             eventLoop().execute(() -> shutdownOutput0(promise));
164         }
165         return promise;
166     }
167 
168     private void shutdownOutput0(ChannelPromise promise) {
169         assert eventLoop().inEventLoop();
170         if (!promise.setUncancellable()) {
171             return;
172         }
173         outputShutdown = true;
174         unsafe.writeWithoutCheckChannelState(QuicStreamFrame.EMPTY_FIN, promise);
175         unsafe.flush();
176     }
177 
178     @Override
179     public ChannelFuture shutdownInput(int error, ChannelPromise promise) {
180         if (eventLoop().inEventLoop()) {
181             shutdownInput0(error, promise);
182         } else {
183             eventLoop().execute(() -> shutdownInput0(error, promise));
184         }
185         return promise;
186     }
187 
188     @Override
189     public ChannelFuture shutdownOutput(int error, ChannelPromise promise) {
190         if (eventLoop().inEventLoop()) {
191             shutdownOutput0(error, promise);
192         } else {
193             eventLoop().execute(() -> shutdownOutput0(error, promise));
194         }
195         return promise;
196     }
197 
198     @Override
199     public QuicheQuicChannel parent() {
200         return parent;
201     }
202 
203     private void shutdownInput0(int err, ChannelPromise channelPromise) {
204         assert eventLoop().inEventLoop();
205         if (!channelPromise.setUncancellable()) {
206             return;
207         }
208         inputShutdown = true;
209         parent().streamShutdown(streamId(), true, false, err, channelPromise);
210         closeIfDone();
211     }
212 
213     @Override
214     public boolean isOutputShutdown() {
215         return outputShutdown;
216     }
217 
218     private void shutdownOutput0(int error, ChannelPromise channelPromise) {
219         assert eventLoop().inEventLoop();
220         if (!channelPromise.setUncancellable()) {
221             return;
222         }
223         parent().streamShutdown(streamId(), false, true, error, channelPromise);
224         outputShutdown = true;
225         closeIfDone();
226     }
227 
228     @Override
229     public boolean isShutdown() {
230         return outputShutdown && inputShutdown;
231     }
232 
233     @Override
234     public ChannelFuture shutdown(ChannelPromise channelPromise) {
235         if (eventLoop().inEventLoop()) {
236             shutdown0(channelPromise);
237         } else {
238             eventLoop().execute(() -> shutdown0(channelPromise));
239         }
240         return channelPromise;
241     }
242 
243     private void shutdown0(ChannelPromise promise) {
244         assert eventLoop().inEventLoop();
245         if (!promise.setUncancellable()) {
246             return;
247         }
248         inputShutdown = true;
249         outputShutdown = true;
250         unsafe.writeWithoutCheckChannelState(QuicStreamFrame.EMPTY_FIN, unsafe.voidPromise());
251         unsafe.flush();
252         parent().streamShutdown(streamId(), true, false, 0, promise);
253         closeIfDone();
254     }
255 
256     @Override
257     public ChannelFuture shutdown(int error, ChannelPromise promise) {
258         if (eventLoop().inEventLoop()) {
259             shutdown0(error, promise);
260         } else {
261             eventLoop().execute(() -> shutdown0(error, promise));
262         }
263         return promise;
264     }
265 
266     private void shutdown0(int error, ChannelPromise channelPromise) {
267         assert eventLoop().inEventLoop();
268         if (!channelPromise.setUncancellable()) {
269             return;
270         }
271         inputShutdown = true;
272         outputShutdown = true;
273         parent().streamShutdown(streamId(), true, true, error, channelPromise);
274         closeIfDone();
275     }
276 
277     private void sendFinIfNeeded() throws Exception {
278         if (!finSent) {
279             finSent = true;
280             parent().streamSendFin(streamId());
281         }
282     }
283 
284     private void closeIfDone() {
285         if (finSent && (finReceived || type() == QuicStreamType.UNIDIRECTIONAL && isLocalCreated())) {
286             unsafe().close(unsafe().voidPromise());
287         }
288     }
289 
290     private void removeStreamFromParent() {
291         if (!active && finReceived) {
292             parent().streamClosed(streamId());
293             inputShutdown = true;
294             outputShutdown = true;
295         }
296     }
297 
298     @Override
299     public QuicStreamChannel flush() {
300         pipeline.flush();
301         return this;
302     }
303 
304     @Override
305     public QuicStreamChannel read() {
306         pipeline.read();
307         return this;
308     }
309 
310     @Override
311     public QuicStreamChannelConfig config() {
312         return config;
313     }
314 
315     @Override
316     public boolean isOpen() {
317         return active;
318     }
319 
320     @Override
321     public boolean isActive() {
322         return isOpen();
323     }
324 
325     @Override
326     public ChannelMetadata metadata() {
327         return METADATA;
328     }
329 
330     @Override
331     public ChannelId id() {
332         return id;
333     }
334 
335     @Override
336     public EventLoop eventLoop() {
337         return parent.eventLoop();
338     }
339 
340     @Override
341     public boolean isRegistered() {
342         return registered;
343     }
344 
345     @Override
346     public ChannelFuture closeFuture() {
347         return closePromise;
348     }
349 
350     @Override
351     public boolean isWritable() {
352         return writable;
353     }
354 
355     @Override
356     public long bytesBeforeUnwritable() {
357         // Capacity might be negative if the stream was closed.
358         return Math.max(capacity, 0);
359     }
360 
361     @Override
362     public long bytesBeforeWritable() {
363         if (writable) {
364             return 0;
365         }
366         // Just return something positive for now
367         return 8;
368     }
369 
370     @Override
371     public QuicStreamChannelUnsafe unsafe() {
372         return unsafe;
373     }
374 
375     @Override
376     public ChannelPipeline pipeline() {
377         return pipeline;
378     }
379 
380     @Override
381     public ByteBufAllocator alloc() {
382         return config.getAllocator();
383     }
384 
385     @Override
386     public int compareTo(Channel o) {
387         return id.compareTo(o.id());
388     }
389 
390     /**
391      * Returns the ID of this channel.
392      */
393     @Override
394     public int hashCode() {
395         return id.hashCode();
396     }
397 
398     /**
399      * Returns {@code true} if and only if the specified object is identical
400      * with this channel (i.e: {@code this == o}).
401      */
402     @Override
403     public boolean equals(Object o) {
404         return this == o;
405     }
406 
407     @Override
408     public String toString() {
409         return "[id: 0x" + id.asShortText() + ", " + address + "]";
410     }
411 
412     /**
413      * Stream writability changed.
414      */
415     boolean writable(long capacity) {
416         assert eventLoop().inEventLoop();
417         if (capacity < 0) {
418             // If the value is negative its a quiche error.
419             if (capacity != Quiche.QUICHE_ERR_DONE) {
420                 if (!queue.isEmpty()) {
421                     if (capacity == Quiche.QUICHE_ERR_STREAM_STOPPED) {
422                         queue.removeAndFailAll(new ChannelOutputShutdownException("STOP_SENDING frame received"));
423                         // If STOP_SENDING is received we should not close the channel but just fail all queued writes.
424                         return false;
425                     } else {
426                         queue.removeAndFailAll(Quiche.convertToException((int) capacity));
427                     }
428                 } else if (capacity == Quiche.QUICHE_ERR_STREAM_STOPPED) {
429                     // If STOP_SENDING is received we should not close the channel
430                     return false;
431                 }
432                 // IF this error was not QUICHE_ERR_STREAM_STOPPED we should close the channel.
433                 finSent = true;
434                 unsafe().close(unsafe().voidPromise());
435             }
436             return false;
437         }
438         this.capacity = capacity;
439         boolean mayNeedWrite = unsafe().writeQueued();
440         // we need to re-read this.capacity as writeQueued() may update the capacity.
441         updateWritabilityIfNeeded(this.capacity > 0);
442         return mayNeedWrite;
443     }
444 
445     private void updateWritabilityIfNeeded(boolean newWritable) {
446         if (writable != newWritable) {
447             writable = newWritable;
448             pipeline.fireChannelWritabilityChanged();
449         }
450     }
451 
452     /**
453      * Stream is readable.
454      */
455     void readable() {
456         assert eventLoop().inEventLoop();
457         // Mark as readable and if a read is pending execute it. If the channel was closed already, what is left
458         // must be dropped, see QuicStreamChannelUnsafe.recv().
459         readable = true;
460         if (readPending || !active) {
461             unsafe().recv();
462         }
463     }
464 
465     final class QuicStreamChannelUnsafe implements Unsafe {
466 
467         @SuppressWarnings("deprecation")
468         private RecvByteBufAllocator.Handle recvHandle;
469 
470         private final ChannelPromise voidPromise = new VoidChannelPromise(
471                 QuicheQuicStreamChannel.this, false);
472         @Override
473         public void connect(SocketAddress remote, SocketAddress local, ChannelPromise promise) {
474             assert eventLoop().inEventLoop();
475             promise.setFailure(new UnsupportedOperationException());
476         }
477 
478         @SuppressWarnings("deprecation")
479         @Override
480         public RecvByteBufAllocator.Handle recvBufAllocHandle() {
481             if (recvHandle == null) {
482                 recvHandle = config.getRecvByteBufAllocator().newHandle();
483             }
484             return recvHandle;
485         }
486 
487         @Override
488         public SocketAddress localAddress() {
489             return address;
490         }
491 
492         @Override
493         public SocketAddress remoteAddress() {
494             return address;
495         }
496 
497         @Override
498         public void register(EventLoop eventLoop, ChannelPromise promise) {
499             assert eventLoop.inEventLoop();
500             if (!promise.setUncancellable()) {
501                 return;
502             }
503             if (registered) {
504                 promise.setFailure(new IllegalStateException());
505                 return;
506             }
507             if (eventLoop != parent.eventLoop()) {
508                 promise.setFailure(new IllegalArgumentException());
509                 return;
510             }
511             registered = true;
512             promise.setSuccess();
513             pipeline.fireChannelRegistered();
514             pipeline.fireChannelActive();
515         }
516 
517         @Override
518         public void bind(SocketAddress localAddress, ChannelPromise promise) {
519             assert eventLoop().inEventLoop();
520             if (!promise.setUncancellable()) {
521                 return;
522             }
523             promise.setFailure(new UnsupportedOperationException());
524         }
525 
526         @Override
527         public void disconnect(ChannelPromise promise) {
528             assert eventLoop().inEventLoop();
529             close(promise);
530         }
531 
532         @Override
533         public void close(ChannelPromise promise) {
534             close(null, promise);
535         }
536 
537         void close(@Nullable ClosedChannelException writeFailCause, ChannelPromise promise) {
538             assert eventLoop().inEventLoop();
539             if (!promise.setUncancellable()) {
540                 return;
541             }
542             if (!active || closePromise.isDone()) {
543                 if (promise.isVoid()) {
544                     return;
545                 }
546                 closePromise.addListener(new PromiseNotifier<>(promise));
547                 return;
548             }
549             active = false;
550             try {
551                 // Close the channel and fail the queued messages in all cases.
552                 sendFinIfNeeded();
553             } catch (Exception ignore) {
554                 // Just ignore
555             } finally {
556                 if (!queue.isEmpty()) {
557                     // Only fail if the queue is non-empty.
558                     if (writeFailCause == null) {
559                         writeFailCause = new ClosedChannelException();
560                     }
561                     queue.removeAndFailAll(writeFailCause);
562                 }
563 
564                 promise.trySuccess();
565                 closePromise.trySuccess();
566                 if (type() == QuicStreamType.UNIDIRECTIONAL && isLocalCreated()) {
567                     inputShutdown = true;
568                     outputShutdown = true;
569                     // If its an unidirectional stream and was created locally it is safe to close the stream now as
570                     // we will never receive data from the other side.
571                     parent().streamClosed(streamId());
572                 } else {
573                     removeStreamFromParent();
574                 }
575             }
576             if (inWriteQueued) {
577                 invokeLater(() -> deregister(voidPromise(), true));
578             } else {
579                 deregister(voidPromise(), true);
580             }
581             // If we are in recv() already, it drops what is left once the loop is done and the parent flushes after
582             // it processed the received packets.
583             if (readable && !inRecv) {
584                 // Drop what is left to read, see recv().
585                 recv();
586 
587                 // As the stream was readable, and we called recv() ourselves we also need to call
588                 // connectionSendAndFlush(). This is needed as recv() consumes data and so a window update frame
589                 // might be produced. If close() was not triggered while processing received packets (for example
590                 // by a timeout or a cancellation from another task) nobody else will send it. If we miss to call
591                 // connectionSendAndFlush() the remote peer may not learn about the new credit and so might never
592                 // send more data, on this stream or on the connection.
593                 // See also https://docs.rs/quiche/latest/quiche/struct.Connection.html#method.send.
594                 parent().connectionSendAndFlush();
595             }
596         }
597 
598         private void deregister(final ChannelPromise promise, final boolean fireChannelInactive) {
599             assert eventLoop().inEventLoop();
600             if (!promise.setUncancellable()) {
601                 return;
602             }
603 
604             if (!registered) {
605                 promise.trySuccess();
606                 return;
607             }
608 
609             // As a user may call deregister() from within any method while doing processing in the ChannelPipeline,
610             // we need to ensure we do the actual deregister operation later. This is needed as for example,
611             // we may be in the ByteToMessageDecoder.callDecode(...) method and so still try to do processing in
612             // the old EventLoop while the user already registered the Channel to a new EventLoop. Without delay,
613             // the deregister operation this could lead to have a handler invoked by different EventLoop and so
614             // threads.
615             //
616             // See:
617             // https://github.com/netty/netty/issues/4435
618             invokeLater(() -> {
619                 if (fireChannelInactive) {
620                     pipeline.fireChannelInactive();
621                 }
622                 // Some transports like local and AIO does not allow the deregistration of
623                 // an open channel.  Their doDeregister() calls close(). Consequently,
624                 // close() calls deregister() again - no need to fire channelUnregistered, so check
625                 // if it was registered.
626                 if (registered) {
627                     registered = false;
628                     pipeline.fireChannelUnregistered();
629                 }
630                 promise.setSuccess();
631             });
632         }
633 
634         private void invokeLater(Runnable task) {
635             try {
636                 // This method is used by outbound operation implementations to trigger an inbound event later.
637                 // They do not trigger an inbound event immediately because an outbound operation might have been
638                 // triggered by another inbound event handler method.  If fired immediately, the call stack
639                 // will look like this for example:
640                 //
641                 //   handlerA.inboundBufferUpdated() - (1) an inbound handler method closes a connection.
642                 //   -> handlerA.ctx.close()
643                 //      -> channel.unsafe.close()
644                 //         -> handlerA.channelInactive() - (2) another inbound handler method called while in (1) yet
645                 //
646                 // which means the execution of two inbound handler methods of the same handler overlap undesirably.
647                 eventLoop().execute(task);
648             } catch (RejectedExecutionException e) {
649                 LOGGER.warn("Can't invoke task later as EventLoop rejected it", e);
650             }
651         }
652 
653         @Override
654         public void closeForcibly() {
655             assert eventLoop().inEventLoop();
656             close(unsafe().voidPromise());
657         }
658 
659         @Override
660         public void deregister(ChannelPromise promise) {
661             assert eventLoop().inEventLoop();
662             deregister(promise, false);
663         }
664 
665         @Override
666         public void beginRead() {
667             assert eventLoop().inEventLoop();
668             readPending = true;
669             if (readable) {
670                 unsafe().recv();
671 
672                 // As the stream was readable, and we called recv() ourselves we also need to call
673                 // connectionSendAndFlush(). This is needed as recv() might consume data and so a window update
674                 // frame might be produced. If we miss to call connectionSendAndFlush() we might never send the update
675                 // to the remote peer and so the remote peer might never attempt to send more data.
676                 // See also https://docs.rs/quiche/latest/quiche/struct.Connection.html#method.send.
677                 parent().connectionSendAndFlush();
678             }
679         }
680 
681         private void closeIfNeeded(boolean wasFinSent) {
682             // Let's check if we should close the channel now.
683             // If it's a unidirectional channel we can close it as there will be no fin that we can read
684             // from the remote peer. If its an bidirectional channel we should only close the channel if we
685             // also received the fin from the remote peer.
686             if (!wasFinSent && QuicheQuicStreamChannel.this.finSent
687                     && (type() == QuicStreamType.UNIDIRECTIONAL || finReceived)) {
688                 // close the channel now
689                 close(voidPromise());
690             }
691         }
692 
693         boolean writeQueued() {
694             assert eventLoop().inEventLoop();
695             boolean wasFinSent = QuicheQuicStreamChannel.this.finSent;
696             inWriteQueued = true;
697             try {
698                 if (queue.isEmpty()) {
699                     return false;
700                 }
701                 boolean written = false;
702                 for (;;) {
703                     Object msg = queue.current();
704                     if (msg == null) {
705                         break;
706                     }
707                     try {
708                         int res = write0(msg);
709                         if (res == 1) {
710                             queue.remove().setSuccess();
711                             written = true;
712                         } else if (res == 0 || res == Quiche.QUICHE_ERR_DONE) {
713                             break;
714                         } else if (res == Quiche.QUICHE_ERR_STREAM_STOPPED) {
715                             // Once its signaled that the stream is stopped we can just fail everything.
716                             // That said we should not close the channel yet as there might be some data that is
717                             // not read yet by the user.
718                             queue.removeAndFailAll(
719                                     new ChannelOutputShutdownException("STOP_SENDING frame received"));
720                             break;
721                         } else {
722                             queue.remove().setFailure(Quiche.convertToException(res));
723                         }
724                     } catch (Exception e) {
725                         queue.remove().setFailure(e);
726                     }
727                 }
728                 if (written) {
729                     updateWritabilityIfNeeded(capacity > 0);
730                 }
731                 return written;
732             } finally {
733                 closeIfNeeded(wasFinSent);
734                 inWriteQueued = false;
735             }
736         }
737 
738         @Override
739         public void write(Object msg, ChannelPromise promise) {
740             assert eventLoop().inEventLoop();
741             if (!promise.setUncancellable()) {
742                 ReferenceCountUtil.release(msg);
743                 return;
744             }
745             // Check first if the Channel is in a state in which it will accept writes, if not fail everything
746             // with the right exception
747             if (!isOpen()) {
748                 queueAndFailAll(msg, promise, new ClosedChannelException());
749             } else if (finSent) {
750                 queueAndFailAll(msg, promise, new ChannelOutputShutdownException("Fin was sent already"));
751             } else if (!queue.isEmpty()) {
752                 // If the queue is not empty we should just add the message to the queue as we will drain
753                 // it later once the stream becomes writable again.
754                 try {
755                     msg = filterMsg(msg);
756                 } catch (UnsupportedOperationException e) {
757                     ReferenceCountUtil.release(msg);
758                     promise.setFailure(e);
759                     return;
760                 }
761 
762                 // Touch the message to make things easier in terms of debugging buffer leaks.
763                 ReferenceCountUtil.touch(msg);
764                 queue.add(msg, promise);
765 
766                 // Try again to write queued messages.
767                 writeQueued();
768             } else {
769                 assert queue.isEmpty();
770                 writeWithoutCheckChannelState(msg, promise);
771             }
772         }
773 
774         private void queueAndFailAll(Object msg, ChannelPromise promise, Throwable cause) {
775             // Touch the message to make things easier in terms of debugging buffer leaks.
776             ReferenceCountUtil.touch(msg);
777 
778             queue.add(msg, promise);
779             queue.removeAndFailAll(cause);
780         }
781 
782         private Object filterMsg(Object msg) {
783             if (msg instanceof ByteBuf) {
784                 ByteBuf buffer = (ByteBuf)  msg;
785                 if (!buffer.isDirect()) {
786                     ByteBuf tmpBuffer = alloc().directBuffer(buffer.readableBytes());
787                     tmpBuffer.writeBytes(buffer, buffer.readerIndex(), buffer.readableBytes());
788                     buffer.release();
789                     return tmpBuffer;
790                 }
791             } else if (msg instanceof QuicStreamFrame) {
792                 QuicStreamFrame frame = (QuicStreamFrame) msg;
793                 ByteBuf buffer = frame.content();
794                 if (!buffer.isDirect()) {
795                     ByteBuf tmpBuffer = alloc().directBuffer(buffer.readableBytes());
796                     tmpBuffer.writeBytes(buffer, buffer.readerIndex(), buffer.readableBytes());
797                     QuicStreamFrame tmpFrame = frame.replace(tmpBuffer);
798                     frame.release();
799                     return tmpFrame;
800                 }
801             } else {
802                 throw new UnsupportedOperationException(
803                         "unsupported message type: " + StringUtil.simpleClassName(msg));
804             }
805             return msg;
806         }
807 
808         void writeWithoutCheckChannelState(Object msg, ChannelPromise promise) {
809             try {
810                 msg = filterMsg(msg);
811             } catch (UnsupportedOperationException e) {
812                 ReferenceCountUtil.release(msg);
813                 promise.setFailure(e);
814                 return;
815             }
816 
817             boolean wasFinSent = QuicheQuicStreamChannel.this.finSent;
818             boolean mayNeedWritabilityUpdate = false;
819             try {
820                 int res = write0(msg);
821                 if (res > 0) {
822                     ReferenceCountUtil.release(msg);
823                     promise.setSuccess();
824                     mayNeedWritabilityUpdate = capacity == 0;
825                 } else if (res == 0 || res == Quiche.QUICHE_ERR_DONE) {
826                     // Touch the message to make things easier in terms of debugging buffer leaks.
827                     ReferenceCountUtil.touch(msg);
828                     queue.add(msg, promise);
829                     mayNeedWritabilityUpdate = true;
830                 } else if (res == Quiche.QUICHE_ERR_STREAM_STOPPED) {
831                     throw new ChannelOutputShutdownException("STOP_SENDING frame received");
832                 } else {
833                     throw Quiche.convertToException(res);
834                 }
835             } catch (Exception e) {
836                 ReferenceCountUtil.release(msg);
837                 promise.setFailure(e);
838                 mayNeedWritabilityUpdate = capacity == 0;
839             } finally {
840                 if (mayNeedWritabilityUpdate) {
841                     updateWritabilityIfNeeded(false);
842                 }
843                 closeIfNeeded(wasFinSent);
844             }
845         }
846 
847         private int write0(Object msg) throws Exception {
848             if (type() == QuicStreamType.UNIDIRECTIONAL && !isLocalCreated()) {
849                 throw new UnsupportedOperationException(
850                         "Writes on non-local created streams that are unidirectional are not supported");
851             }
852             if (finSent) {
853                 throw new ChannelOutputShutdownException("Fin was sent already");
854             }
855 
856             final boolean fin;
857             ByteBuf buffer;
858             if (msg instanceof ByteBuf) {
859                 fin = false;
860                 buffer = (ByteBuf) msg;
861             } else {
862                 QuicStreamFrame frame = (QuicStreamFrame) msg;
863                 fin = frame.hasFin();
864                 buffer = frame.content();
865             }
866 
867             boolean readable = buffer.isReadable();
868             if (!fin && !readable) {
869                 return 1;
870             }
871 
872             boolean sendSomething = false;
873             try {
874                 do {
875                     int res = parent().streamSend(streamId(), buffer, fin);
876 
877                     // Update the capacity as well.
878                     long cap = parent.streamCapacity(streamId());
879                     if (cap >= 0) {
880                         capacity = cap;
881                     }
882                     if (res < 0) {
883                         return res;
884                     }
885                     if (readable && res == 0) {
886                         return 0;
887                     }
888                     sendSomething = true;
889                     buffer.skipBytes(res);
890                 } while (buffer.isReadable());
891 
892                 if (fin) {
893                     finSent = true;
894                     outputShutdown = true;
895                 }
896                 return 1;
897             } finally {
898                 // As we called quiche_conn_stream_send(...) we need to ensure we will call quiche_conn_send(...) either
899                 // now or we will do so once we see the channelReadComplete event.
900                 //
901                 // See https://docs.rs/quiche/0.6.0/quiche/struct.Connection.html#method.send
902                 if (sendSomething) {
903                     parent.connectionSendAndFlush();
904                 }
905             }
906         }
907 
908         @Override
909         public void flush() {
910             assert eventLoop().inEventLoop();
911             // NOOP.
912         }
913 
914         @Override
915         public ChannelPromise voidPromise() {
916             assert eventLoop().inEventLoop();
917             return voidPromise;
918         }
919 
920         @Override
921         @Nullable
922         public ChannelOutboundBuffer outboundBuffer() {
923             return null;
924         }
925 
926         private void closeOnRead(ChannelPipeline pipeline, boolean readFrames) {
927             if (readFrames && finReceived && finSent) {
928                 close(voidPromise());
929             } else if (config.isAllowHalfClosure()) {
930                 if (finReceived) {
931                     // If we receive a fin there will be no more data to read so we need to fire both events
932                     // to be consistent with other transports.
933                     pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
934                     pipeline.fireUserEventTriggered(ChannelInputShutdownReadComplete.INSTANCE);
935                     if (finSent) {
936                         // This was an unidirectional stream which means as soon as we received FIN and sent a FIN
937                         // we need close the connection.
938                         close(voidPromise());
939                     }
940                 }
941             } else {
942                 // This was an unidirectional stream which means as soon as we received FIN we need
943                 // close the connection.
944                 close(voidPromise());
945             }
946         }
947 
948         private void handleReadException(ChannelPipeline pipeline, @Nullable ByteBuf byteBuf, Throwable cause,
949                                          @SuppressWarnings("deprecation") RecvByteBufAllocator.Handle allocHandle,
950                                          boolean readFrames) {
951             if (byteBuf != null) {
952                 if (byteBuf.isReadable()) {
953                     pipeline.fireChannelRead(byteBuf);
954                 } else {
955                     byteBuf.release();
956                 }
957             }
958 
959             readComplete(allocHandle, pipeline);
960             pipeline.fireExceptionCaught(cause);
961             if (finReceived) {
962                 closeOnRead(pipeline, readFrames);
963             }
964         }
965 
966         void recv() {
967             assert eventLoop().inEventLoop();
968             if (inRecv) {
969                 // As the use may call read() we need to guard against reentrancy here as otherwise it could
970                 // be possible that we re-enter this method while still processing it.
971                 return;
972             }
973 
974             inRecv = true;
975             try {
976                 ChannelPipeline pipeline = pipeline();
977                 QuicheQuicStreamChannelConfig config = (QuicheQuicStreamChannelConfig) config();
978                 // Directly access the DirectIoByteBufAllocator as we need an direct buffer to read into in all cases
979                 // even if there is no Unsafe present and the direct buffer is not pooled.
980                 DirectIoByteBufAllocator allocator = config.allocator;
981                 @SuppressWarnings("deprecation")
982                 RecvByteBufAllocator.Handle allocHandle = this.recvBufAllocHandle();
983                 boolean readFrames = config.isReadFrames();
984 
985                 // We should loop as long as a read() was requested and there is anything left to read, which means the
986                 // stream was marked as readable before.
987                 while (active && readPending && readable) {
988                     allocHandle.reset(config);
989                     ByteBuf byteBuf = null;
990                     QuicheQuicChannel parent = parent();
991                     // It's possible that the stream was marked as finish while we iterated over the readable streams
992                     // or while we did have auto read disabled. If so we need to ensure we not try to read from it as it
993                     // would produce an error.
994                     boolean readCompleteNeeded = false;
995                     boolean continueReading = true;
996                     try {
997                         while (!finReceived && continueReading) {
998                             byteBuf = allocHandle.allocate(allocator);
999                             allocHandle.attemptedBytesRead(byteBuf.writableBytes());
1000                             handleRecvResult(parent.streamRecv(streamId(), byteBuf));
1001                             allocHandle.lastBytesRead(byteBuf.readableBytes());
1002                             if (allocHandle.lastBytesRead() <= 0) {
1003                                 byteBuf.release();
1004                                 if (finReceived && readFrames) {
1005                                     // If we read QuicStreamFrames we should fire an frame through the pipeline
1006                                     // with an empty buffer but the fin flag set to true.
1007                                     byteBuf = Unpooled.EMPTY_BUFFER;
1008                                 } else {
1009                                     byteBuf = null;
1010                                     break;
1011                                 }
1012                             }
1013                             // We did read one message.
1014                             allocHandle.incMessagesRead(1);
1015                             readCompleteNeeded = true;
1016 
1017                             // It's important that we reset this to false before we call fireChannelRead(...)
1018                             // as the user may request another read() from channelRead(...) callback.
1019                             readPending = false;
1020 
1021                             if (readFrames) {
1022                                 pipeline.fireChannelRead(new DefaultQuicStreamFrame(byteBuf, finReceived));
1023                             } else {
1024                                 pipeline.fireChannelRead(byteBuf);
1025                             }
1026                             byteBuf = null;
1027                             continueReading = allocHandle.continueReading();
1028                         }
1029 
1030                         if (readCompleteNeeded) {
1031                             readComplete(allocHandle, pipeline);
1032                         }
1033                         if (finReceived) {
1034                             readable = false;
1035                             closeOnRead(pipeline, readFrames);
1036                         }
1037                     } catch (Throwable cause) {
1038                         readable = false;
1039                         handleReadException(pipeline, byteBuf, cause, allocHandle, readFrames);
1040                     }
1041                 }
1042                 if (!active) {
1043                     discardInput(allocator, allocHandle);
1044                 }
1045             } finally {
1046                 // About to leave the method lets reset so we can enter it again.
1047                 inRecv = false;
1048                 removeStreamFromParent();
1049             }
1050         }
1051 
1052         private void handleRecvResult(QuicheQuicChannel.StreamRecvResult result) {
1053             switch (result) {
1054                 case DONE:
1055                     // Nothing left to read;
1056                     readable = false;
1057                     break;
1058                 case FIN:
1059                     // If we received a FIN we also should mark the channel as non-readable as
1060                     // there is nothing left to read really.
1061                     readable = false;
1062                     finReceived = true;
1063                     inputShutdown = true;
1064                     break;
1065                 case OK:
1066                     break;
1067                 default:
1068                     throw new Error("Unexpected StreamRecvResult: " + result);
1069             }
1070         }
1071 
1072         /**
1073          * The channel was closed before the end of the stream was read. Read and drop what is left: as long as quiche
1074          * holds the data it keeps its connection flow control credit, and the stream is never completed and collected,
1075          * so its stream credit is never returned to the remote peer either.
1076          */
1077         private void discardInput(DirectIoByteBufAllocator allocator,
1078                                   @SuppressWarnings("deprecation") RecvByteBufAllocator.Handle allocHandle) {
1079             QuicheQuicChannel parent = parent();
1080             while (readable && !finReceived) {
1081                 allocHandle.reset(config());
1082                 ByteBuf byteBuf = allocHandle.allocate(allocator);
1083                 try {
1084                     handleRecvResult(parent.streamRecv(streamId(), byteBuf));
1085                 } catch (Throwable cause) {
1086                     // The stream was reset by the remote peer or the connection was closed, there is nothing left
1087                     // to read.
1088                     readable = false;
1089                     finReceived = true;
1090                     inputShutdown = true;
1091                     // A ClosedChannelException means the connection was closed or freed already. This is expected
1092                     // when the streams are closed as part of closing the connection, and the user is notified about
1093                     // the closure of the stream and the connection anyway, so there is no need to fire it. Everything
1094                     // else is reported as the data that was dropped is lost.
1095                     if (!(cause instanceof ClosedChannelException)) {
1096                         pipeline().fireExceptionCaught(cause);
1097                     }
1098                 } finally {
1099                     byteBuf.release();
1100                 }
1101             }
1102         }
1103 
1104         // Read was complete and something was read, so we we need to reset the readPending flags, the allocHandle
1105         // and call fireChannelReadComplete(). The user may schedule another read now.
1106         private void readComplete(@SuppressWarnings("deprecation") RecvByteBufAllocator.Handle allocHandle,
1107                                   ChannelPipeline pipeline) {
1108             allocHandle.readComplete();
1109             pipeline.fireChannelReadComplete();
1110         }
1111     }
1112 }