| File: Internal\Http2\Http2KeepAlive.cs | Web Access |
| Project: src\aspnetcore\src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj (Microsoft.AspNetCore.Server.Kestrel.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2; internal enum KeepAliveState { None, SendPing, PingSent, Timeout } /// <summary> /// Used by a <see cref="Http2Connection"/> to determine whether the connection is still alive. /// If no frames have been received within a given period of time, triggers a ping that should /// draw a response. After a further timeout, signals that the connection should be dropped. /// </summary> internal sealed class Http2KeepAlive { // An empty ping payload internal static readonly ReadOnlySequence<byte> PingPayload = new ReadOnlySequence<byte>(new byte[8]); private readonly long _keepAliveInterval; private readonly long _keepAliveTimeout; private readonly TimeProvider _timeProvider; private long _lastFrameReceivedTimestamp; private long _pingSentTimestamp; // Internal for testing internal KeepAliveState _state; public Http2KeepAlive(TimeSpan keepAliveInterval, TimeSpan keepAliveTimeout, TimeProvider timeProvider) { _keepAliveInterval = keepAliveInterval.ToTicks(timeProvider); _keepAliveTimeout = keepAliveTimeout == TimeSpan.MaxValue ? long.MaxValue : keepAliveTimeout.ToTicks(timeProvider); _timeProvider = timeProvider; } public KeepAliveState ProcessKeepAlive(bool frameReceived) { var timestamp = _timeProvider.GetTimestamp(); if (frameReceived) { // To err on the side of caution, add a second to the time when calculating the ping sent time. _lastFrameReceivedTimestamp = timestamp + _timeProvider.TimestampFrequency; // Any frame received after the keep alive interval is exceeded resets the state back to none. if (_state == KeepAliveState.PingSent) { _pingSentTimestamp = 0; _state = KeepAliveState.None; } } else { switch (_state) { case KeepAliveState.None: // Check whether keep alive interval has passed since last frame received if (timestamp > (_lastFrameReceivedTimestamp + _keepAliveInterval)) { // Ping will be sent immeditely after this method finishes. // Set the status directly to ping sent and set the timestamp _state = KeepAliveState.PingSent; // To err on the side of caution, add a second to the time when calculating the ping sent time. _pingSentTimestamp = timestamp + _timeProvider.TimestampFrequency; // Indicate that the ping needs to be sent. This is only returned once return KeepAliveState.SendPing; } break; case KeepAliveState.PingSent: if (_keepAliveTimeout != long.MaxValue) { if (timestamp > (_pingSentTimestamp + _keepAliveTimeout)) { _state = KeepAliveState.Timeout; } } break; } } return _state; } }