// end stub helpers

		public IRabbitChannel CreateChannel(ChannelOptions options = null)
		{
			EnsureNotDisposed();

			var channel = new StubRabbitChannel(options);
			_channelCreated.Add(channel);
			return channel;
		}
		public StubRabbitChannel(ChannelOptions options)
		{
			this.Options = options;

			_defaultExchange = new StubRabbitExchange("", new ExchangeOptions(), o => null);

			_queuesDeclared = new List<StubRabbitQueue>();
			_queuesDeclaredNoWait = new List<StubRabbitQueue>();

			_exchangesDeclared = new List<StubRabbitExchange>();
			_exchangesDeclaredNoWait = new List<StubRabbitExchange>();

			_bound = new List<StubRabbitQueueBinding>();
			_boundNoWait = new List<StubRabbitQueueBinding>();
			_unbound = new List<StubRabbitQueueBinding>();
		}
示例#3
0
 public Channel(ChannelOptions options)
 {
     Name  = options.Name;
     Token = options.Token;
 }
示例#4
0
        public IChannelCreator CreateChannelCreator <TPackageInfo>(ListenOptions options, ChannelOptions channelOptions, ILoggerFactory loggerFactory, object pipelineFilterFactory) where TPackageInfo : class
        {
            var filterFactory = pipelineFilterFactory as IPipelineFilterFactory <TPackageInfo>;

            channelOptions.Logger = loggerFactory.CreateLogger(nameof(IChannel));
            var channelFactoryLogger = loggerFactory.CreateLogger(nameof(UdpChannelCreator));

            return(new UdpChannelCreator(options, null, channelFactoryLogger));
        }
示例#5
0
        protected virtual void ApplySocketOptions(Socket socket, ListenOptions listenOptions, ChannelOptions channelOptions, Dynamic.Core.Log.ILogger logger)
        {
            try
            {
                if (listenOptions.NoDelay)
                {
                    socket.NoDelay = true;
                }
            }
            catch (Exception e)
            {
                logger.Warn("Failed to set NoDelay for the socket." + e.ToString());
            }

            try
            {
                if (channelOptions.ReceiveBufferSize > 0)
                {
                    socket.ReceiveBufferSize = channelOptions.ReceiveBufferSize;
                }
            }
            catch (Exception e)
            {
                logger.Warn(e.ToString() + "Failed to set ReceiveBufferSize for the socket.");
            }

            try
            {
                if (channelOptions.SendBufferSize > 0)
                {
                    socket.SendBufferSize = channelOptions.SendBufferSize;
                }
            }
            catch (Exception e)
            {
                logger.Warn(e.ToString() + "Failed to set SendBufferSize for the socket.");
            }

            try
            {
                if (channelOptions.ReceiveTimeout > 0)
                {
                    socket.ReceiveTimeout = channelOptions.ReceiveTimeout;
                }
            }
            catch (Exception e)
            {
                logger.Warn(e.ToString() + "Failed to set ReceiveTimeout for the socket.");
            }

            try
            {
                if (channelOptions.SendTimeout > 0)
                {
                    socket.SendTimeout = channelOptions.SendTimeout;
                }
            }
            catch (Exception e)
            {
                logger.Warn(e.ToString() + "Failed to set SendTimeout for the socket.");
            }

            try
            {
                _socketOptionsSetter?.Invoke(socket);
            }
            catch (Exception e)
            {
                logger.Warn(e.ToString() + "Failed to run socketOptionSetter for the socket.");
            }
        }
示例#6
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DecodingContext"/> class.
 /// </summary>
 public DecodingContext()
 {
     ChannelOptions = new ChannelOptions();
 }
示例#7
0
 /// <summary>
 /// Creates a new <see cref="DecodingContext"/> from the provided <see cref="ChannelOptions"/>.
 /// </summary>
 /// <param name="options">the <see cref="ChannelOptions"/> used in the new context.</param>
 /// <returns><see cref="DecodingContext"/> created with passed Channel options.</returns>
 public static DecodingContext ToDecodingContext(this ChannelOptions options)
 {
     return(new DecodingContext(options ?? new ChannelOptions()));
 }
示例#8
0
 public static void Validate(ChannelOptions channelOptions)
 {
     Validate(channelOptions as BaseOptions);
 }
