Example #1
0
        private async Task HandleFrameProcessingError(
            Http2Error err)
        {
            if (err.StreamId == 0)
            {
                await InitiateGoAway(err.Code, true);
            }
            else
            {
                StreamImpl stream = null;
                lock (shared.Mutex)
                {
                    shared.streamMap.TryGetValue(err.StreamId, out stream);
                }

                if (stream != null)
                {
                    await stream.Reset(err.Code, false);
                }
                else
                {
                    var fh = new FrameHeader
                    {
                        StreamId = err.StreamId,
                        Type     = FrameType.ResetStream,
                        Flags    = 0,
                    };
                    var resetData = new ResetFrameData
                    {
                        ErrorCode = err.Code,
                    };
                    await writer.WriteResetStream(fh, resetData);
                }
            }
        }
Example #2
0
 public Http2GoAwayException(int lastStreamId, Http2Error errorCode, byte[] debugData)
     : base(Http2Error.StreamClosed)
 {
     LastStreamId = lastStreamId;
     ErrorCode    = errorCode;
     DebugData    = debugData;
 }
        public Http2FrameStreamException(IHttp2FrameStream stream, Http2Error error, Exception cause)
            : base(cause.Message, cause)
        {
            if (stream is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.stream);
            }

            Stream = stream;
            Error  = error;
        }
Example #4
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);
        }
        private void CancelGoAwayStreams(int lastStreamId, Http2Error errorCode, IByteBuffer debugData)
        {
            if (0u >= (uint)_pendingStreams.Count)
            {
                return;
            }

            var e = new Http2GoAwayException(lastStreamId, errorCode, ByteBufferUtil.GetBytes(debugData));

            var keyList = new List <int>(_pendingStreams.Count);

            foreach (var stream in _pendingStreams.Values)
            {
                if (stream._streamId > lastStreamId)
                {
                    keyList.Add(stream._streamId);
                    stream.Close(e);
                }
            }
            foreach (var item in keyList)
            {
                _ = _pendingStreams.Remove(item);
            }
        }
Example #6
0
            /// <summary>
            /// Clears the pending queue and writes errors for each remaining frame.
            /// </summary>
            /// <param name="error">the <see cref="Http2Error"/> to use.</param>
            /// <param name="cause">the <see cref="Exception"/> that caused this method to be invoked.</param>
            internal void Cancel(Http2Error error, Exception cause = null)
            {
                _cancelled = true;
                // Ensure that the queue can't be modified while we are writing.
                if (_writing)
                {
                    return;
                }

                if (_pendingWriteQueue.TryRemoveFromFront(out IHttp2RemoteFlowControlled frame))
                {
                    // Only create exception once and reuse to reduce overhead of filling in the stacktrace.
                    Http2Exception exception = ThrowHelper.GetStreamError_StreamClosedBeforeWriteCouldTakePlace(
                        _stream.Id, error, cause);
                    do
                    {
                        WriteError(frame, exception);
                    } while (_pendingWriteQueue.TryRemoveFromFront(out frame));
                }

                _controller._streamByteDistributor.UpdateStreamableBytes(this);

                _controller._monitor.StateCancelled(this);
            }
Example #7
0
        public StreamException(int streamId, Http2Error error, string message, Exception cause)
            : base(error, message, cause, ShutdownHint.NoShutdown)

        {
            StreamId = streamId;
        }
Example #8
0
 public Http2Exception(Http2Error error, ShutdownHint shutdownHint)
 {
     Error        = error;
     ShutdownHint = shutdownHint;
 }
Example #9
0
 public ClosedStreamCreationException(Http2Error error, string message, Exception cause)
     : base(error, message, cause)
 {
 }
