You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

173 lines
5.9 KiB

  1. using Microsoft.AspNetCore.Connections;
  2. using Microsoft.AspNetCore.Http.Connections.Features;
  3. using MQTTnet.Adapter;
  4. using MQTTnet.AspNetCore.Client.Tcp;
  5. using MQTTnet.Exceptions;
  6. using MQTTnet.Formatter;
  7. using MQTTnet.Packets;
  8. using System;
  9. using System.IO.Pipelines;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. namespace MQTTnet.AspNetCore
  13. {
  14. public class MqttConnectionContext : IMqttChannelAdapter
  15. {
  16. public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
  17. {
  18. PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
  19. Connection = connection ?? throw new ArgumentNullException(nameof(connection));
  20. if (Connection.Transport != null)
  21. {
  22. _input = Connection.Transport.Input;
  23. _output = Connection.Transport.Output;
  24. }
  25. _reader = new SpanBasedMqttPacketBodyReader();
  26. }
  27. private PipeReader _input;
  28. private PipeWriter _output;
  29. private readonly SpanBasedMqttPacketBodyReader _reader;
  30. public string Endpoint
  31. {
  32. get
  33. {
  34. var connection = Http?.HttpContext?.Connection;
  35. if (connection == null)
  36. {
  37. return Connection.ConnectionId;
  38. }
  39. return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
  40. }
  41. }
  42. public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;
  43. private IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
  44. public ConnectionContext Connection { get; }
  45. public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
  46. public long BytesSent { get; set; }
  47. public long BytesReceived { get; set; }
  48. public Action ReadingPacketStartedCallback { get; set; }
  49. public Action ReadingPacketCompletedCallback { get; set; }
  50. private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
  51. public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
  52. {
  53. if (Connection is TcpConnection tcp && !tcp.IsConnected)
  54. {
  55. await tcp.StartAsync().ConfigureAwait(false);
  56. }
  57. _input = Connection.Transport.Input;
  58. _output = Connection.Transport.Output;
  59. }
  60. public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
  61. {
  62. _input?.Complete();
  63. _output?.Complete();
  64. return Task.CompletedTask;
  65. }
  66. public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
  67. {
  68. var input = Connection.Transport.Input;
  69. try
  70. {
  71. while (!cancellationToken.IsCancellationRequested)
  72. {
  73. ReadResult readResult;
  74. var readTask = input.ReadAsync(cancellationToken);
  75. if (readTask.IsCompleted)
  76. {
  77. readResult = readTask.Result;
  78. }
  79. else
  80. {
  81. readResult = await readTask.ConfigureAwait(false);
  82. }
  83. var buffer = readResult.Buffer;
  84. var consumed = buffer.Start;
  85. var observed = buffer.Start;
  86. try
  87. {
  88. if (!buffer.IsEmpty)
  89. {
  90. if (PacketFormatterAdapter.TryDecode(_reader, buffer, out var packet, out consumed, out observed, out var received))
  91. {
  92. BytesReceived += received;
  93. return packet;
  94. }
  95. else
  96. {
  97. // we did receive something but the message is not yet complete
  98. ReadingPacketStartedCallback?.Invoke();
  99. }
  100. }
  101. else if (readResult.IsCompleted)
  102. {
  103. throw new MqttCommunicationException("Connection Aborted");
  104. }
  105. }
  106. finally
  107. {
  108. // The buffer was sliced up to where it was consumed, so we can just advance to the start.
  109. // We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
  110. // before yielding the read again.
  111. input.AdvanceTo(consumed, observed);
  112. }
  113. }
  114. }
  115. finally
  116. {
  117. ReadingPacketCompletedCallback?.Invoke();
  118. }
  119. cancellationToken.ThrowIfCancellationRequested();
  120. return null;
  121. }
  122. public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
  123. {
  124. var formatter = PacketFormatterAdapter;
  125. await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
  126. try
  127. {
  128. var buffer = formatter.Encode(packet);
  129. var msg = buffer.AsMemory();
  130. var output = _output;
  131. msg.CopyTo(output.GetMemory(msg.Length));
  132. BytesSent += msg.Length;
  133. PacketFormatterAdapter.FreeBuffer();
  134. output.Advance(msg.Length);
  135. await output.FlushAsync().ConfigureAwait(false);
  136. }
  137. finally
  138. {
  139. _writerSemaphore.Release();
  140. }
  141. }
  142. public void Dispose()
  143. {
  144. }
  145. }
  146. }