internal void OnDataOrHeadersReceived(Http2Connection connection)
            {
                if (_state != State.Waiting)
                {
                    return;
                }

                long now     = Stopwatch.GetTimestamp();
                bool initial = _initialBurst > 0;

                if (initial || now - _pingSentTimestamp > PingIntervalInTicks)
                {
                    if (initial)
                    {
                        _initialBurst--;
                    }

                    // Send a PING
                    _pingCounter--;
                    if (NetEventSource.Log.IsEnabled())
                    {
                        connection.Trace($"[FlowControl] Sending RTT PING with payload {_pingCounter}");
                    }
                    connection.LogExceptions(connection.SendPingAsync(_pingCounter, isAck: false));
                    _pingSentTimestamp = now;
                    _state             = State.PingSent;
                }
            }
            private void AdjustWindowDynamic(int bytesConsumed, Http2Stream stream)
            {
                _deliveredBytes += bytesConsumed;

                if (_deliveredBytes < StreamWindowThreshold)
                {
                    return;
                }

                int             windowUpdateIncrement = _deliveredBytes;
                long            currentTime           = Stopwatch.GetTimestamp();
                Http2Connection connection            = stream.Connection;

                TimeSpan rtt = connection._rttEstimator.MinRtt;

                if (rtt > TimeSpan.Zero && _streamWindowSize < MaxStreamWindowSize)
                {
                    TimeSpan dt = Stopwatch.GetElapsedTime(_lastWindowUpdate, currentTime);

                    // We are detecting bursts in the amount of data consumed within a single 'dt' window update period.
                    // The value "_deliveredBytes / dt" correlates with the bandwidth of the connection.
                    // We need to extend the window, if the bandwidth-delay product grows over the current window size.
                    // To enable empirical fine tuning, we apply a configurable multiplier (_windowScaleThresholdMultiplier) to the window size, which defaults to 1.0
                    //
                    // The condition to extend the window is:
                    // (_deliveredBytes / dt) * rtt > _streamWindowSize * _windowScaleThresholdMultiplier
                    //
                    // Which is reordered into the form below, to avoid the division:
                    if (_deliveredBytes * (double)rtt.Ticks > _streamWindowSize * dt.Ticks * WindowScaleThresholdMultiplier)
                    {
                        int extendedWindowSize = Math.Min(MaxStreamWindowSize, _streamWindowSize * 2);
                        windowUpdateIncrement += extendedWindowSize - _streamWindowSize;
                        _streamWindowSize      = extendedWindowSize;

                        if (NetEventSource.Log.IsEnabled())
                        {
                            stream.Trace($"[FlowControl] Updated Stream Window. StreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}");
                        }

                        Debug.Assert(_streamWindowSize <= MaxStreamWindowSize);
                        if (_streamWindowSize == MaxStreamWindowSize)
                        {
                            if (NetEventSource.Log.IsEnabled())
                            {
                                stream.Trace($"[FlowControl] StreamWindowSize reached the configured maximum of {MaxStreamWindowSize}.");
                            }
                        }
                    }
                }

                _deliveredBytes = 0;

                Task sendWindowUpdateTask = connection.SendWindowUpdateAsync(stream.StreamId, windowUpdateIncrement);

                connection.LogExceptions(sendWindowUpdateTask);

                _lastWindowUpdate = currentTime;
            }
            private void AjdustWindowStatic(int bytesConsumed, Http2Stream stream)
            {
                _deliveredBytes += bytesConsumed;
                if (_deliveredBytes < StreamWindowThreshold)
                {
                    return;
                }

                int windowUpdateIncrement = _deliveredBytes;

                _deliveredBytes = 0;

                Http2Connection connection           = stream.Connection;
                Task            sendWindowUpdateTask = connection.SendWindowUpdateAsync(stream.StreamId, windowUpdateIncrement);

                connection.LogExceptions(sendWindowUpdateTask);
            }
Example #4
0
            internal void OnDataOrHeadersReceived(Http2Connection connection)
            {
                if (_state != State.Waiting)
                {
                    return;
                }

                long now     = Stopwatch.GetTimestamp();
                bool initial = _initialBurst > 0;

                if (initial || now - _pingSentTimestamp > PingIntervalInTicks)
                {
                    if (initial)
                    {
                        _initialBurst--;
                    }

                    // Send a PING
                    _pingCounter--;
                    connection.LogExceptions(connection.SendPingAsync(_pingCounter, isAck: false));
                    _pingSentTimestamp = now;
                    _state             = State.PingSent;
                }
            }
Example #5
0
            public async Task SendRequestBodyAsync(CancellationToken cancellationToken)
            {
                Debug.Assert(_request.Content != null);
                if (NetEventSource.IsEnabled)
                {
                    Trace($"{_request.Content}");
                }

                try
                {
                    using (Http2WriteStream writeStream = new Http2WriteStream(this))
                    {
                        // TODO: until #9071 is fixed, cancellation on content.CopyToAsync does not apply for most content types,
                        // because most content types aren't passed the token given to this internal overload of CopyToAsync.
                        // To work around it, we register to set _abortException as needed; this won't preempt reads issued to
                        // the source content, but it will at least enable the writes then performed on our write stream to see
                        // that cancellation was requested and abort, rather than waiting for the whole copy to complete.
                        using (cancellationToken.UnsafeRegister(stream =>
                        {
                            var thisRef = (Http2Stream)stream;
                            if (thisRef._abortException == null)
                            {
                                if (NetEventSource.IsEnabled)
                                {
                                    thisRef.Trace($"Canceling sending request body.");
                                }
                                Interlocked.CompareExchange(ref thisRef._abortException, new OperationCanceledException(), null);
                            }
                        }, this))
                        {
                            await _request.Content.CopyToAsync(writeStream, null, cancellationToken).ConfigureAwait(false);

                            if (NetEventSource.IsEnabled)
                            {
                                Trace($"Finished sending request body.");
                            }
                        }
                    }

                    // Don't wait for completion, which could happen asynchronously.
                    _connection.LogExceptions(_connection.SendEndStreamAsync(_streamId));
                }
                catch (Exception e)
                {
                    if (NetEventSource.IsEnabled)
                    {
                        Trace($"Failed to send request body: {e}");
                    }

                    // if we decided abandon sending request and we get ObjectDisposed as result of it, just eat exception.
                    if (!_shouldSendRequestBody && (e is ObjectDisposedException || e.InnerException is ObjectDisposedException))
                    {
                        // Try to notify server if we did not finish sending request body.
                        IgnoreExceptions(_connection.SendRstStreamAsync(_streamId, Http2ProtocolErrorCode.Cancel));
                        return;
                    }

                    // If we are still processing the response after receiving response headers,
                    // this will give us a chance to propagate exception up.
                    Interlocked.CompareExchange(ref _abortException, e, null);

                    throw;
                }
            }