void ReadPushPromiseFrame(IChannelHandlerContext ctx, IByteBuffer payload, int payloadEndIndex, IHttp2FrameListener listener) { int pushPromiseStreamId = _streamId; int padding = ReadPadding(payload); VerifyPadding(padding); int promisedStreamId = Http2CodecUtil.ReadUnsignedInt(payload); // Create a handler that invokes the listener when the header block is complete. _headersContinuation = new PushPromiseFrameHeadersContinuation(this, ctx, pushPromiseStreamId, padding, promisedStreamId); // Process the initial fragment, invoking the listener's callback if end of headers. int dataLength = LengthWithoutTrailingPadding(payloadEndIndex - payload.ReaderIndex, padding); _headersContinuation.ProcessFragment(_flags.EndOfHeaders(), payload, dataLength, listener); ResetHeadersContinuationIfEnd(_flags.EndOfHeaders()); }
void ReadHeadersFrame(IChannelHandlerContext ctx, IByteBuffer payload, int payloadEndIndex, IHttp2FrameListener listener) { int headersStreamId = _streamId; Http2Flags headersFlags = _flags; int padding = ReadPadding(payload); VerifyPadding(padding); // The callback that is invoked is different depending on whether priority information // is present in the headers frame. if (headersFlags.PriorityPresent()) { long word1 = payload.ReadUnsignedInt(); bool exclusive = (word1 & 0x80000000L) != 0; int streamDependency = (int)(uint)(word1 & 0x7FFFFFFFL); if (streamDependency == headersStreamId) { ThrowHelper.ThrowStreamError_AStreamCannotDependOnItself(headersStreamId); } short weight = (short)(payload.ReadByte() + 1); int lenToRead = LengthWithoutTrailingPadding(payloadEndIndex - payload.ReaderIndex, padding); // Create a handler that invokes the listener when the header block is complete. _headersContinuation = new PriorityHeadersFrameHeadersContinuation(this, ctx, headersStreamId, padding, streamDependency, weight, exclusive, headersFlags); // Process the initial fragment, invoking the listener's callback if end of headers. _headersContinuation.ProcessFragment(headersFlags.EndOfHeaders(), payload, lenToRead, listener); ResetHeadersContinuationIfEnd(headersFlags.EndOfHeaders()); return; } // The priority fields are not present in the frame. Prepare a continuation that invokes // the listener callback without priority information. _headersContinuation = new HeadersFrameHeadersContinuation(this, ctx, headersStreamId, padding, headersFlags); // Process the initial fragment, invoking the listener's callback if end of headers. int dataLength = LengthWithoutTrailingPadding(payloadEndIndex - payload.ReaderIndex, padding); _headersContinuation.ProcessFragment(headersFlags.EndOfHeaders(), payload, dataLength, listener); ResetHeadersContinuationIfEnd(headersFlags.EndOfHeaders()); }
/// <summary> /// Writes as many continuation frames as needed until <paramref name="headerBlock"/> are consumed. /// </summary> /// <param name="ctx"></param> /// <param name="streamId"></param> /// <param name="headerBlock"></param> /// <param name="promiseAggregator"></param> /// <returns></returns> Task WriteContinuationFramesAsync(IChannelHandlerContext ctx, int streamId, IByteBuffer headerBlock, SimplePromiseAggregator promiseAggregator) { Http2Flags flags = new Http2Flags(); if (headerBlock.IsReadable()) { // The frame header (and padding) only changes on the last frame, so allocate it once and re-use int fragmentReadableBytes = Math.Min(headerBlock.ReadableBytes, _maxFrameSize); IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.ContinuationFrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, fragmentReadableBytes, Http2FrameTypes.Continuation, flags, streamId); do { fragmentReadableBytes = Math.Min(headerBlock.ReadableBytes, _maxFrameSize); IByteBuffer fragment = headerBlock.ReadRetainedSlice(fragmentReadableBytes); if (headerBlock.IsReadable()) { _ = ctx.WriteAsync(buf.Retain(), promiseAggregator.NewPromise()); } else { // The frame header is different for the last frame, so re-allocate and release the old buffer flags = flags.EndOfHeaders(true); _ = buf.Release(); buf = ctx.Allocator.Buffer(Http2CodecUtil.ContinuationFrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, fragmentReadableBytes, Http2FrameTypes.Continuation, flags, streamId); _ = ctx.WriteAsync(buf, promiseAggregator.NewPromise()); } _ = ctx.WriteAsync(fragment, promiseAggregator.NewPromise()); }while (headerBlock.IsReadable()); } return(promiseAggregator.Task); }
Task WriteHeadersInternal(IChannelHandlerContext ctx, int streamId, IHttp2Headers headers, int padding, bool endOfStream, bool hasPriority, int streamDependency, short weight, bool exclusive, IPromise promise) { IByteBuffer headerBlock = null; SimplePromiseAggregator promiseAggregator = new SimplePromiseAggregator(promise); try { if ((uint)(streamId - 1) > SharedConstants.TooBigOrNegative) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.StreamID); } if (hasPriority) { if (streamDependency < 0) { ThrowHelper.ThrowArgumentException_PositiveOrZero(ExceptionArgument.StreamDependency); } Http2CodecUtil.VerifyPadding(padding); VerifyWeight(weight); } // Encode the entire header block. headerBlock = ctx.Allocator.Buffer(); _headersEncoder.EncodeHeaders(streamId, headers, headerBlock); Http2Flags flags = new Http2Flags().EndOfStream(endOfStream).PriorityPresent(hasPriority).PaddingPresent(padding > 0); // Read the first fragment (possibly everything). int nonFragmentBytes = padding + flags.GetNumPriorityBytes(); int maxFragmentLength = _maxFrameSize - nonFragmentBytes; IByteBuffer fragment = headerBlock.ReadRetainedSlice(Math.Min(headerBlock.ReadableBytes, maxFragmentLength)); // Set the end of headers flag for the first frame. _ = flags.EndOfHeaders(!headerBlock.IsReadable()); int payloadLength = fragment.ReadableBytes + nonFragmentBytes; IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.HeadersFrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, payloadLength, Http2FrameTypes.Headers, flags, streamId); WritePaddingLength(buf, padding); if (hasPriority) { _ = buf.WriteInt(exclusive ? (int)(uint)(0x80000000L | streamDependency) : streamDependency); // Adjust the weight so that it fits into a single byte on the wire. _ = buf.WriteByte(weight - 1); } _ = ctx.WriteAsync(buf, promiseAggregator.NewPromise()); // Write the first fragment. _ = ctx.WriteAsync(fragment, promiseAggregator.NewPromise()); // Write out the padding, if any. if (PaddingBytes(padding) > 0) { _ = ctx.WriteAsync(ZeroBuffer.Slice(0, PaddingBytes(padding)), promiseAggregator.NewPromise()); } if (!flags.EndOfHeaders()) { _ = WriteContinuationFramesAsync(ctx, streamId, headerBlock, promiseAggregator); } } catch (Http2Exception e) { promiseAggregator.SetException(e); } catch (Exception t) { promiseAggregator.SetException(t); _ = promiseAggregator.DoneAllocatingPromises(); throw; } finally { _ = (headerBlock?.Release()); } _ = promiseAggregator.DoneAllocatingPromises(); return(promiseAggregator.Task); }
public virtual Task WritePushPromiseAsync(IChannelHandlerContext ctx, int streamId, int promisedStreamId, IHttp2Headers headers, int padding, IPromise promise) { IByteBuffer headerBlock = null; SimplePromiseAggregator promiseAggregator = new SimplePromiseAggregator(promise); try { if ((uint)(streamId - 1) > SharedConstants.TooBigOrNegative) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.StreamID); } if (promisedStreamId <= 0) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.PromisedStreamId); } Http2CodecUtil.VerifyPadding(padding); // Encode the entire header block into an intermediate buffer. headerBlock = ctx.Allocator.Buffer(); _headersEncoder.EncodeHeaders(streamId, headers, headerBlock); // Read the first fragment (possibly everything). Http2Flags flags = new Http2Flags().PaddingPresent(padding > 0); // IntFieldLength is for the length of the promisedStreamId int nonFragmentLength = Http2CodecUtil.IntFieldLength + padding; int maxFragmentLength = _maxFrameSize - nonFragmentLength; IByteBuffer fragment = headerBlock.ReadRetainedSlice(Math.Min(headerBlock.ReadableBytes, maxFragmentLength)); _ = flags.EndOfHeaders(!headerBlock.IsReadable()); int payloadLength = fragment.ReadableBytes + nonFragmentLength; IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.PushPromiseFrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, payloadLength, Http2FrameTypes.PushPromise, flags, streamId); WritePaddingLength(buf, padding); // Write out the promised stream ID. _ = buf.WriteInt(promisedStreamId); _ = ctx.WriteAsync(buf, promiseAggregator.NewPromise()); // Write the first fragment. _ = ctx.WriteAsync(fragment, promiseAggregator.NewPromise()); // Write out the padding, if any. if (PaddingBytes(padding) > 0) { _ = ctx.WriteAsync(ZeroBuffer.Slice(0, PaddingBytes(padding)), promiseAggregator.NewPromise()); } if (!flags.EndOfHeaders()) { _ = WriteContinuationFramesAsync(ctx, streamId, headerBlock, promiseAggregator); } } catch (Http2Exception e) { promiseAggregator.SetException(e); } catch (Exception t) { promiseAggregator.SetException(t); _ = promiseAggregator.DoneAllocatingPromises(); throw; } finally { if (headerBlock is object) { _ = headerBlock.Release(); } } _ = promiseAggregator.DoneAllocatingPromises(); return(promiseAggregator.Task); }