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); }
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; } }
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; } }