Example #10
0
 /// <summary>
 /// A specific stream error resulting from failing to decode headers that exceeds the max header size list.
 /// If the <paramref name="id"/> is not <see cref="Http2CodecUtil.ConnectionStreamId"/> then a
 /// <see cref="StreamException"/> will be returned. Otherwise the error is considered a
 /// connection error and a <see cref="Http2Exception"/> is returned.
 /// </summary>
 /// <param name="id">The stream id for which the error is isolated to.</param>
 /// <param name="error">The type of error as defined by the HTTP/2 specification.</param>
 /// <param name="onDecode">Whether this error was caught while decoding headers</param>
 /// <param name="fmt">string with the content and format for the additional debug data.</param>
 /// <param name="args">Objects which fit into the format defined by <paramref name="fmt"/>.</param>
 /// <returns>If the <paramref name="id"/> is not
 /// <see cref="Http2CodecUtil.ConnectionStreamId"/> then a <see cref="HeaderListSizeException"/>
 /// will be returned. Otherwise the error is considered a connection error and a <see cref="Http2Exception"/> is
 /// returned.</returns>
 public static Http2Exception HeaderListSizeError(int id, Http2Error error, bool onDecode, string fmt, params object[] args)
 {
     return(Http2CodecUtil.ConnectionStreamId == id?
            Http2Exception.ConnectionError(error, fmt, args) :
                new HeaderListSizeException(id, error, args is object && (uint)args.Length > 0u ? string.Format(fmt, args) : fmt, onDecode));
 }
Example #11
0
 public ClosedStreamCreationException(Http2Error error)
     : base(error)
 {
 }
Example #12
0
 public Http2Exception(Http2Error error, string message, Exception cause)
     : this(error, message, cause, ShutdownHint.HardShutdown)
 {
 }
Example #13
0
 public void LogRstStream(Direction direction, IChannelHandlerContext ctx, int streamId, Http2Error errorCode)
 {
     if (IsEnabled())
     {
         _logger.Log(_level, "{} {} RST_STREAM: streamId={} errorCode={}",
                     ctx.Channel, direction, streamId, errorCode);
     }
 }
Example #14
0
 /// <summary>
 /// Use if an error has occurred which can not be isolated to a single stream, but instead applies
 /// to the entire connection.
 /// </summary>
 /// <param name="error">The type of error as defined by the HTTP/2 specification.</param>
 /// <param name="cause">The object which caused the error.</param>
 /// <param name="fmt">string with the content and format for the additional debug data.</param>
 /// <param name="args">Objects which fit into the format defined by <paramref name="fmt"/>.</param>
 /// <returns>An exception which can be translated into an HTTP/2 error.</returns>
 public static Http2Exception ConnectionError(Http2Error error, Exception cause, string fmt, params object[] args)
 {
     return(new Http2Exception(error, args is object && (uint)args.Length > 0u ? string.Format(fmt, args) : fmt, cause));
 }
 public virtual Task WriteRstStreamAsync(IChannelHandlerContext ctx, int streamId, Http2Error errorCode, IPromise promise)
 {
     return(_innerWriter.WriteRstStreamAsync(ctx, streamId, errorCode, promise));
 }
Example #16
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);
            }
        }
