public static IFlowState <TResult> SelectMany <TSource, TResult>(this IFlowState <TSource> state, Func <TSource, IFlowState <TResult> > valueSelector, Func <Exception, IFlowState <TResult> > errorSelector) { switch (state) { case Value <TSource> val: try { return(valueSelector(val.Val)); } catch (Exception e) { return(new Error <TResult>(e)); } case Error <TSource> err: try { return(errorSelector(err.Ex)); } catch (Exception e) { return(new Error <TResult>(e)); } default: return(new Pending <TResult>()); } }
public static IFlowState <TResult> Select <TSource, TResult>(this IFlowState <TSource> state, Func <TSource, TResult> valueSelector, Func <Exception, TResult> errorSelector) { return(state.SelectMany(t => new Value <TResult>(valueSelector(t)), e => new Value <TResult>(errorSelector(e)))); }
public override void OnStreamClosed(IHttp2Stream stream) { try { // When a stream is closed, consume any remaining bytes so that they // are restored to the connection window. IFlowState state = GetState(stream); int unconsumedBytes = state.UnconsumedBytes; if (_ctx is object && unconsumedBytes > 0) { if (ConsumeAllBytes(state, unconsumedBytes)) { // As the user has no real control on when this callback is used we should better // call flush() if we produced any window update to ensure we not stale. _ = _ctx.Flush(); } } } catch (Http2Exception) { throw; } finally { // Unconditionally reduce the amount of memory required for flow control because there is no // object allocation costs associated with doing so and the stream will not have any more // local flow control state to keep track of anymore. _ = stream.SetProperty(_stateKey, REDUCED_FLOW_STATE); } }
/// <summary> /// The window update ratio is used to determine when a window update must be sent. If the ratio /// of bytes processed since the last update has meet or exceeded this ratio then a window update will /// be sent. This window update ratio will only be applied to <c>streamId</c>. /// <para>Note it is the responsibly of the caller to ensure that the /// initial <c>SETTINGS</c> frame is sent before this is called. It would /// be considered a <see cref="Http2Error.ProtocolError"/> if a <c>WINDOW_UPDATE</c> /// was generated by this method before the initial <c>SETTINGS</c> frame is sent.</para> /// </summary> /// <param name="stream">the stream for which <paramref name="ratio"/> applies to.</param> /// <param name="ratio">the ratio to use when checking if a <c>WINDOW_UPDATE</c> is determined necessary.</param> /// <remarks>If a protocol-error occurs while generating <c>WINDOW_UPDATE</c> frames</remarks> public void WindowUpdateRatio(IHttp2Stream stream, float ratio) { Debug.Assert(_ctx is object && _ctx.Executor.InEventLoop); CheckValidRatio(ratio); IFlowState state = GetState(stream); state.WindowUpdateRatio(ratio); _ = state.WriteWindowUpdateIfNeeded(); }
public void IncrementWindowSize(IHttp2Stream stream, int delta) { Debug.Assert(_ctx is object && _ctx.Executor.InEventLoop); IFlowState state = GetState(stream); // Just add the delta to the stream-specific initial window size so that the next time the window // expands it will grow to the new initial size. state.IncrementInitialStreamWindow(delta); _ = state.WriteWindowUpdateIfNeeded(); }
public void SetFlowState(FlowState.State state) { if (_currentFlowState != null) { _currentFlowState.Exit(); } if (_stateMachine.ContainsKey(state)) { _currentFlowState = _stateMachine[state]; _currentFlowState.Enter(); } else { Debug.LogError("NO flow state: " + state + " exists in flow state map"); } }
public bool Visit(IHttp2Stream stream) { try { // Increment flow control window first so state will be consistent if overflow is detected. IFlowState state = _controller.GetState(stream); state.IncrementFlowControlWindows(_delta); state.IncrementInitialStreamWindow(_delta); } catch (StreamException e) { if (_compositeException is null) { _compositeException = new CompositeStreamException(e.Error, 4); } _compositeException.Add(e); } return(true); }
/// <summary> /// Constructs a controller with the given settings. /// </summary> /// <param name="connection">the connection state.</param> /// <param name="windowUpdateRatio">the window percentage below which to send a <c>WINDOW_UPDATE</c>.</param> /// <param name="autoRefillConnectionWindow">if <c>true</c>, effectively disables the connection window /// in the flow control algorithm as they will always refill automatically without requiring the /// application to consume the bytes. When enabled, the maximum bytes you must be prepared to /// queue is proportional to <c>maximum number of concurrent streams * the initial window /// size per stream</c> /// (<a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_MAX_CONCURRENT_STREAMS</a> /// <a href="https://tools.ietf.org/html/rfc7540#section-6.5.2">SETTINGS_INITIAL_WINDOW_SIZE</a>). /// </param> public DefaultHttp2LocalFlowController(IHttp2Connection connection, float windowUpdateRatio, bool autoRefillConnectionWindow) { if (connection is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.connection); } _connection = connection; WindowUpdateRatio(windowUpdateRatio); // Add a flow state for the connection. _stateKey = connection.NewKey(); IFlowState connectionState = autoRefillConnectionWindow ? new AutoRefillState(this, connection.ConnectionStream, _initialWindowSize) : new DefaultState(this, connection.ConnectionStream, _initialWindowSize); _ = connection.ConnectionStream.SetProperty(_stateKey, connectionState); // Register for notification of new streams. connection.AddListener(this); }
public void ReceiveFlowControlledFrame(IHttp2Stream stream, IByteBuffer data, int padding, bool endOfStream) { Debug.Assert(_ctx is object && _ctx.Executor.InEventLoop); int dataLength = data.ReadableBytes + padding; // Apply the connection-level flow control IFlowState connectionState = ConnectionState(); connectionState.ReceiveFlowControlledFrame(dataLength); if (stream is object && !IsClosed(stream)) { // Apply the stream-level flow control IFlowState state = GetState(stream); state.EndOfStream(endOfStream); state.ReceiveFlowControlledFrame(dataLength); } else if (dataLength > 0) { // Immediately consume the bytes for the connection window. _ = connectionState.ConsumeBytes(dataLength); } }
public bool Equals(IFlowState <FlowState> other) { return(Equals(other as FlowState)); }
public static IFlowState <TResult> Select <TSource, TResult>(this IFlowState <TSource> state, Func <TSource, TResult> selector) { return(state.SelectMany(t => new Value <TResult>(selector(t)))); }
public static IFlowState <TResult> SelectMany <TSource, TFlow, TResult>(this IFlowState <TSource> source, Func <TSource, IFlowState <TFlow> > flowSelector, Func <TSource, TFlow, TResult> resultSelector) { return(source.SelectMany(s => flowSelector(s).Select(t => resultSelector(s, t)))); }
public static IFlowState <TResult> SelectMany <TSource, TResult>(this IFlowState <TSource> state, Func <TSource, IFlowState <TResult> > selector) { return(state.SelectMany(selector, e => new Error <TResult>(e))); }
public static IFlowState <T> Flatten <T>(this IFlowState <IFlowState <T> > state) { return(state.SelectMany(t => t)); }
private bool ConsumeAllBytes(IFlowState state, int numBytes) { return(ConnectionState().ConsumeBytes(numBytes) | state.ConsumeBytes(numBytes)); }