Пример #1
0
        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);
            }
        }
Пример #2
0
        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);
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        public void SetMaxFrameSize(int max)
        {
            if (!Http2CodecUtil.IsMaxFrameSizeValid(max))
            {
                ThrowHelper.ThrowConnectionError_InvalidMaxFrameSizeSpecifiedInSentSettings(max);
            }

            _maxFrameSize = max;
        }
Пример #5
0
        /// <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);
        }
Пример #6
0
 public void Finish()
 {
     if (_exceededMaxLength)
     {
         Http2CodecUtil.HeaderListSizeExceeded(_streamId, _maxHeaderListSize, true);
     }
     else if (_validationException is object)
     {
         ThrowValidationException();
     }
 }
Пример #7
0
        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));
        }
Пример #8
0
        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);
        }
Пример #9
0
        /// <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());
        }
Пример #10
0
        /// <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;
        }
Пример #11
0
        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;
            }
        }
Пример #12
0
 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);
     }
 }
Пример #13
0
        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);
                }
            }
        }
Пример #14
0
        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);
        }
Пример #15
0
        /// <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;
        }
Пример #16
0
        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());
        }
Пример #17
0
        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);
        }
Пример #18
0
        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);
        }
Пример #19
0
        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);
            }
        }
Пример #20
0
        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);
            }
        }
Пример #21
0
        /// <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);
        }
Пример #22
0
 public void UpdateStreamableBytes(IStreamByteDistributorStreamState streamState)
 {
     GetState(streamState.Stream).UpdateStreamableBytes(Http2CodecUtil.StreamableBytes(streamState),
                                                        streamState.HasFrame,
                                                        streamState.WindowSize);
 }
Пример #23
0
        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);
        }
Пример #24
0
        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);
        }
Пример #25
0
        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);
        }
Пример #26
0
 /// <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);
 }
Пример #27
0
        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;
            }
        }