示例#1
0
        /// <param name="client">The client that will send the message.</param>
        /// <param name="m">The monitor used to log.</param>
        /// <param name="message">The message that will be sent. It will be disposed after being stored.</param>
        /// <returns>A ValueTask, that complete when the message has been stored, containing a Task that complete when the publish has been ack.
        /// On QoS 0, the message is directly sent and the returned Task is Task.CompletedTask.
        /// </returns>
        public static async ValueTask <Task> PublishAsync(this IMqtt3Client client, IActivityMonitor?m, DisposableApplicationMessage message)
        {
            Task task = await client.PublishAsync(m, message.Topic, message.QoS, message.Retain, message.Payload); //The packet has been stored

            message.Dispose();                                                                                     // So we can dispose after it has been stored.
            return(task);                                                                                          // The task we return complete when the packet has been acked.
        }
 /// <summary>
 /// Gets all the <see cref="StObjMapInfo"/> available in all the loaded assemblies.
 /// This method like all the methods that manipulates StObjMapInfo are thread/concurrency safe.
 /// </summary>
 /// <returns>An array of all the available informations.</returns>
 public static StObjMapInfo[] GetAvailableMapInfos(IActivityMonitor?monitor = null)
 {
     lock ( _alreadyHandled )
     {
         return(LockedGetAvailableMapInfos(ref monitor).ToArray());
     }
 }
示例#3
0
        /// <summary>
        /// Create a <see cref="TcpChannel"/>. The connection string should be "hostname:port".
        /// </summary>
        /// <param name="m">The logger to use.</param>
        /// <param name="connectionString">"hostname:port"</param>
        /// <returns></returns>
        public ValueTask <IMqttChannel> CreateAsync(IActivityMonitor?m, string connectionString)
        {
            string[]  strs   = connectionString.Split(':');
            TcpClient client = new(strs[0], int.Parse(strs[1]));

            client.NoDelay = true;
            return(new ValueTask <IMqttChannel>(new TcpChannel(client)));
        }
 /// <summary>
 /// Tries to get a <see cref="StObjMapInfo"/> from its signature among all loaded assemblies.
 /// This never throws: errors are logged (a new monitor is automatically managed when <paramref name="monitor"/> is null),
 /// and null is returned.
 /// This method like all the methods that manipulates StObjMapInfo are thread/concurrency safe.
 /// </summary>
 /// <param name="signature">Signature to find.</param>
 /// <param name="monitor">Optional monitor.</param>
 /// <returns>The StObjMapInfo if it exists.</returns>
 public static StObjMapInfo?GetMapInfo(SHA1Value signature, IActivityMonitor?monitor = null)
 {
     lock ( _alreadyHandled )
     {
         LockedGetAvailableMapInfos(ref monitor);
         return(_alreadyHandled.GetValueOrDefault(signature.ToString()));
     }
 }
