Ejemplo n.º 1
0
        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);
            }
        }
Ejemplo n.º 2
0
        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);
            }
        }
Ejemplo n.º 3
0
 public StateHolder(InputPump input, OutputPump output) => (Input, OutputPump) = (input, output);
Ejemplo n.º 4
0
        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);
                }
            }
        }
Ejemplo n.º 5
0
        /// <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));
                }
            }
        }
Ejemplo n.º 6
0
 public ClientState(InputPump input, OutputPump output, IMqttChannel channel, IIncomingPacketStore packetIdStore, IOutgoingPacketStore store) : base(input, output)
 {
     Channel       = channel;
     PacketIdStore = packetIdStore;
     Store         = store;
 }
Ejemplo n.º 7
0
        async ValueTask InvalidPacket(IInputLogger?m, InputPump sender, byte header, int packetSize, PipeReader reader, CancellationToken cancellationToken)
        {
            _ = await CloseAsync(DisconnectedReason.ProtocolError);

            throw new ProtocolViolationException();
        }