示例#9
0
 public WithBinaryProtocolWithEncryption(ITestOutputHelper output)
     : base(output)
 {
     _options = new ChannelOptions(Crypto.GetDefaultParams());
     _client  = GetRestClient(null, opts => opts.UseBinaryProtocol = true);
 }
 /// <summary>
 /// 构造函数
 /// </summary>
 /// <param name="socket">socket对象</param>
 /// <param name="options">通道选项</param>
 /// <param name="logger">日志</param>
 public TcpPipeChannel(Socket socket, ChannelOptions options, ILogger logger)
     : base(socket, options, logger)
 {
     this.RemoteIPEndPoint = socket.RemoteEndPoint;
 }
 public TerminatorPipelineFilter(ChannelOptions channelOptions, FilterInfo filterInfo)
 {
     this._channelOptions = channelOptions;
     this._filterInfo     = filterInfo;
     _terminatorMark      = this._filterInfo.BasePortocalFilterInfo.Terminator;
 }
示例#12
0
 /// <summary>
 /// Creates a new Yacs communication channel for a client.
 /// </summary>
 /// <param name="host">The host to connect to.</param>
 /// <param name="port">The TCP port number to connect to.</param>
 /// <param name="options">The <see cref="ChannelOptions"/> to use to initialise the channel.</param>
 public Channel(string host, int port, ChannelOptions options = null)
     : this(new TcpClient(host, port), options ?? new ChannelOptions())
 {
 }
示例#13
0
        private async ValueTask OnOffer(int channelId, Memory <byte> payloadBuffer, CancellationToken cancellationToken)
        {
            await ReadToFillAsync(this.stream, payloadBuffer, throwOnEmpty : true, cancellationToken).ConfigureAwait(false);

            string name = DecodeString(payloadBuffer);

            var            channel = new Channel(this, channelId, name, channelOptions: DefaultChannelOptions);
            bool           acceptingChannelAlreadyPresent = false;
            ChannelOptions options = null;

            lock (this.syncObject)
            {
                if (name != null && this.acceptingChannels.TryGetValue(name, out var acceptingChannels))
                {
                    while (acceptingChannels.Count > 0)
                    {
                        var candidate = acceptingChannels.Dequeue();
                        if (candidate.TrySetResult(channel))
                        {
                            if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
                            {
                                this.TraceSource.TraceEvent(TraceEventType.Information, (int)TraceEventId.ChannelOfferReceived, "Remote party offers channel {1} \"{0}\" which matches up with a pending " + nameof(this.AcceptChannelAsync), name, channelId);
                            }

                            acceptingChannelAlreadyPresent = true;
                            options = (ChannelOptions)candidate.Task.AsyncState;
                            break;
                        }
                    }
                }

                if (!acceptingChannelAlreadyPresent)
                {
                    if (name != null)
                    {
                        if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Information, (int)TraceEventId.ChannelOfferReceived, "Remote party offers channel {1} \"{0}\" which has no pending " + nameof(this.AcceptChannelAsync), name, channelId);
                        }

                        if (!this.channelsOfferedByThemByName.TryGetValue(name, out var offeredChannels))
                        {
                            this.channelsOfferedByThemByName.Add(name, offeredChannels = new Queue <Channel>());
                        }

                        offeredChannels.Enqueue(channel);
                    }
                    else
                    {
                        if (this.TraceSource.Switch.ShouldTrace(TraceEventType.Information))
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Information, (int)TraceEventId.ChannelOfferReceived, "Remote party offers anonymous channel {0}", channelId);
                        }
                    }
                }

                this.openChannels.Add(channelId, channel);
            }

            if (acceptingChannelAlreadyPresent)
            {
                this.AcceptChannelOrThrow(channel, options);
            }

            var args = new ChannelOfferEventArgs(channel.Id, channel.Name, acceptingChannelAlreadyPresent);

            this.OnChannelOffered(args);
        }
