public override void Write(IChannelHandlerContext ctx, int allowedBytes)
            {
                int queuedData = _queue.ReadableBytes();

                if (!_endOfStream)
                {
                    if (0u >= (uint)queuedData)
                    {
                        if (_queue.IsEmpty())
                        {
                            // When the queue is empty it means we did clear it because of an error(...) call
                            // (as otherwise we will have at least 1 entry in there), which will happen either when called
                            // explicit or when the write itself fails. In this case just set dataSize and padding to 0
                            // which will signal back that the whole frame was consumed.
                            //
                            // See https://github.com/netty/netty/issues/8707.
                            _padding = _dataSize = 0;
                        }
                        else
                        {
                            // There's no need to write any data frames because there are only empty data frames in the
                            // queue and it is not end of stream yet. Just complete their promises by getting the buffer
                            // corresponding to 0 bytes and writing it to the channel (to preserve notification order).
                            var writePromise0 = ctx.NewPromise();
                            AddListener(writePromise0);
                            _ = ctx.WriteAsync(_queue.Remove(0, writePromise0), writePromise0);
                        }
                        return;
                    }

                    if (0u >= (uint)allowedBytes)
                    {
                        return;
                    }
                }

                // Determine how much data to write.
                int writableData = Math.Min(queuedData, allowedBytes);
                var writePromise = ctx.NewPromise();

                AddListener(writePromise);
                var toWrite = _queue.Remove(writableData, writePromise);

                _dataSize = _queue.ReadableBytes();

                // Determine how much padding to write.
                int writablePadding = Math.Min(allowedBytes - writableData, _padding);

                _padding -= writablePadding;

                // Write the frame(s).
                _ = _owner._frameWriter.WriteDataAsync(ctx, _stream.Id, toWrite, writablePadding,
                                                       _endOfStream && 0u >= (uint)Size, writePromise);
            }
Example #2
0
        /// <summary>Add the given <c>msg</c> and returns <see cref="Task" /> for completion of processing <c>msg</c>.</summary>
        public void Add(object msg, IPromise promise)
        {
            Debug.Assert(_ctx.Executor.InEventLoop);
            if (msg is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.msg);
            }

            int messageSize = GetSize(msg);

            PendingWrite currentTail = _tail;

            if (currentTail is object)
            {
                bool canBundle = CanBatch(msg, messageSize, currentTail.Size);
                if (canBundle)
                {
                    currentTail.Add(msg, messageSize);
                    if (!promise.IsVoid)
                    {
                        currentTail.Promise.Task.LinkOutcome(promise);
                    }
                    return;
                }
            }

            PendingWrite write;

            if (promise.IsVoid || promise is SimplePromiseAggregator)
            {
                var headPromise = _ctx.NewPromise();
                headPromise.Task.LinkOutcome(promise);
                write = PendingWrite.NewInstance(msg, messageSize, headPromise);
            }
            else
            {
                write = PendingWrite.NewInstance(msg, messageSize, promise);
            }
            if (currentTail is null)
            {
                _tail = _head = write;
            }
            else
            {
                currentTail.Next = write;
                _tail            = write;
            }
            _size++;
            // We need to guard against null as channel.Unsafe.OutboundBuffer may returned null
            // if the channel was already closed when constructing the PendingWriteQueue.
            // See https://github.com/netty/netty/issues/3967
            _buffer?.IncrementPendingOutboundBytes(messageSize);
        }
        /**
         * Sends a "Hello World" DATA frame to the client.
         */
        void SendResponse(IChannelHandlerContext ctx, int streamId, IByteBuffer payload)
        {
            // Send a frame for the response status
            IHttp2Headers headers = new DefaultHttp2Headers()
            {
                Status = HttpResponseStatus.OK.CodeAsText
            };

            this.Encoder.WriteHeadersAsync(ctx, streamId, headers, 0, false, ctx.NewPromise());
            this.Encoder.WriteDataAsync(ctx, streamId, payload, 0, true, ctx.NewPromise());

            // no need to call flush as channelReadComplete(...) will take care of it.
        }
        private IPromise HandleOutstandingControlFrames(IChannelHandlerContext ctx, IPromise promise)
        {
            if (!_limitReached)
            {
                if (_outstandingControlFrames == _maxOutstandingControlFrames)
                {
                    // Let's try to flush once as we may be able to flush some of the control frames.
                    _ = ctx.Flush();
                }
                if (_outstandingControlFrames == _maxOutstandingControlFrames)
                {
                    _limitReached = true;
                    Http2Exception exception = ThrowHelper.GetConnectionError_Maximum_number_of_outstanding_control_frames_reached(_maxOutstandingControlFrames);
                    if (Logger.InfoEnabled)
                    {
                        Logger.Maximum_number_of_outstanding_control_frames_reached(_maxOutstandingControlFrames, ctx, exception);
                    }

                    // First notify the Http2LifecycleManager and then close the connection.
                    _lifecycleManager.OnError(ctx, true, exception);
                    _ = ctx.CloseAsync();
                }
                _outstandingControlFrames++;

                // We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
                // once the promise was completed
                var newPromise = promise is object?promise.Unvoid() : ctx.NewPromise();

                _ = newPromise.Task.ContinueWith(OutstandingControlFramesListenerAction, this, TaskContinuationOptions.ExecuteSynchronously);
                return(newPromise);
            }
            return(promise);
        }
