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); }
/// <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); }
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(); } }
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)); } }
/// <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); }
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();
public override void HandlerAdded(IChannelHandlerContext context) { _handshakeFuture = context.NewPromise(); }
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); }