| /* |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| namespace DotPulsar.Internal; |
| |
| using Abstractions; |
| using PulsarApi; |
| using System; |
| using System.Diagnostics; |
| using System.Threading; |
| using System.Threading.Tasks; |
| |
| public sealed class PingPongHandler : IAsyncDisposable |
| { |
| private readonly IConnection _connection; |
| private readonly TimeSpan _keepAliveInterval; |
| private readonly Timer _timer; |
| private readonly CommandPing _ping; |
| private readonly CommandPong _pong; |
| private long _lastCommand; |
| |
| public PingPongHandler(IConnection connection, TimeSpan keepAliveInterval) |
| { |
| _connection = connection; |
| _keepAliveInterval = keepAliveInterval; |
| _timer = new Timer(Watch); |
| _timer.Change(_keepAliveInterval, TimeSpan.Zero); |
| _ping = new CommandPing(); |
| _pong = new CommandPong(); |
| _lastCommand = Stopwatch.GetTimestamp(); |
| } |
| |
| public bool Incoming(BaseCommand.Type commandType) |
| { |
| Interlocked.Exchange(ref _lastCommand, Stopwatch.GetTimestamp()); |
| |
| if (commandType == BaseCommand.Type.Ping) |
| { |
| Task.Factory.StartNew(() => SendPong()); |
| return true; |
| } |
| |
| return commandType == BaseCommand.Type.Pong; |
| } |
| |
| private void Watch(object? state) |
| { |
| try |
| { |
| var lastCommand = Interlocked.Read(ref _lastCommand); |
| var now = Stopwatch.GetTimestamp(); |
| var elapsed = TimeSpan.FromSeconds((now - lastCommand) / Stopwatch.Frequency); |
| if (elapsed >= _keepAliveInterval) |
| { |
| Task.Factory.StartNew(() => SendPing()); |
| _timer.Change(_keepAliveInterval, TimeSpan.Zero); |
| } |
| else |
| _timer.Change(_keepAliveInterval.Subtract(elapsed), TimeSpan.Zero); |
| } |
| catch |
| { |
| // Ignore |
| } |
| } |
| |
| private async Task SendPing() |
| { |
| try |
| { |
| await _connection.Send(_ping, default).ConfigureAwait(false); |
| } |
| catch { } |
| } |
| |
| private async Task SendPong() |
| { |
| try |
| { |
| await _connection.Send(_pong, default).ConfigureAwait(false); |
| } |
| catch { } |
| } |
| |
| #if NETSTANDARD2_0 |
| public ValueTask DisposeAsync() |
| { |
| _timer.Dispose(); |
| return new ValueTask(); |
| } |
| #else |
| public async ValueTask DisposeAsync() |
| { |
| await _timer.DisposeAsync().ConfigureAwait(false); |
| } |
| #endif |
| } |