Example #17
0
        private async ValueTask <Http2Error?> HandleDataFrame(FrameHeader fh)
        {
            if (fh.StreamId == 0)
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.ProtocolError,
                    Message = "接收到无效的数据帧头",
                });
            }
            if ((fh.Flags & (byte)DataFrameFlags.Padded) != 0 &&
                fh.Length < 1)
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.ProtocolError,
                    Message = "帧太小,无法包含填充",
                });
            }
            if (fh.Length > localSettings.MaxFrameSize)
            {
                return(new Http2Error
                {
                    StreamId = 0,
                    Code = ErrorCode.FrameSizeError,
                    Message = "超过最大帧大小",
                });
            }

            var dataBuffer = config.BufferPool.Rent(fh.Length);

            try
            {
                await inputStream.ReadAll(
                    new ArraySegment <byte>(dataBuffer, 0, fh.Length));
            }
            catch (Exception)
            {
                config.BufferPool.Return(dataBuffer);
                throw;
            }

            var isPadded = (fh.Flags & (byte)DataFrameFlags.Padded) != 0;
            var padLen   = 0;
            var offset   = isPadded ? 1 : 0;
            var dataSize = fh.Length;

            if (isPadded)
            {
                padLen   = dataBuffer[0];
                dataSize = fh.Length - 1 - padLen;
                if (dataSize < 0)
                {
                    config.BufferPool.Return(dataBuffer);
                    return(new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.ProtocolError,
                        Message = "减法填充后帧太小",
                    });
                }
            }

            if (dataSize != 0)
            {
                if (dataSize > connReceiveFlowWindow)
                {
                    config.BufferPool.Return(dataBuffer);
                    return(new Http2Error
                    {
                        StreamId = 0,
                        Code = ErrorCode.FlowControlError,
                        Message = "超过接收窗口",
                    });
                }
                connReceiveFlowWindow -= dataSize;
            }

            StreamImpl stream = null;
            uint       lastIncomingStreamId;
            uint       lastOutgoingStreamId;

            lock (shared.Mutex)
            {
                lastIncomingStreamId = shared.LastIncomingStreamId;
                lastOutgoingStreamId = shared.LastOutgoingStreamId;
                shared.streamMap.TryGetValue(fh.StreamId, out stream);
            }

            Http2Error?processError = null;
            bool       streamTookBufferOwnership = false;

            if (stream != null)
            {
                processError = stream.PushBuffer(
                    new ArraySegment <byte>(dataBuffer, offset, dataSize),
                    (fh.Flags & (byte)DataFrameFlags.EndOfStream) != 0,
                    out streamTookBufferOwnership);
            }
            else
            {
                var isIdleStreamId = IsIdleStreamId(
                    fh.StreamId, lastOutgoingStreamId, lastIncomingStreamId);
                processError = new Http2Error
                {
                    StreamId = isIdleStreamId ? 0u : fh.StreamId,
                    Code     = ErrorCode.StreamClosed,
                    Message  = "接收到未知帧的数据",
                };
            }

            if (!streamTookBufferOwnership)
            {
                config.BufferPool.Return(dataBuffer);
            }
            dataBuffer = null;

            if (processError.HasValue && processError.Value.StreamId == 0)
            {
                return(processError);
            }

            var maxWindow            = Constants.InitialConnectionWindowSize;
            var possibleWindowUpdate = maxWindow - connReceiveFlowWindow;
            var windowUpdateAmount   = 0;

            if (possibleWindowUpdate >= (maxWindow / 2))
            {
                windowUpdateAmount     = possibleWindowUpdate;
                connReceiveFlowWindow += windowUpdateAmount;
            }

            if (windowUpdateAmount > 0)
            {
                var wfh = new FrameHeader
                {
                    StreamId = 0,
                    Type     = FrameType.WindowUpdate,
                    Flags    = 0,
                };

                var updateData = new WindowUpdateData
                {
                    WindowSizeIncrement = windowUpdateAmount,
                };

                try
                {
                    await writer.WriteWindowUpdate(wfh, updateData);
                }
                catch (Exception)
                {
                }
            }

            return(processError);
        }
Example #18
0
 public Task WriteGoAwayAsync(IChannelHandlerContext ctx, int lastStreamId, Http2Error errorCode, IByteBuffer debugData, IPromise promise)
 {
     _logger.LogGoAway(Direction.Outbound, ctx, lastStreamId, errorCode, debugData);
     return(_writer.WriteGoAwayAsync(ctx, lastStreamId, errorCode, debugData, promise));
 }
Example #19
0
 public Task WriteRstStreamAsync(IChannelHandlerContext ctx, int streamId, Http2Error errorCode, IPromise promise)
 {
     _logger.LogRstStream(Direction.Outbound, ctx, streamId, errorCode);
     return(_writer.WriteRstStreamAsync(ctx, streamId, errorCode, promise));
 }
Example #20
0
 public void LogGoAway(Direction direction, IChannelHandlerContext ctx, int lastStreamId, Http2Error errorCode, IByteBuffer debugData)
 {
     if (IsEnabled())
     {
         _logger.Log(_level, "{} {} GO_AWAY: lastStreamId={} errorCode={} length={} bytes={}",
                     ctx.Channel, direction, lastStreamId, errorCode, debugData.ReadableBytes, ToString(debugData));
     }
 }
Example #21
0
 public HeaderListSizeException(int streamId, Http2Error error, string message, bool decode)
     : base(streamId, error, message)
 {
     DuringDecode = decode;
 }
