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); } } }
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; }
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); } }
/// <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); }
public StreamException(int streamId, Http2Error error, string message, Exception cause) : base(error, message, cause, ShutdownHint.NoShutdown) { StreamId = streamId; }
public Http2Exception(Http2Error error, ShutdownHint shutdownHint) { Error = error; ShutdownHint = shutdownHint; }
public ClosedStreamCreationException(Http2Error error, string message, Exception cause) : base(error, message, cause) { }
/// <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)); }
public ClosedStreamCreationException(Http2Error error) : base(error) { }
public Http2Exception(Http2Error error, string message, Exception cause) : this(error, message, cause, ShutdownHint.HardShutdown) { }
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); } }
/// <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)); }
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); } }
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); }
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)); }
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)); }
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)); } }
public HeaderListSizeException(int streamId, Http2Error error, string message, bool decode) : base(streamId, error, message) { DuringDecode = decode; }
public virtual void OnRstStreamRead(IChannelHandlerContext ctx, int streamId, Http2Error errorCode) { _listener.OnRstStreamRead(ctx, streamId, errorCode); }
public CompositeStreamException(Http2Error error, int initialCapacity) : base(error, ShutdownHint.NoShutdown) { _exceptions = new List <StreamException>(initialCapacity); }
public Http2Exception(Http2Error error) : this(error, ShutdownHint.HardShutdown) { }
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)); }
/// <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)); }
/// <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)); }