示例#14
0
        void AddBindingIfValid(List <RaidChannelBinding> channelBindings, SocketGuild guild, ChannelOptions channelOptions)
        {
            var channelFrom = guild.TextChannels.FirstOrDefault(t => t.Name == channelOptions.From);
            var channelTo   = guild.TextChannels.FirstOrDefault(t => t.Name == channelOptions.To);

            bool channelFromBinding = channelFrom == null && channelOptions.From != "*";
            bool channelToBinding   = channelTo == null;

            if (channelFromBinding || channelToBinding)
            {
                if (channelFromBinding)
                {
                    logger.LogError($"Unknown from channel binding '{channelOptions.From}'");
                }
                if (channelToBinding)
                {
                    logger.LogError($"Unknown to channel binding '{channelOptions.To}'");
                }
                return;
            }

            IMentionable mention = null;

            if (!string.IsNullOrEmpty(channelOptions.Mention))
            {
                mention = guild.Roles.FirstOrDefault(t => t.Name == channelOptions.Mention);
                if (mention == null)
                {
                    logger.LogError($"Unknown role '{channelOptions.Mention}'");
                }
            }

            channelBindings.Add(new RaidChannelBinding(channelFrom, channelTo, mention, channelOptions.ScheduledRaids));
        }
示例#15
0
 public UdpPipeChannel(Socket socket, IPipelineFilter <TPackageInfo> pipelineFilter, ChannelOptions options, IPEndPoint remoteEndPoint, string sessionIdentifier)
     : base(pipelineFilter, options)
 {
     _socket           = socket;
     _remoteEndPoint   = remoteEndPoint;
     SessionIdentifier = sessionIdentifier;
 }
示例#16
0
 public UdpPipeChannel(Socket socket, IPipelineFilter <TPackageInfo> pipelineFilter, ChannelOptions options, IPEndPoint remoteEndPoint)
     : this(socket, pipelineFilter, options, remoteEndPoint, $"{remoteEndPoint.Address}:{remoteEndPoint.Port}")
 {
 }
示例#17
0
 public EasyClient(IPipelineFilter <TPackage> pipelineFilter, IPackageEncoder <TSendPackage> packageEncoder, ChannelOptions options)
     : base(pipelineFilter, options)
 {
     _packageEncoder = packageEncoder;
 }