Example #22
0
 public virtual void OnRstStreamRead(IChannelHandlerContext ctx, int streamId, Http2Error errorCode)
 {
     _listener.OnRstStreamRead(ctx, streamId, errorCode);
 }
Example #23
0
 public CompositeStreamException(Http2Error error, int initialCapacity)
     : base(error, ShutdownHint.NoShutdown)
 {
     _exceptions = new List <StreamException>(initialCapacity);
 }
Example #24
0
 public Http2Exception(Http2Error error)
     : this(error, ShutdownHint.HardShutdown)
 {
 }
Example #25
0
 public Http2Exception(Http2Error error, string message, Exception cause, ShutdownHint shutdownHint)
     : base(message, cause)
 {
     Error        = error;
     ShutdownHint = shutdownHint;
 }
        public override void OnRstStreamRead(IChannelHandlerContext ctx, int streamId, Http2Error errorCode)
        {
            IHttp2Stream     stream = _connection.Stream(streamId);
            IFullHttpMessage msg    = GetMessage(stream);

            if (msg is object)
            {
                OnRstStreamRead(stream, msg);
            }
            _ = ctx.FireExceptionCaught(ThrowHelper.GetStreamError_Http2ToHttpLayerCaughtStreamReset(streamId, errorCode));
        }
Example #27
0
 /// <summary>
 /// Use if an error has occurred which can not be isolated to a single stream, but instead applies
 /// to the entire connection.
 /// </summary>
 /// <param name="error">The type of error as defined by the HTTP/2 specification.</param>
 /// <param name="fmt">string with the content and format for the additional debug data.</param>
 /// <param name="args">Objects which fit into the format defined by <paramref name="fmt"/>.</param>
 /// <returns>An exception which can be translated into an HTTP/2 error.</returns>
 public static Http2Exception ClosedStreamError(Http2Error error, string fmt, params object[] args)
 {
     return(new ClosedStreamCreationException(error, args is object && (uint)args.Length > 0u ? string.Format(fmt, args) : fmt));
 }
        public override Task WriteRstStreamAsync(IChannelHandlerContext ctx, int streamId, Http2Error errorCode, IPromise promise)
        {
            var newPromise = HandleOutstandingControlFrames(ctx, promise);

            if (newPromise is null)
            {
                return(promise.Task);
            }
            return(base.WriteRstStreamAsync(ctx, streamId, errorCode, newPromise));
        }
 public virtual Task WriteGoAwayAsync(IChannelHandlerContext ctx, int lastStreamId, Http2Error errorCode, IByteBuffer debugData, IPromise promise)
 {
     return(_innerWriter.WriteGoAwayAsync(ctx, lastStreamId, errorCode, debugData, promise));
 }
Example #30
0
 /// <summary>
 /// Use if an error which can be isolated to a single stream has occurred.  If the <paramref name="id"/> is not
 /// <see cref="Http2CodecUtil.ConnectionStreamId"/> then a <see cref="StreamException"/> will be returned.
 /// Otherwise the error is considered a connection error and a <see cref="Http2Exception"/> is returned.
 /// </summary>
 /// <param name="id">The stream id for which the error is isolated to.</param>
 /// <param name="error">The type of error as defined by the HTTP/2 specification.</param>
 /// <param name="cause">The object which caused the error.</param>
 /// <param name="fmt">string with the content and format for the additional debug data.</param>
 /// <param name="args">Objects which fit into the format defined by <paramref name="fmt"/>.</param>
 /// <returns>If the <paramref name="id"/> is not
 /// <see cref="Http2CodecUtil.ConnectionStreamId"/> then a <see cref="StreamException"/> will be returned.
 /// Otherwise the error is considered a connection error and a <see cref="Http2Exception"/> is returned.</returns>
 public static Http2Exception StreamError(int id, Http2Error error, Exception cause, string fmt, params object[] args)
 {
     return(Http2CodecUtil.ConnectionStreamId == id?
            Http2Exception.ConnectionError(error, cause, fmt, args) :
                new StreamException(id, error, args is object && (uint)args.Length > 0u ? string.Format(fmt, args) : fmt, cause));
 }