public virtual Task WritePriorityAsync(IChannelHandlerContext ctx, int streamId, int streamDependency, short weight, bool exclusive, IPromise promise) { try { if ((uint)(streamId - 1) > SharedConstants.TooBigOrNegative) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.StreamID); } if (streamDependency <= 0) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.StreamDependency); } VerifyWeight(weight); IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.PriorityFrameLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, Http2CodecUtil.PriorityEntryLength, Http2FrameTypes.Priority, new Http2Flags(), streamId); _ = 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); return(ctx.WriteAsync(buf, promise)); } catch (Exception t) { promise.SetException(t); return(promise.Task); } }
public virtual Task WriteSettingsAsync(IChannelHandlerContext ctx, Http2Settings settings, IPromise promise) { try { if (settings is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.settings); } int payloadLength = Http2CodecUtil.SettingEntryLength * settings.Count; IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength + settings.Count * Http2CodecUtil.SettingEntryLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, payloadLength, Http2FrameTypes.Settings, new Http2Flags(), 0); foreach (KeyValuePair <char, long> entry in settings) { _ = buf.WriteChar(entry.Key); _ = buf.WriteInt((int)entry.Value); } return(ctx.WriteAsync(buf, promise)); } catch (Exception t) { promise.SetException(t); return(promise.Task); } }
static void ReadGoAwayFrame(IChannelHandlerContext ctx, IByteBuffer payload, int payloadEndIndex, IHttp2FrameListener listener) { int lastStreamId = Http2CodecUtil.ReadUnsignedInt(payload); var errorCode = (Http2Error)payload.ReadUnsignedInt(); IByteBuffer debugData = payload.ReadSlice(payloadEndIndex - payload.ReaderIndex); listener.OnGoAwayRead(ctx, lastStreamId, errorCode, debugData); }
public void SetMaxFrameSize(int max) { if (!Http2CodecUtil.IsMaxFrameSizeValid(max)) { ThrowHelper.ThrowConnectionError_InvalidMaxFrameSizeSpecifiedInSentSettings(max); } _maxFrameSize = max; }
/// <summary> /// Creates an HTTP2-Settings header with the given payload. The payload buffer is released. /// </summary> /// <param name="ctx"></param> /// <param name="payload"></param> /// <returns></returns> private static IByteBuffer CreateSettingsFrame(IChannelHandlerContext ctx, IByteBuffer payload) { var frame = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength + payload.ReadableBytes); Http2CodecUtil.WriteFrameHeader(frame, payload.ReadableBytes, Http2FrameTypes.Settings, new Http2Flags(), 0); _ = frame.WriteBytes(payload); _ = payload.Release(); return(frame); }
public void Finish() { if (_exceededMaxLength) { Http2CodecUtil.HeaderListSizeExceeded(_streamId, _maxHeaderListSize, true); } else if (_validationException is object) { ThrowValidationException(); } }
public virtual Task WritePingAsync(IChannelHandlerContext ctx, bool ack, long data, IPromise promise) { var flags = ack ? new Http2Flags().Ack(true) : new Http2Flags(); var buf = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength + Http2CodecUtil.PingFramePayloadLength); // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership // in the catch block. Http2CodecUtil.WriteFrameHeaderInternal(buf, Http2CodecUtil.PingFramePayloadLength, Http2FrameTypes.Ping, flags, 0); _ = buf.WriteLong(data); return(ctx.WriteAsync(buf, promise)); }
void ReadWindowUpdateFrame(IChannelHandlerContext ctx, IByteBuffer payload, IHttp2FrameListener listener) { int windowSizeIncrement = Http2CodecUtil.ReadUnsignedInt(payload); if (0u >= (uint)windowSizeIncrement) { ThrowHelper.ThrowStreamError_ReceivedWindowUpdateWithDelta0ForStream(_streamId); } listener.OnWindowUpdateRead(ctx, _streamId, windowSizeIncrement); }
/// <summary> /// Exposed Used for testing only! Default values used in the initial settings frame are overridden intentionally /// for testing but violate the RFC if used outside the scope of testing. /// </summary> /// <param name="validateHeaders"></param> /// <param name="hpackDecoder"></param> internal DefaultHttp2HeadersDecoder(bool validateHeaders, HpackDecoder hpackDecoder) { if (hpackDecoder is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.hpackDecoder); } _hpackDecoder = hpackDecoder; _validateHeaders = validateHeaders; _maxHeaderListSizeGoAway = Http2CodecUtil.CalculateMaxHeaderListSizeGoAway(hpackDecoder.GetMaxHeaderListSize()); }
/// <summary> /// Construct a new headers message. /// </summary> /// <param name="headers">the non-<c>null</c> headers to send</param> /// <param name="endStream">whether these headers should terminate the stream</param> /// <param name="padding">additional bytes that should be added to obscure the true content size. Must be between 0 and /// 256 (inclusive).</param> public DefaultHttp2HeadersFrame(IHttp2Headers headers, bool endStream, int padding) { if (headers is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.headers); } _headers = headers; _endStream = endStream; Http2CodecUtil.VerifyPadding(padding); _padding = padding; }
static void VerifyStandardSetting(int key, long value) { switch (key) { case Http2CodecUtil.SettingsHeaderTableSize: if (value < Http2CodecUtil.MinHeaderTableSize || value > Http2CodecUtil.MaxHeaderTableSize) { ThrowHelper.ThrowArgumentException_InvalidHeaderTableSize(value); } break; case Http2CodecUtil.SettingsEnablePush: if (value != 0L && value != 1L) { ThrowHelper.ThrowArgumentException_InvalidEnablePush(value); } break; case Http2CodecUtil.SettingsMaxConcurrentStreams: if (value < Http2CodecUtil.MinConcurrentStreams || value > Http2CodecUtil.MaxConcurrentStreams) { ThrowHelper.ThrowArgumentException_InvalidConcurrentStreams(value); } break; case Http2CodecUtil.SettingsInitialWindowSize: if (value < Http2CodecUtil.MinInitialWindowSize || value > Http2CodecUtil.MaxInitialWindowSize) { ThrowHelper.ThrowArgumentException_InvalidInitialWindowSize(value); } break; case Http2CodecUtil.SettingsMaxFrameSize: if (!Http2CodecUtil.IsMaxFrameSizeValid(value)) { ThrowHelper.ThrowArgumentException_InvalidMaxFrameSize(value); } break; case Http2CodecUtil.SettingsMaxHeaderListSize: if (value < Http2CodecUtil.MinHeaderListSize || value > Http2CodecUtil.MaxHeaderListSize) { ThrowHelper.ThrowArgumentException_InvalidHeaderListSize(value); } break; default: // Non-standard HTTP/2 setting - don't do validation. break; } }
public virtual Task WriteSettingsAckAsync(IChannelHandlerContext ctx, IPromise promise) { try { IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, 0, Http2FrameTypes.Settings, new Http2Flags().Ack(true), 0); return(ctx.WriteAsync(buf, promise)); } catch (Exception t) { promise.SetException(t); return(promise.Task); } }
private void WriteHeadersFrame(IChannelHandlerContext ctx, IHttp2HeadersFrame headersFrame, IPromise promise) { if (Http2CodecUtil.IsStreamIdValid(headersFrame.Stream.Id)) { _ = Encoder.WriteHeadersAsync(ctx, headersFrame.Stream.Id, headersFrame.Headers, headersFrame.Padding, headersFrame.IsEndStream, promise); } else { var stream = (DefaultHttp2FrameStream)headersFrame.Stream; var connection = Connection; var streamId = connection.Local.IncrementAndGetNextStreamId; if (streamId < 0) { promise.SetException(new Http2NoMoreStreamIdsException()); // Simulate a GOAWAY being received due to stream exhaustion on this connection. We use the maximum // valid stream ID for the current peer. OnHttp2Frame(ctx, new DefaultHttp2GoAwayFrame(connection.IsServer ? int.MaxValue : int.MaxValue - 1, Http2Error.NoError, ByteBufferUtil.WriteAscii(ctx.Allocator, "Stream IDs exhausted on local stream creation"))); return; } stream.Id = streamId; // Use a Map to store all pending streams as we may have multiple. This is needed as if we would store the // stream in a field directly we may override the stored field before onStreamAdded(...) was called // and so not correctly set the property for the buffered stream. // // See https://github.com/netty/netty/issues/8692 var result = _frameStreamToInitializeMap.TryAdd(streamId, stream); // We should not re-use ids. Debug.Assert(result); _ = Encoder.WriteHeadersAsync(ctx, streamId, headersFrame.Headers, headersFrame.Padding, headersFrame.IsEndStream, promise); if (!promise.IsCompleted) { _ = Interlocked.Increment(ref v_numBufferedStreams); // Clean up the stream being initialized if writing the headers fails and also // decrement the number of buffered streams. _ = promise.Task.ContinueWith(ResetNufferedStreamsAction, (this, streamId), TaskContinuationOptions.ExecuteSynchronously); } else { HandleHeaderFuture(promise.Task, streamId); } } }
public virtual Task WriteGoAwayAsync(IChannelHandlerContext ctx, int lastStreamId, Http2Error errorCode, IByteBuffer debugData, IPromise promise) { SimplePromiseAggregator promiseAggregator = new SimplePromiseAggregator(promise); try { if (lastStreamId < 0) { ThrowHelper.ThrowArgumentException_PositiveOrZero(ExceptionArgument.LastStreamId); } VerifyErrorCode((long)errorCode); int payloadLength = 8 + debugData.ReadableBytes; IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.GoAwayFrameHeaderLength); // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership // in the catch block. Http2CodecUtil.WriteFrameHeaderInternal(buf, payloadLength, Http2FrameTypes.GoAway, new Http2Flags(), 0); _ = buf.WriteInt(lastStreamId); _ = buf.WriteInt((int)errorCode); _ = ctx.WriteAsync(buf, promiseAggregator.NewPromise()); } catch (Exception t) { try { _ = debugData.Release(); } finally { promiseAggregator.SetException(t); _ = promiseAggregator.DoneAllocatingPromises(); } return(promiseAggregator.Task); } try { _ = ctx.WriteAsync(debugData, promiseAggregator.NewPromise()); } catch (Exception t) { promiseAggregator.SetException(t); } _ = promiseAggregator.DoneAllocatingPromises(); return(promiseAggregator.Task); }
/// <summary> /// Construct a new data message. /// </summary> /// <param name="content">non-<c>null</c> payload</param> /// <param name="endStream">whether this data should terminate the stream</param> /// <param name="padding">additional bytes that should be added to obscure the true content size. Must be between 0 and /// 256 (inclusive).</param> public DefaultHttp2DataFrame(IByteBuffer content, bool endStream, int padding) { if (content is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.content); } _content = content; _endStream = endStream; Http2CodecUtil.VerifyPadding(padding); _padding = padding; if (Content.ReadableBytes + (long)padding > int.MaxValue) { ThrowHelper.ThrowArgumentException_InvalidContendAndPadding(); } _initialFlowControlledBytes = content.ReadableBytes + padding; }
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()); }
public virtual Task WriteFrameAsync(IChannelHandlerContext ctx, Http2FrameTypes frameType, int streamId, Http2Flags flags, IByteBuffer payload, IPromise promise) { SimplePromiseAggregator promiseAggregator = new SimplePromiseAggregator(promise); try { if (streamId < 0) { ThrowHelper.ThrowArgumentException_PositiveOrZero(ExceptionArgument.StreamID); } IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.FrameHeaderLength); // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership // in the catch block. Http2CodecUtil.WriteFrameHeaderInternal(buf, payload.ReadableBytes, frameType, flags, streamId); _ = ctx.WriteAsync(buf, promiseAggregator.NewPromise()); } catch (Exception t) { try { _ = payload.Release(); } finally { promiseAggregator.SetException(t); _ = promiseAggregator.DoneAllocatingPromises(); } return(promiseAggregator.Task); } try { _ = ctx.WriteAsync(payload, promiseAggregator.NewPromise()); } catch (Exception t) { promiseAggregator.SetException(t); } _ = promiseAggregator.DoneAllocatingPromises(); return(promiseAggregator.Task); }
void EncodeHeadersEnforceMaxHeaderListSize(int streamId, IByteBuffer output, IHttp2Headers headers, ISensitivityDetector sensitivityDetector) { long headerSize = 0; // To ensure we stay consistent with our peer check the size is valid before we potentially modify HPACK state. foreach (HeaderEntry <ICharSequence, ICharSequence> header in headers) { ICharSequence name = header.Key; ICharSequence value = header.Value; // OK to increment now and check for bounds after because this value is limited to unsigned int and will not // overflow. headerSize += HpackHeaderField.SizeOf(name, value); if (headerSize > _maxHeaderListSize) { Http2CodecUtil.HeaderListSizeExceeded(streamId, _maxHeaderListSize, false); } } EncodeHeadersIgnoreMaxHeaderListSize(@output, headers, sensitivityDetector); }
public virtual Task WriteRstStreamAsync(IChannelHandlerContext ctx, int streamId, Http2Error errorCode, IPromise promise) { try { if ((uint)(streamId - 1) > SharedConstants.TooBigOrNegative) { ThrowHelper.ThrowArgumentException_Positive(ExceptionArgument.StreamID); } VerifyErrorCode((long)errorCode); IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.RstStreamFrameLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, Http2CodecUtil.IntFieldLength, Http2FrameTypes.RstStream, new Http2Flags(), streamId); _ = buf.WriteInt((int)errorCode); return(ctx.WriteAsync(buf, promise)); } catch (Exception t) { promise.SetException(t); return(promise.Task); } }
public virtual Task WriteWindowUpdateAsync(IChannelHandlerContext ctx, int streamId, int windowSizeIncrement, IPromise promise) { try { if (streamId < 0) { ThrowHelper.ThrowArgumentException_PositiveOrZero(ExceptionArgument.StreamID); } VerifyWindowSizeIncrement(windowSizeIncrement); IByteBuffer buf = ctx.Allocator.Buffer(Http2CodecUtil.WindowUpdateFrameLength); Http2CodecUtil.WriteFrameHeaderInternal(buf, Http2CodecUtil.IntFieldLength, Http2FrameTypes.WindowUpdate, new Http2Flags(), streamId); _ = buf.WriteInt(windowSizeIncrement); return(ctx.WriteAsync(buf, promise)); } catch (Exception t) { promise.SetException(t); return(promise.Task); } }
/// <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); }
public void UpdateStreamableBytes(IStreamByteDistributorStreamState streamState) { GetState(streamState.Stream).UpdateStreamableBytes(Http2CodecUtil.StreamableBytes(streamState), streamState.HasFrame, streamState.WindowSize); }
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); }
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); }
/// <summary> /// The local header size maximum has been exceeded while accumulating bytes. /// </summary> /// <exception cref="Http2Exception">A connection error indicating too much data has been received.</exception> void HeaderSizeExceeded() { Close(); Http2CodecUtil.HeaderListSizeExceeded(_reader._headersDecoder.Configuration.MaxHeaderListSizeGoAway); }
void ProcessHeaderState(IByteBuffer input) { if (input.ReadableBytes < Http2CodecUtil.FrameHeaderLength) { // Wait until the entire frame header has been read. return; } // Read the header and prepare the unmarshaller to read the frame. _payloadLength = input.ReadUnsignedMedium(); if (_payloadLength > _maxFrameSize) { ThrowHelper.ThrowConnectionError_FrameLengthExceedsMaximum(_payloadLength, _maxFrameSize); } _frameType = (Http2FrameTypes)input.ReadByte(); _flags = new Http2Flags(input.ReadByte()); _streamId = Http2CodecUtil.ReadUnsignedInt(input); // We have consumed the data, next time we read we will be expecting to read the frame payload. _readingHeaders = false; switch (_frameType) { case Http2FrameTypes.Data: VerifyDataFrame(); break; case Http2FrameTypes.Headers: VerifyHeadersFrame(); break; case Http2FrameTypes.Priority: VerifyPriorityFrame(); break; case Http2FrameTypes.RstStream: VerifyRstStreamFrame(); break; case Http2FrameTypes.Settings: VerifySettingsFrame(); break; case Http2FrameTypes.PushPromise: VerifyPushPromiseFrame(); break; case Http2FrameTypes.Ping: VerifyPingFrame(); break; case Http2FrameTypes.GoAway: VerifyGoAwayFrame(); break; case Http2FrameTypes.WindowUpdate: VerifyWindowUpdateFrame(); break; case Http2FrameTypes.Continuation: VerifyContinuationFrame(); break; default: // Unknown frame type, could be an extension. VerifyUnknownFrame(); break; } }