/// <summary> /// If padding is present in the payload, reads the next byte as padding. The padding also includes the one byte /// width of the pad length field. Otherwise, returns zero. /// </summary> /// <param name="payload"></param> /// <returns></returns> int ReadPadding(IByteBuffer payload) { if (!_flags.PaddingPresent()) { return(0); } return(payload.ReadByte() + 1); }
public virtual Task WriteDataAsync(IChannelHandlerContext ctx, int streamId, IByteBuffer data, int padding, bool endOfStream, IPromise promise) { SimplePromiseAggregator promiseAggregator = new SimplePromiseAggregator(promise); IByteBuffer frameHeader = null; try { if ((uint)(streamId - 1) > SharedConstants.TooBigOrNegative) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.StreamID); } Http2CodecUtil.VerifyPadding(padding); int remainingData = data.ReadableBytes; Http2Flags flags = new Http2Flags(); _ = flags.EndOfStream(false); _ = flags.PaddingPresent(false); // Fast path to write frames of payload size maxFrameSize first. if (remainingData > _maxFrameSize) { frameHeader = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(frameHeader, _maxFrameSize, Http2FrameTypes.Data, flags, streamId); do { // Write the header. _ = ctx.WriteAsync(frameHeader.RetainedSlice(), promiseAggregator.NewPromise()); // Write the payload. _ = ctx.WriteAsync(data.ReadRetainedSlice(_maxFrameSize), promiseAggregator.NewPromise()); remainingData -= _maxFrameSize; // Stop iterating if remainingData == _maxFrameSize so we can take care of reference counts below. }while (remainingData > _maxFrameSize); } if (0u >= (uint)padding) { // Write the header. if (frameHeader is object) { _ = frameHeader.Release(); frameHeader = null; } IByteBuffer frameHeader2 = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength); _ = flags.EndOfStream(endOfStream); Http2CodecUtil.WriteFrameHeaderInternal(frameHeader2, remainingData, Http2FrameTypes.Data, flags, streamId); _ = ctx.WriteAsync(frameHeader2, promiseAggregator.NewPromise()); // Write the payload. IByteBuffer lastFrame = data.ReadSlice(remainingData); data = null; _ = ctx.WriteAsync(lastFrame, promiseAggregator.NewPromise()); } else { if (remainingData != _maxFrameSize) { if (frameHeader is object) { _ = frameHeader.Release(); frameHeader = null; } } else { remainingData -= _maxFrameSize; // Write the header. IByteBuffer lastFrame; if (frameHeader is null) { lastFrame = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(lastFrame, _maxFrameSize, Http2FrameTypes.Data, flags, streamId); } else { lastFrame = frameHeader.Slice(); frameHeader = null; } _ = ctx.WriteAsync(lastFrame, promiseAggregator.NewPromise()); // Write the payload. lastFrame = data.ReadableBytes != _maxFrameSize?data.ReadSlice(_maxFrameSize) : data; data = null; _ = ctx.WriteAsync(lastFrame, promiseAggregator.NewPromise()); } do { int frameDataBytes = Math.Min(remainingData, _maxFrameSize); int framePaddingBytes = Math.Min(padding, Math.Max(0, (_maxFrameSize - 1) - frameDataBytes)); // Decrement the remaining counters. padding -= framePaddingBytes; remainingData -= frameDataBytes; // Write the header. IByteBuffer frameHeader2 = ctx.Allocator.Buffer(Http2CodecUtil.DataFrameHeaderLength); _ = flags.EndOfStream(endOfStream && 0u >= (uint)remainingData && 0u >= (uint)padding); _ = flags.PaddingPresent(framePaddingBytes > 0); Http2CodecUtil.WriteFrameHeaderInternal(frameHeader2, framePaddingBytes + frameDataBytes, Http2FrameTypes.Data, flags, streamId); WritePaddingLength(frameHeader2, framePaddingBytes); _ = ctx.WriteAsync(frameHeader2, promiseAggregator.NewPromise()); // Write the payload. if (frameDataBytes != 0) { if (0u >= (uint)remainingData) { IByteBuffer lastFrame = data.ReadSlice(frameDataBytes); data = null; _ = ctx.WriteAsync(lastFrame, promiseAggregator.NewPromise()); } else { _ = ctx.WriteAsync(data.ReadRetainedSlice(frameDataBytes), promiseAggregator.NewPromise()); } } // Write the frame padding. if (PaddingBytes(framePaddingBytes) > 0) { _ = ctx.WriteAsync( ZeroBuffer.Slice(0, PaddingBytes(framePaddingBytes)), promiseAggregator.NewPromise()); } }while (remainingData != 0 || padding != 0); } } catch (Exception cause) { if (frameHeader is object) { _ = frameHeader.Release(); } // Use a try/finally here in case the data has been released before calling this method. This is not // necessary above because we internally allocate frameHeader. try { if (data is object) { _ = data.Release(); } } finally { promiseAggregator.SetException(cause); _ = promiseAggregator.DoneAllocatingPromises(); } return(promiseAggregator.Task); } _ = promiseAggregator.DoneAllocatingPromises(); return(promiseAggregator.Task); }