示例#5
0
 protected void OpenPumps(IActivityMonitor?m, T newState)
 {
     using (m?.OpenInfo("Opening pumps."))
     {
         Open();
         _state = newState;
     }
 }
 /// <summary>
 /// Tries to get a <see cref="StObjMapInfo"/> from an assembly.
 /// This never throws: errors are logged (a new monitor is automatically managed when <paramref name="monitor"/> is null),
 /// and null is returned.
 /// This method like all the methods that manipulates StObjMapInfo are thread/concurrency safe.
 /// </summary>
 /// <param name="a">Candidate assembly.</param>
 /// <param name="monitor">Optional monitor.</param>
 /// <returns>A <see cref="IStObjMap"/> that provides access to the objects graph.</returns>
 public static StObjMapInfo?GetMapInfo(Assembly a, IActivityMonitor?monitor = null)
 {
     if (a == null)
     {
         throw new ArgumentNullException(nameof(a));
     }
     lock ( _alreadyHandled )
     {
         return(LockedGetMapInfo(a, ref monitor));
     }
 }
        public static ValueTask <Task <T?> > SendPacket <T>(IActivityMonitor?m, IOutgoingPacketStore store, OutputPump output, IOutgoingPacket packet)
            where T : class
        {
            IDisposableGroup?group = m?.OpenTrace($"Sending a packet '{packet}'in QoS {packet.Qos}");

            return(packet.Qos switch
            {
                QualityOfService.AtMostOnce => PublishQoS0 <T>(m, group, output, packet),
                QualityOfService.AtLeastOnce => StoreAndSend <T>(m, group, output, store, packet, packet.Qos),
                QualityOfService.ExactlyOnce => StoreAndSend <T>(m, group, output, store, packet, packet.Qos),
                _ => throw new ArgumentException("Invalid QoS."),
            });
 /// <summary>
 /// Gets the <see cref="IStObjMap"/> if no error prevents its instantiation from
 /// the <see cref="StObjMapInfo"/>.
 /// This never throws: errors are logged (a new monitor is automatically managed when <paramref name="monitor"/> is null),
 /// and null is returned.
 /// This method like all the methods that manipulates StObjMapInfo are thread/concurrency safe.
 /// </summary>
 /// <param name="info">The info.</param>
 /// <param name="monitor">Optional monitor.</param>
 /// <returns>The loaded map or null on error.</returns>
 public static IStObjMap?GetStObjMap(StObjMapInfo info, IActivityMonitor?monitor = null)
 {
     Throw.CheckNotNullArgument(info);
     if (info.StObjMap != null || info.LoadError != null)
     {
         return(info.StObjMap);
     }
     lock ( _alreadyHandled )
     {
         return(LockedGetStObjMapFromInfo(info, ref monitor));
     }
 }
 /// <summary>
 /// Attempts to load a StObjMap with a provided signature from all available maps.
 /// </summary>
 /// <param name="signature">The signature.</param>
 /// <param name="monitor">Optional monitor to use.</param>
 /// <returns>A <see cref="IStObjMap"/> that provides access to the objects graph.</returns>
 public static IStObjMap?Load(SHA1Value signature, IActivityMonitor?monitor = null)
 {
     lock ( _alreadyHandled )
     {
         LockedGetAvailableMapInfos(ref monitor);
         if (_alreadyHandled.TryGetValue(signature.ToString(), out var info))
         {
             Debug.Assert(info != null);
             return(LockedGetStObjMapFromInfo(info, ref monitor));
         }
         return(null);
     }
 }
示例#10
0
        public ValueTask <Task <T?> > SendPacket <T>(IActivityMonitor?m, IOutgoingPacket outgoingPacket) where T : class
        {
            ClientState?state = State;

            if (!IsConnected)
            {
                throw new InvalidOperationException("Client is Disconnected.");
            }
            if (state is null)
            {
                throw new NullReferenceException();
            }
            return(SenderHelper.SendPacket <T>(m, state.Store, state.OutputPump, outgoingPacket));
        }
        static List <StObjMapInfo> LockedGetAvailableMapInfos([NotNullIfNotNull("monitor")] ref IActivityMonitor?monitor)
        {
            var all = AppDomain.CurrentDomain.GetAssemblies();

            if (all.Length != _allAssemblyCount)
            {
                // Don't know/trust the ordering: process them all.
                foreach (var a in all)
                {
                    LockedGetMapInfo(a, ref monitor);
                }
                _allAssemblyCount = all.Length;
            }
            return(_availableMaps);
        }
        /// <summary>
        /// Attempts to load a StObjMap from an assembly name.
        /// <para>
        /// If a <see cref="SuffixSignature"/> file exists and contains a valid signature, the StObjMap is
        /// loaded from the <see cref="GetAvailableMapInfos(IActivityMonitor?)"/> if it exists.
        /// </para>
        /// </summary>
        /// <param name="assemblyName">The assembly name.</param>
        /// <param name="monitor">Optional monitor to use.</param>
        /// <returns>A <see cref="IStObjMap"/> that provides access to the objects graph.</returns>
        public static IStObjMap?Load(string assemblyName, IActivityMonitor?monitor = null)
        {
            Throw.CheckNotNullOrEmptyArgument(assemblyName);
            Throw.CheckArgument(FileUtil.IndexOfInvalidFileNameChars(assemblyName) < 0);

            if (!assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) &&
                !assemblyName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
            {
                assemblyName = assemblyName + ".dll";
            }
            string assemblyFullPath = Path.Combine(AppContext.BaseDirectory, assemblyName);
            var    signaturePath    = assemblyFullPath + SuffixSignature;

            if (File.Exists(signaturePath) &&
                SHA1Value.TryParse(File.ReadAllText(signaturePath), out var signature))
            {
                var map = Load(signature, monitor);
                if (map != null)
                {
                    return(map);
                }
            }

            lock ( _alreadyHandled )
            {
                LockedEnsureMonitor(ref monitor);
                using (monitor.OpenInfo($"Loading StObj map from '{assemblyName}'."))
                {
                    try
                    {
                        // LoadFromAssemblyPath caches the assemblies by their path.
                        // No need to do it.
                        var a    = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFullPath);
                        var info = LockedGetMapInfo(a, ref monitor);
                        if (info == null)
                        {
                            return(null);
                        }
                        return(LockedGetStObjMapFromInfo(info, ref monitor));
                    }
                    catch (Exception ex)
                    {
                        monitor.Error(ex);
                        return(null);
                    }
                }
            }
        }