示例#18
0
 public UdpSocketChannel(ChannelOptions options) : base(options)
 {
     this.Pipeline = new UdpChannelPipeline(this);
 }
        private async void InitTcpConnectAndReceive()
        {
            var options = new ChannelOptions
            {
                Logger       = NullLogger.Instance,
                ReadAsDemand = true
            };

            _sendTcpClient = new EasyClient <TextPackageInfo>(new LinePipelineFilter(), options).AsClient();

            _connected = await _sendTcpClient.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 8888));

            var connectPackage = new TcpPackage()
            {
                OpCode      = OpCode.Connect,
                LocalName   = TbUserName.Text,
                RemoteName  = "Server",
                MessageType = MessageType.Text
            };

            await _sendTcpClient.SendAsync(
                new ReadOnlyMemory <byte>(Encoding.UTF8.GetBytes(connectPackage.ToString())));


            while (true)
            {
                var receivePackage = TcpPackage.JsonToPackage((await _sendTcpClient.ReceiveAsync()).Text);
                if (string.IsNullOrEmpty(receivePackage.Message))
                {
                    continue;
                }

                switch (receivePackage.OpCode)
                {
                case OpCode.Connect:
                    SpOnlineList.Children.Clear();
                    var allItem = new UserItemsControl("All");
                    allItem.setRoteName = SetRoteName;
                    SpOnlineList.Children.Add(allItem);
                    foreach (var onlineClient in receivePackage.Clients)
                    {
                        var childItem = new UserItemsControl(onlineClient.Username);
                        childItem.setRoteName = SetRoteName;
                        SpOnlineList.Children.Add(childItem);
                    }
                    TbUserName.IsEnabled       = false;
                    BtnConnectServer.IsEnabled = false;
                    break;

                case OpCode.DisConnect:
                    MessageBox.Show(receivePackage.Message, receivePackage.LocalName);
                    TbUserName.IsEnabled = true;
                    break;

                case OpCode.All:
                case OpCode.Single:
                    LbChatWith.Content = receivePackage.OpCode == OpCode.All
                            ? receivePackage.RemoteName : receivePackage.LocalName;
                    switch (receivePackage.MessageType)
                    {
                    case MessageType.Text:
                        ChatArea.Children.Add(new ReceiveControl(receivePackage, null));
                        break;

                    case MessageType.Image:
                        // 建立UDP客户端 直接接收消息
                        ReceiveImage(receivePackage: receivePackage);
                        break;

                    case MessageType.File:
                        var res = MessageBox.Show("是否接收文件?", "提示", MessageBoxButton.YesNo,
                                                  MessageBoxImage.Information);
                        if (res == MessageBoxResult.Yes)
                        {
                            // 发送TCP确认连接消息
                            // 创建UDP客户端
                        }
                        break;
                    }
                    break;

                case OpCode.Confirm:
                    switch (receivePackage.MessageType)
                    {
                    case MessageType.Image:
                        var fileName = await SendImage();

                        await Task.Delay(1000);
                        await SendImage(fileName, receivePackage);

                        break;

                    case MessageType.File:
                        break;
                    }
                    break;
                }

                Scr.ScrollToEnd();

                if (_connected)
                {
                    BdConnectState.Background = new SolidColorBrush(Colors.LimeGreen);
                    TbUserName.IsEnabled      = false;
                }
                else
                {
                    BdConnectState.Background = new SolidColorBrush(Colors.OrangeRed);
                    TbUserName.IsEnabled      = true;
                    break;
                }

                //await Task.Delay(1000);
            }
        }
            internal Channel(MultiplexingStream multiplexingStream, int id, string name, ChannelOptions channelOptions)
            {
                Requires.NotNull(multiplexingStream, nameof(multiplexingStream));
                Requires.NotNull(channelOptions, nameof(channelOptions));
                Requires.NotNull(name, nameof(name));

                this.UnderlyingMultiplexingStream = multiplexingStream;
                this.Id          = id;
                this.Name        = name;
                this.TraceSource = channelOptions.TraceSource ?? new TraceSource($"{nameof(MultiplexingStream)}.{nameof(Channel)} {id} ({name})", SourceLevels.Critical);

                this.receivingPipe    = new Pipe();
                this.transmissionPipe = new Pipe();
                this.DisposeSelfOnFailure(this.ProcessOutboundTransmissionsAsync());
                this.DisposeSelfOnFailure(this.AutoCloseOnPipesClosureAsync());
            }
示例#21
0
 public WithTextProtocolWithEncryption(ITestOutputHelper output)
     : base(output)
 {
     _options = new ChannelOptions(Crypto.GetDefaultParams());
     _client  = GetRestClient();
 }
 internal static Channel <T> CreateChannel <T>(ChannelOptions channelOptions)
 => channelOptions is BoundedChannelOptions bco
        public new IChannelCreator CreateChannelCreator <TPackageInfo>(ListenOptions options, ChannelOptions channelOptions, ILoggerFactory loggerFactory, object pipelineFilterFactory)
        {
            var filterFactory = pipelineFilterFactory as IPipelineFilterFactory <TPackageInfo>;

            channelOptions.Logger = loggerFactory.CreateLogger(nameof(IChannel));

            var channelFactoryLogger = loggerFactory.CreateLogger(nameof(TcpChannelCreator));

            var channelFactory = new Func <Socket, ValueTask <IChannel> >(async(s) =>
            {
                ApplySocketOptions(s, options, channelOptions, channelFactoryLogger);

                Stream stream = new NetworkStream(s, true);
                if (options.Security != SslProtocols.None)
                {
                    var authOptions = new SslServerAuthenticationOptions();

                    authOptions.EnabledSslProtocols       = options.Security;
                    authOptions.ServerCertificate         = options.CertificateOptions.Certificate;
                    authOptions.ClientCertificateRequired = options.CertificateOptions.ClientCertificateRequired;

                    if (options.CertificateOptions.RemoteCertificateValidationCallback != null)
                    {
                        authOptions.RemoteCertificateValidationCallback = options.CertificateOptions.RemoteCertificateValidationCallback;
                    }

                    var sslStream = new SslStream(stream, false);
                    await sslStream.AuthenticateAsServerAsync(authOptions, CancellationToken.None).ConfigureAwait(false);

                    stream = sslStream;
                }
                stream = new GZipReadWriteStream(stream, true);
                return(new StreamPipeChannel <TPackageInfo>(stream, s.RemoteEndPoint, s.LocalEndPoint, filterFactory.Create(s), channelOptions));
            });

            return(new TcpChannelCreator(options, channelFactory, channelFactoryLogger));
        }
