public async ValueTask ProcessIncomingPacketAsync( IInputLogger?m, InputPump sender, byte header, int packetLength, PipeReader pipeReader, Func <ValueTask> next, CancellationToken cancellationToken) { if (PacketType.PingResponse != (PacketType)header) { await next(); return; } using (m?.ProcessPacket(PacketType.PingResponse)) { WaitingPingResp = false; if (packetLength > 0) { m?.UnparsedExtraBytes(sender, PacketType.PingResponse, 0, packetLength, packetLength); } await pipeReader.SkipBytes(packetLength); } }
public async ValueTask ProcessIncomingPacketAsync(IInputLogger?m, InputPump sender, byte header, int packetLength, PipeReader pipeReader, Func <ValueTask> next, CancellationToken cancellationToken) { if (PacketType.SubscribeAck != (PacketType)header) { await next(); return; } using (m?.ProcessPacket(PacketType.SubscribeAck)) { ReadResult?read = await pipeReader.ReadAsync(m, packetLength); if (!read.HasValue) { return; } Parse(read.Value.Buffer, packetLength, out ushort packetId, out QualityOfService[]? qos, out SequencePosition position); pipeReader.AdvanceTo(position); await _store.OnQos1AckAsync(m, packetId, qos); } }
public StateHolder(InputPump input, OutputPump output) => (Input, OutputPump) = (input, output);
public async ValueTask ProcessIncomingPacketAsync(IInputLogger?m, InputPump sender, byte header, int packetLength, PipeReader reader, Func <ValueTask> next, CancellationToken cancellationToken) { if ((PacketType)((header >> 4) << 4) != PacketType.Publish) { await next(); return; } QualityOfService qos = (QualityOfService)((header >> 1) & 3); using (m?.ProcessPublishPacket(sender, header, packetLength, reader, next, qos)) { bool dup = (header & _dupFlag) > 0; bool retain = (header & _retainFlag) > 0; if ((byte)qos > 2) { throw new ProtocolViolationException($"Parsed QoS byte is invalid({(byte)qos})."); } string?topic; ushort packetId; while (true) { ReadResult read = await reader.ReadAsync(); if (read.IsCanceled) { return; } if (qos == QualityOfService.AtMostOnce) { string theTopic = await reader.ReadMQTTString(); await _messageHandler(_mqttConfiguration.OnInputMonitor, theTopic, reader, packetLength - theTopic.MQTTSize(), qos, retain, cancellationToken); return; } if (Publish.ParsePublishWithPacketId(read.Buffer, out topic, out packetId, out SequencePosition position)) { reader.AdvanceTo(position); break; } reader.AdvanceTo(read.Buffer.Start, read.Buffer.End); } if (qos == QualityOfService.AtLeastOnce) { await _messageHandler(_mqttConfiguration.OnInputMonitor, topic, reader, packetLength - 2 - topic.MQTTSize(), qos, retain, cancellationToken); if (!_output.QueueReflexMessage(LifecyclePacketV3.Puback(packetId))) { m?.QueueFullPacketDropped(PacketType.PublishAck, packetId); } return; } if (qos != QualityOfService.ExactlyOnce) { throw new ProtocolViolationException(); } await _store.StoreId(m, packetId); await _messageHandler(_mqttConfiguration.OnInputMonitor, topic, reader, packetLength - 2 - topic.MQTTSize(), qos, retain, cancellationToken); if (!_output.QueueReflexMessage(LifecyclePacketV3.Pubrec(packetId))) { m?.QueueFullPacketDropped(PacketType.PublishReceived, packetId); } } }
/// <inheritdoc/> public async Task <ConnectResult> ConnectAsync(IActivityMonitor?m, MqttClientCredentials?credentials = null, OutgoingLastWill?lastWill = null) { if (IsConnected) { throw new InvalidOperationException("This client is already connected."); } using (m?.OpenTrace("Connecting...")) { try { (IOutgoingPacketStore store, IIncomingPacketStore packetIdStore) = await _config.StoreFactory.CreateAsync(m, _pConfig, _config, _config.ConnectionString, credentials?.CleanSession ?? true); IMqttChannel channel = await _config.ChannelFactory.CreateAsync(m, _config.ConnectionString); ConnectAckReflex connectAckReflex = new(); Task <ConnectResult> connectedTask = connectAckReflex.Task; var output = new OutputPump(this, _pConfig); OutputProcessor outputProcessor; var input = new InputPump(this, channel.DuplexPipe.Input, connectAckReflex.ProcessIncomingPacket); OpenPumps(m, new ClientState(input, output, channel, packetIdStore, store)); ReflexMiddlewareBuilder builder = new ReflexMiddlewareBuilder() .UseMiddleware(new PublishReflex(_config, packetIdStore, OnMessage, output)) .UseMiddleware(new PublishLifecycleReflex(packetIdStore, store, output)) .UseMiddleware(new SubackReflex(store)) .UseMiddleware(new UnsubackReflex(store)); if (_config.KeepAliveSeconds == 0) { outputProcessor = new OutputProcessor(output, channel.DuplexPipe.Output, store); } else { OutputProcessorWithKeepAlive withKeepAlive = new(_config, output, channel.DuplexPipe.Output, store);; outputProcessor = withKeepAlive; _ = builder.UseMiddleware(withKeepAlive); } output.StartPumping(outputProcessor); connectAckReflex.Reflex = builder.Build(InvalidPacket); OutgoingConnect outgoingConnect = new(_pConfig, _config, credentials, lastWill); CancellationTokenSource cts = new(_config.WaitTimeoutMilliseconds); IOutgoingPacket.WriteResult writeConnectResult = await outgoingConnect.WriteAsync(_pConfig.ProtocolLevel, channel.DuplexPipe.Output, cts.Token); if (writeConnectResult != IOutgoingPacket.WriteResult.Written) { _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(ConnectError.Timeout)); } Task timeout = _config.DelayHandler.Delay(_config.WaitTimeoutMilliseconds, CloseToken); _ = await Task.WhenAny(connectedTask, timeout); // This following code wouldn't be better with a sort of ... switch/pattern matching ? if (connectedTask.Exception is not null) { m?.Fatal(connectedTask.Exception); _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(ConnectError.InternalException)); } if (CloseToken.IsCancellationRequested) { _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(ConnectError.RemoteDisconnected)); } if (!connectedTask.IsCompleted) { _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(ConnectError.Timeout)); } ConnectResult res = await connectedTask; if (res.ConnectError != ConnectError.Ok) { _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(res.ConnectError)); } bool askedCleanSession = credentials?.CleanSession ?? true; if (askedCleanSession && res.SessionState != SessionState.CleanSession) { _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(ConnectError.ProtocolError_SessionNotFlushed)); } if (res.SessionState == SessionState.CleanSession) { ValueTask task = packetIdStore.ResetAsync(); await store.ResetAsync(); await task; } else { throw new NotImplementedException(); } return(res); } catch (Exception e) { m?.Error("Error while connecting, closing client.", e); _ = await CloseAsync(DisconnectedReason.None); return(new ConnectResult(ConnectError.InternalException)); } } }
public ClientState(InputPump input, OutputPump output, IMqttChannel channel, IIncomingPacketStore packetIdStore, IOutgoingPacketStore store) : base(input, output) { Channel = channel; PacketIdStore = packetIdStore; Store = store; }
async ValueTask InvalidPacket(IInputLogger?m, InputPump sender, byte header, int packetSize, PipeReader reader, CancellationToken cancellationToken) { _ = await CloseAsync(DisconnectedReason.ProtocolError); throw new ProtocolViolationException(); }