示例#13
0
            /// <summary>
            /// Replays this monitor's content into another monitor.
            /// </summary>
            /// <param name="replay">The target monitor. Can not be null.</param>
            /// <param name="monitor">Optional monitor (nothing is logged when null).</param>
            public void Replay(IActivityMonitor replay, IActivityMonitor?monitor = null)
            {
                using (monitor?.OpenInfo($"Replaying activity from '{MonitorId}'."))
                {
                    int nbMissing = 0;
                    int nbTotal   = 0;
                    using (var page = ReadFirstPage(1024))
                    {
                        foreach (ParentedLogEntry e in page.Entries)
                        {
                            ++nbTotal;
                            LogLevel level = e.Entry.LogLevel;
                            if (e.IsMissing)
                            {
                                ++nbMissing;
                                level = LogLevel.Trace;
                            }
                            switch (e.Entry.LogType)
                            {
                            case LogEntryType.Line:
                                var d = new ActivityMonitorLogData(level, e.Entry.Tags, e.Entry.Text, CKException.CreateFrom(e.Entry.Exception), e.Entry.FileName, e.Entry.LineNumber);
                                d.SetExplicitLogTime(e.Entry.LogTime);
                                replay.UnfilteredLog(ref d);
                                break;

                            case LogEntryType.OpenGroup:
                                d = new ActivityMonitorLogData(level, e.Entry.Tags, e.Entry.Text, CKException.CreateFrom(e.Entry.Exception), e.Entry.FileName, e.Entry.LineNumber);
                                d.SetExplicitLogTime(e.Entry.LogTime);
                                replay.UnfilteredOpenGroup(ref d);
                                break;

                            case LogEntryType.CloseGroup:
                                replay.CloseGroup(e.Entry.Conclusions, e.Entry.LogTime);
                                break;
                            }
                        }
                        page.ForwardPage();
                    }
                    monitor?.CloseGroup($"Replayed {nbTotal} entries ({nbMissing} missing).");
                }
            }
示例#14
0
        protected override async ValueTask <IOutgoingPacket> DoStorePacket(IActivityMonitor?m, IOutgoingPacket packet)
        {
            int packetSize = packet.GetSize(_protocolConfig.ProtocolLevel);

            m?.Trace($"Renting {packetSize} bytes to persist {packet}.");
            IMemoryOwner <byte> memOwner = MemoryPool <byte> .Shared.Rent(packetSize);

            PipeWriter pipe = PipeWriter.Create(memOwner.Memory.AsStream());   // And write their content to this memory.

            using (m?.OpenTrace($"Serializing {packet} into memory..."))
            {
                if (await packet.WriteAsync(_protocolConfig.ProtocolLevel, pipe, default) != WriteResult.Written)
                {
                    throw new InvalidOperationException("Didn't wrote packet correctly.");
                }
            }
            Memory <byte> slicedMem = memOwner.Memory.Slice(0, packetSize);

            base[packet.PacketId].Content.Storage = new StoredPacket(slicedMem, memOwner);
            return(new FromMemoryOutgoingPacket(slicedMem, packet.Qos, packet.PacketId));
        }
 static IStObjMap?LockedGetStObjMapFromInfo(StObjMapInfo info, [NotNullIfNotNull("monitor")] ref IActivityMonitor?monitor)
 {
     if (info.StObjMap != null || info.LoadError != null)
     {
         return(info.StObjMap);
     }
     LockedEnsureMonitor(ref monitor);
     using (monitor.OpenInfo($"Instantiating StObjMap from {info}."))
     {
         try
         {
             return(info.StObjMap = (IStObjMap?)Activator.CreateInstance(info.StObjMapType, new object[] { monitor }));
         }
         catch (Exception ex)
         {
             monitor.Error(ex);
             info.LoadError = ex.Message;
             return(null);
         }
     }
 }