示例#24
0
 public TcpPipeChannel(Socket socket, ChannelOptions <TPackage> options, PipePackageFilter <TPackage> pipePackageFilter) : base(options, pipePackageFilter)
 {
     _socket = socket;
 }
示例#25
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DecodingContext"/> class.
 /// </summary>
 /// <param name="options">Channel options used for the encode / decode operations.</param>
 public DecodingContext(ChannelOptions options)
 {
     ChannelOptions = options ?? new ChannelOptions();
 }
示例#26
0
 private double?GetDownloadLimit(ChannelOptions channelOptions)
 {
     return(channelOptions.DownloadSizeLimit);
 }
示例#27
0
 public ChannelConfig(ClockRate clockRate, byte latencyTimer, ChannelOptions options)
 {
     this.clockRate    = clockRate;
     this.latencyTimer = latencyTimer;
     this.options      = options;
 }
示例#28
0
 public TransparentPipeChannel(IPipelineFilter <TPackageInfo> pipelineFilter, ChannelOptions options)
     : base(pipelineFilter, options)
 {
     _tcs         = new TaskCompletionSource <int>();
     _channelTask = _tcs.Task;
 }
示例#29
0
 private void ProcessMessages <T>(IEnumerable <T> payloads, ChannelOptions options) where T : IEncodedMessage
 {
     DecodePayloads(options, payloads as IEnumerable <IEncodedMessage>);
 }
示例#30
0
        public IChannelCreator CreateChannelCreator <TPackageInfo>(ListenOptions options, ChannelOptions channelOptions, ILoggerFactory loggerFactory, object pipelineFilterFactory)
            where TPackageInfo : class
        {
            var filterFactory = pipelineFilterFactory as IPipelineFilterFactory <TPackageInfo>;

            channelOptions.Logger = loggerFactory.CreateLogger(nameof(IChannel));

            if (options.Security == SslProtocols.None)
            {
                return(new TcpChannelCreator(options, (s) => Task.FromResult((new TcpPipeChannel <TPackageInfo>(s, filterFactory.Create(s), channelOptions)) as IChannel), loggerFactory.CreateLogger(nameof(TcpChannelCreator))));
            }
            else
            {
                var channelFactory = new Func <Socket, Task <IChannel> >(async(s) =>
                {
                    var authOptions = new SslServerAuthenticationOptions();
                    authOptions.EnabledSslProtocols = options.Security;
                    authOptions.ServerCertificate   = options.CertificateOptions.Certificate;
                    var stream = new SslStream(new NetworkStream(s, true), false);
                    await stream.AuthenticateAsServerAsync(authOptions, CancellationToken.None);
                    return(new StreamPipeChannel <TPackageInfo>(stream, filterFactory.Create(s), channelOptions));
                });

                return(new TcpChannelCreator(options, channelFactory, loggerFactory.CreateLogger(nameof(TcpChannelCreator))));
            }
        }
 public AbstractSocketChannel(ChannelOptions options)
 {
     this.ConnectionId  = CorrelationIdGenerator.GetNextId();
     this.Options       = options;
     this.RemoteAddress = new IPEndPoint(IPAddress.Parse(Options.IP), Options.Port);
 }