Example #5
0
        private void WrapAndFlush(IChannelHandlerContext context)
        {
            if (_pendingUnencryptedWrites.IsEmpty)
            {
                // It's important to NOT use a voidPromise here as the user
                // may want to add a ChannelFutureListener to the ChannelPromise later.
                //
                // See https://github.com/netty/netty/issues/3364
                _pendingUnencryptedWrites.Add(Unpooled.Empty, context.NewPromise());
            }

            if (!EnsureAuthenticated(context))
            {
                State |= TlsHandlerState.FlushedBeforeHandshake;
                return;
            }

            try
            {
                Wrap(context);
            }
            finally
            {
                // We may have written some parts of data before an exception was thrown so ensure we always flush.
                _ = context.Flush();
            }
        }
Example #6
0
        public override void Close(IChannelHandlerContext context, IPromise promise)
        {
            var completion = this.FinishEncode(context, context.NewPromise());

            _ = completion.ContinueWith(CloseOnCompleteAction, (context, promise), TaskContinuationOptions.ExecuteSynchronously);
            if (!completion.IsCompleted)
            {
                _ = ctx.Executor.Schedule(CloseHandlerContextAction, context, promise, TimeSpan.FromSeconds(10));
            }
        }
Example #7
0
        /// <summary>
        /// Removes all pending write operation and performs them via <see cref="IChannelHandlerContext.WriteAsync(object, IPromise)"/>
        /// </summary>
        /// <returns><see cref="Task"/> if something was written and <c>null</c>
        /// if the <see cref="PendingWriteQueue"/> is empty.</returns>
        public Task RemoveAndWriteAllAsync()
        {
            Debug.Assert(_ctx.Executor.InEventLoop);

            if (IsEmpty)
            {
                return(null);
            }

            var             p        = _ctx.NewPromise();
            PromiseCombiner combiner = new PromiseCombiner(_ctx.Executor);

            try
            {
                // It is possible for some of the written promises to trigger more writes. The new writes
                // will "revive" the queue, so we need to write them up until the queue is empty.
                for (PendingWrite write = _head; write is object; write = _head)
                {
                    _head  = _tail = null;
                    _size  = 0;
                    _bytes = 0;

                    while (write is object)
                    {
                        PendingWrite next    = write.Next;
                        object       msg     = write.Msg;
                        IPromise     promise = write.Promise;
                        Recycle(write, false);
                        if (!promise.IsVoid)
                        {
                            combiner.Add(promise.Task);
                        }
                        _     = _ctx.WriteAsync(msg, promise);
                        write = next;
                    }
                }
                combiner.Finish(p);
            }
            catch (Exception exc)
            {
                p.SetException(exc);
            }
            AssertEmpty();
            return(p.Task);
        }
Example #8
0
        public void Timeout()
        {
            AtomicReference <IPromise> refp    = new AtomicReference <IPromise>();
            WebSocketProtocolHandler   handler = new TestWebSocketProtocolHandler(false, WebSocketCloseStatus.NormalClosure, 1)
            {
            };
            EmbeddedChannel channel = new EmbeddedChannel(new TimeoutHandler(refp), handler);

            var future = channel.WriteAndFlushAsync(new CloseWebSocketFrame());
            IChannelHandlerContext ctx = channel.Pipeline.Context <WebSocketProtocolHandler>();

            handler.Close(ctx, ctx.NewPromise());

            do
            {
                Thread.Sleep(10);
                channel.RunPendingTasks();
            } while (!future.IsCompleted);

            Assert.True(future.Exception.InnerException is WebSocketHandshakeException);
            Assert.False(refp.Value.IsCompleted);
            Assert.False(channel.Finish());
        }
 public IPromise NewPromise() => _ctx.NewPromise();
Example #10
0
 public override void HandlerAdded(IChannelHandlerContext context)
 {
     _handshakeFuture = context.NewPromise();
 }
Example #11
0
 public override void HandlerAdded(IChannelHandlerContext context)
 {
     _ctx = context;
     _handshakePromise = context.NewPromise();
 }
        public override Task WriteDataAsync(IChannelHandlerContext ctx, int streamId, IByteBuffer data, int padding, bool endOfStream, IPromise promise)
        {
            IHttp2Stream    stream  = Connection.Stream(streamId);
            EmbeddedChannel channel = stream?.GetProperty <EmbeddedChannel>(_propertyKey);

            if (channel is null)
            {
                // The compressor may be null if no compatible encoding type was found in this stream's headers
                return(base.WriteDataAsync(ctx, streamId, data, padding, endOfStream, promise));
            }

            try
            {
                // The channel will release the buffer after being written
                _ = channel.WriteOutbound(data);
                var buf = NextReadableBuf(channel);
                if (buf is null)
                {
                    if (endOfStream)
                    {
                        if (channel.Finish())
                        {
                            buf = NextReadableBuf(channel);
                        }
                        return(base.WriteDataAsync(ctx, streamId, buf ?? Unpooled.Empty, padding,
                                                   true, promise));
                    }
                    // END_STREAM is not set and the assumption is data is still forthcoming.
                    promise.Complete();
                    return(promise.Task);
                }

                var tasks = new List <Task>();
                while (true)
                {
                    var nextBuf = NextReadableBuf(channel);
                    var compressedEndOfStream = nextBuf is null && endOfStream;
                    if (compressedEndOfStream && channel.Finish())
                    {
                        nextBuf = NextReadableBuf(channel);
                        compressedEndOfStream = nextBuf is null;
                    }

                    var bufPromise = ctx.NewPromise();
                    tasks.Add(bufPromise.Task);
                    _ = base.WriteDataAsync(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise);

                    if (nextBuf is null)
                    {
                        break;
                    }

                    padding = 0; // Padding is only communicated once on the first iteration
                    buf     = nextBuf;
                }
                Task.WhenAll(tasks).LinkOutcome(promise);
            }
            catch (Exception cause)
            {
                _ = promise.TrySetException(cause);
            }
            finally
            {
                if (endOfStream)
                {
                    Cleanup(stream, channel);
                }
            }

            return(promise.Task);
        }