示例#16
0
        /// <summary>
        /// Called by the external world to explicitly close the connection to the remote.
        /// </summary>
        /// <param name="reason">The reason of the disconnection.</param>
        /// <returns>True if this call actually closed the connection, false if the connection has already been closed by a concurrent decision.</returns>
        public Task <bool> DisconnectAsync(IActivityMonitor?m, bool clearSession, bool cancelAckTasks)
        {
            ClientState?state = State;

            if (clearSession && !cancelAckTasks)
            {
                throw new ArgumentException("When the session is cleared, the ACK tasks must be canceled too.");
            }
            if (!IsConnected)
            {
                return(Task.FromResult(false));
            }
            if (state is null)
            {
                throw new NullReferenceException();
            }
            if (cancelAckTasks)
            {
                state !.Store.CancelAllAckTask(m);
            }
            return(CloseAsync(DisconnectedReason.UserDisconnected));
        }
 /// <summary>
 /// Attempts to load a StObjMap from an assembly.
 /// </summary>
 /// <param name="a">Already generated assembly.</param>
 /// <param name="monitor">Optional monitor for loading operation.</param>
 /// <returns>A <see cref="IStObjMap"/> that provides access to the objects graph.</returns>
 public static IStObjMap?Load(Assembly a, IActivityMonitor?monitor = null)
 {
     Throw.CheckNotNullArgument(a);
     lock ( _alreadyHandled )
     {
         var info = LockedGetMapInfo(a, ref monitor);
         if (info == null)
         {
             return(null);
         }
         var alc = AssemblyLoadContext.GetLoadContext(a);
         if (alc == null)
         {
             monitor.Warn($"Assembly '{a.FullName}' is not in any AssemblyLoadContext.");
         }
         else if (alc != AssemblyLoadContext.Default)
         {
             monitor.Warn($"Assembly '{a.FullName}' is loaded in non-default AssemblyLoadContext '{alc.Name}'.");
         }
         return(LockedGetStObjMapFromInfo(info, ref monitor));
     }
 }
 void StObjConstruct(IActivityMonitor monitor, IActivityMonitor?anotherLogger = null)
 {
     monitor.Should().NotBeNull("This is the Setup monitor.");
     anotherLogger.Should().BeSameAs(monitor, "All IActivityMonitor are Setup monitors.");
     monitor.Trace("Setup monitor can be used by StObjConstruct method.");
 }
 public static async ValueTask <Task> PublishAsync(this IMqtt5Client client, IActivityMonitor?m, string topic, QualityOfService qos, bool retain, ReadOnlyMemory <byte> payload,
                                                   string?responseTopic = null, ushort correlationDataSize = 0, SpanAction?correlationDataWriter = null) //properties
 => await client.SendPacket <object>(m, new SmallOutgoingApplicationMessage( topic, qos, retain, payload, responseTopic, correlationDataSize, correlationDataWriter ));
 public static async ValueTask <Task> PublishAsync(this IMqtt3Client client, IActivityMonitor?m, string topic, QualityOfService qos, bool retain, ReadOnlyMemory <byte> payload)
 => await client.SendPacket <object>(m, new SmallOutgoingApplicationMessage( topic, qos, retain, payload ));
示例#21
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));
                }
            }
        }