public void AppendToHeaderList(ICharSequence name, ICharSequence value) { _headersLength += HpackHeaderField.SizeOf(name, value); _exceededMaxLength |= _headersLength > _maxHeaderListSize; if (_exceededMaxLength || _validationException is object) { // We don't store the header since we've already failed validation requirements. return; } if (_validate) { try { _previousType = HpackDecoder.Validate(_streamId, name, _previousType); } catch (Http2Exception ex) { _validationException = ex; return; } } _ = _headers.Add(name, value); }
private IPromise HandleOutstandingControlFrames(IChannelHandlerContext ctx, IPromise promise) { if (!_limitReached) { if (_outstandingControlFrames == _maxOutstandingControlFrames) { // Let's try to flush once as we may be able to flush some of the control frames. _ = ctx.Flush(); } if (_outstandingControlFrames == _maxOutstandingControlFrames) { _limitReached = true; Http2Exception exception = ThrowHelper.GetConnectionError_Maximum_number_of_outstanding_control_frames_reached(_maxOutstandingControlFrames); if (Logger.InfoEnabled) { Logger.Maximum_number_of_outstanding_control_frames_reached(_maxOutstandingControlFrames, ctx, exception); } // First notify the Http2LifecycleManager and then close the connection. _lifecycleManager.OnError(ctx, true, exception); _ = ctx.CloseAsync(); } _outstandingControlFrames++; // We did not reach the limit yet, add the listener to decrement the number of outstanding control frames // once the promise was completed var newPromise = promise is object?promise.Unvoid() : ctx.NewPromise(); _ = newPromise.Task.ContinueWith(OutstandingControlFramesListenerAction, this, TaskContinuationOptions.ExecuteSynchronously); return(newPromise); } return(promise); }
/// <summary> /// Discards this <see cref="IHttp2RemoteFlowControlled"/>, writing an error. If this frame is in the pending queue, /// the unwritten bytes are removed from this branch of the priority tree. /// </summary> /// <param name="frame"></param> /// <param name="cause"></param> void WriteError(IHttp2RemoteFlowControlled frame, Http2Exception cause) { IChannelHandlerContext ctx = _controller._ctx; Debug.Assert(ctx is object); DecrementPendingBytes(frame.Size, true); frame.Error(ctx, cause); }
public void ReadFrame(IChannelHandlerContext ctx, IByteBuffer input, IHttp2FrameListener listener) { if (_readError) { _ = input.SkipBytes(input.ReadableBytes); return; } try { do { if (_readingHeaders) { ProcessHeaderState(input); if (_readingHeaders) { // Wait until the entire header has arrived. return; } } // The header is complete, fall into the next case to process the payload. // This is to ensure the proper handling of zero-length payloads. In this // case, we don't want to loop around because there may be no more data // available, causing us to exit the loop. Instead, we just want to perform // the first pass at payload processing now. ProcessPayloadState(ctx, input, listener); if (!_readingHeaders) { // Wait until the entire payload has arrived. return; } }while (input.IsReadable()); } catch (Http2Exception e) { _readError = !Http2Exception.IsStreamError(e); throw; } catch (Http2RuntimeException) { _readError = true; throw; } catch (Exception) { _readError = true; throw; } }
/// <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); }
/// <summary> /// Get the stream id associated with an exception. /// </summary> /// <param name="e">The exception to get the stream id for.</param> /// <returns><see cref="Http2CodecUtil.ConnectionStreamId"/> if <paramref name="e"/> is a connection error. /// Otherwise the stream id associated with the stream error.</returns> public static int GetStreamId(Http2Exception e) { return(e is StreamException streamException ? streamException.StreamId : Http2CodecUtil.ConnectionStreamId); }
/// <summary> /// Check if an exception is isolated to a single stream or the entire connection. /// </summary> /// <param name="e">The exception to check.</param> /// <returns><c>true</c> if <paramref name="e"/> is an instance of <see cref="StreamException"/>. /// <c>false</c> otherwise.</returns> public static bool IsStreamError(Http2Exception e) { return(e is StreamException); }
/// <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)); }
/// <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)); }
private static Http2Exception GetStreamNeverExistedException(int streamId) { return(Http2Exception.StreamError(streamId, Http2Error.ProtocolError, "Stream never existed")); }
public static void HeaderListSizeExceeded(long maxHeaderListSize) { throw Http2Exception.ConnectionError(Http2Error.ProtocolError, "Header size exceeded max allowed size ({0})", maxHeaderListSize); }
public static void HeaderListSizeExceeded(int streamId, long maxHeaderListSize, bool onDecode) { throw Http2Exception.HeaderListSizeError(streamId, Http2Error.ProtocolError, onDecode, "Header size exceeded max allowed size ({0})", maxHeaderListSize); }
public static void Maximum_number_of_outstanding_control_frames_reached(this IInternalLogger logger, int maxOutstandingControlFrames, IChannelHandlerContext ctx, Http2Exception exception) { logger.Info("Maximum number {} of outstanding control frames reached. Closing channel {}", maxOutstandingControlFrames, ctx.Channel, exception); }