示例#32
0
            /// <summary>
            /// Apply channel options to this channel, including setting up or migrating to an user-supplied pipe writer/reader pair.
            /// </summary>
            /// <param name="channelOptions">The channel options to apply.</param>
            private void ApplyChannelOptions(ChannelOptions channelOptions)
            {
                Requires.NotNull(channelOptions, nameof(channelOptions));
                Assumes.Null(this.TraceSource); // We've already applied options

                try
                {
                    this.TraceSource = channelOptions.TraceSource
                                       ?? this.MultiplexingStream.DefaultChannelTraceSourceFactory?.Invoke(this.Id, this.Name)
                                       ?? new TraceSource($"{nameof(Streams.MultiplexingStream)}.{nameof(Channel)} {this.Id} ({this.Name})", SourceLevels.Critical);

                    lock (this.SyncObject)
                    {
                        Verify.NotDisposed(this);
                        if (channelOptions.ExistingPipe != null)
                        {
                            if (this.mxStreamIOWriter != null)
                            {
                                // A Pipe was already created (because data has been coming in for this channel even before it was accepted).
                                // To be most efficient, we need to:
                                // 1. Start forwarding all bytes written with this.mxStreamIOWriter to channelOptions.ExistingPipe.Output
                                // 2. Arrange for the *next* call to GetReceivedMessagePipeWriterAsync to:
                                //      call this.mxStreamIOWriter.Complete()
                                //      wait for our forwarding code to finish (without propagating copmletion to channel.ExistingPipe.Output)
                                //      return channel.ExistingPipe.Output
                                //    From then on, GetReceivedMessagePipeWriterAsync should simply return channel.ExistingPipe.Output
                                // Since this channel hasn't yet been exposed to the local owner, we can just replace the PipeWriter they use to transmit.

                                // Take ownership of reading bytes that the MultiplexingStream may have already written to this channel.
                                var mxStreamIncomingBytesReader = this.channelIO !.Input;
                                this.channelIO = null;

                                // Forward any bytes written by the MultiplexingStream to the ExistingPipe.Output writer,
                                // and make that ExistingPipe.Output writer available only after the old Pipe-based writer has completed.
                                // First, capture the ExistingPipe as a local since ChannelOptions is a mutable type, and we're going to need
                                // its current value later on.
                                var existingPipe = channelOptions.ExistingPipe;
                                this.switchingToExistingPipe = Task.Run(async delegate
                                {
                                    // Await propagation of all bytes. Don't complete the ExistingPipe.Output when we're done because we still want to use it.
                                    await mxStreamIncomingBytesReader.LinkToAsync(existingPipe.Output, propagateSuccessfulCompletion: false).ConfigureAwait(false);
                                    return(existingPipe.Output);
                                });
                            }
                            else
                            {
                                // We haven't created a Pipe yet, so we can simply direct all writing to the ExistingPipe.Output immediately.
                                this.mxStreamIOWriter = channelOptions.ExistingPipe.Output;
                            }

                            this.mxStreamIOReader = channelOptions.ExistingPipe.Input;
                        }
                        else if (channelOptions.InputPipeOptions != null && this.mxStreamIOWriter != null)
                        {
                            this.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Data received on channel {0} before it was accepted. Migrating data from temporary buffer to accepted channel's new pipe.", this.Id);

                            // Similar strategy to the situation above with ExistingPipe.
                            // Take ownership of reading bytes that the MultiplexingStream may have already written to this channel.
                            var mxStreamIncomingBytesReader = this.channelIO !.Input;

                            var writerRelay = new Pipe();
                            var readerRelay = new Pipe(channelOptions.InputPipeOptions);
                            this.mxStreamIOReader = writerRelay.Reader;
                            this.channelIO        = new DuplexPipe(readerRelay.Reader, writerRelay.Writer);

                            this.switchingToExistingPipe = Task.Run(async delegate
                            {
                                // Await propagation of all bytes. Don't complete the readerRelay.Writer when we're done because we still want to use it.
                                await mxStreamIncomingBytesReader.LinkToAsync(readerRelay.Writer, propagateSuccessfulCompletion: false).ConfigureAwait(false);
                                this.TraceSource.TraceEvent(TraceEventType.Verbose, 0, "Data from temporary buffer to accepted channel {0}'s new pipe is completed.", this.Id);

                                return(readerRelay.Writer);
                            });
                        }
                        else
                        {
                            this.InitializeOwnPipes(channelOptions.InputPipeOptions ?? PipeOptions.Default);
                        }
                    }

                    this.mxStreamIOReaderCompleted = this.ProcessOutboundTransmissionsAsync();
                    this.DisposeSelfOnFailure(this.mxStreamIOReaderCompleted);
                    this.DisposeSelfOnFailure(this.AutoCloseOnPipesClosureAsync());
                }
                catch (Exception ex)
                {
                    this.optionsAppliedTaskSource?.TrySetException(ex);
                    throw;
                }
                finally
                {
                    this.optionsAppliedTaskSource?.TrySetResult(null);
                }
            }