/// <summary>
        /// Creates a streaming specific connector client.
        /// </summary>
        private IConnectorClient CreateStreamingConnectorClient(Activity activity, StreamingRequestHandler requestHandler)
        {
            var emptyCredentials = (ChannelProvider != null && ChannelProvider.IsGovernment()) ?
                                   MicrosoftGovernmentAppCredentials.Empty :
                                   MicrosoftAppCredentials.Empty;
            var streamingClient = new StreamingHttpClient(requestHandler, Logger);
            var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl), emptyCredentials, customHttpClient: streamingClient);

            return(connectorClient);
        }
        /// <summary>
        /// Creates a streaming specific connector client.
        /// </summary>
        private IConnectorClient CreateStreamingConnectorClient(Activity activity, StreamingRequestHandler requestHandler)
        {
            var emptyCredentials = (ChannelProvider != null && ChannelProvider.IsGovernment()) ?
                                   MicrosoftGovernmentAppCredentials.Empty :
                                   MicrosoftAppCredentials.Empty;

#pragma warning disable CA2000 // Dispose objects before losing scope (We need to make ConnectorClient disposable to fix this, ignoring it for now)
            var streamingClient = new StreamingHttpClient(requestHandler, Logger);
#pragma warning restore CA2000 // Dispose objects before losing scope
            var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl), emptyCredentials, customHttpClient: streamingClient);
            return(connectorClient);
        }
        /// <summary>
        /// Sends an activity.
        /// </summary>
        /// <param name="activity">>The <see cref="Activity"/> to send.</param>
        /// <param name="cancellationToken">A cancellation token that can be used by other objects
        /// or threads to receive notice of cancellation.</param>
        /// <returns>A task representing the asynchronous operation.</returns>
        /// <remarks>If the task completes successfully, the result contains a the resource response object.</remarks>
        public async Task <ResourceResponse> SendStreamingActivityAsync(Activity activity, CancellationToken cancellationToken = default)
        {
            // Check to see if any of this adapter's StreamingRequestHandlers is associated with this conversation.
            var possibleHandlers = RequestHandlers.Where(x => x.ServiceUrl == activity.ServiceUrl).Where(y => y.HasConversation(activity.Conversation.Id));

            if (possibleHandlers.Any())
            {
                if (possibleHandlers.Count() > 1)
                {
                    // The conversation has moved to a new connection and the former StreamingRequestHandler needs to be told to forget about it.
                    var correctHandler = possibleHandlers.OrderBy(x => x.ConversationAddedTime(activity.Conversation.Id)).Last();
                    foreach (var handler in possibleHandlers)
                    {
                        if (handler != correctHandler)
                        {
                            handler.ForgetConversation(activity.Conversation.Id);
                        }
                    }

                    return(await correctHandler.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false));
                }

                return(await possibleHandlers.First().SendActivityAsync(activity, cancellationToken).ConfigureAwait(false));
            }

            if (ConnectedBot != null)
            {
                // This is a proactive message that will need a new streaming connection opened.
                // The ServiceUrl of a streaming connection follows the pattern "urn:[ChannelName]:[Protocol]:[Host]".
#pragma warning disable CA2000 // Dispose objects before losing scope (we can't fix this without closing the socket connection, this should be addressed after we make StreamingRequestHandler disposable and we dispose the connector )
                var connection = new ClientWebSocket();
#pragma warning restore CA2000 // Dispose objects before losing scope
                var uri      = activity.ServiceUrl.Split(':');
                var protocol = uri[uri.Length - 2];
                var host     = uri[uri.Length - 1];
                await connection.ConnectAsync(new Uri(protocol + host + "/api/messages"), cancellationToken).ConfigureAwait(false);

#pragma warning disable CA2000 // Dispose objects before losing scope (We'll dispose this when the adapter gets disposed or when elements are removed)
                var handler = new StreamingRequestHandler(ConnectedBot, this, connection, Logger);
#pragma warning restore CA2000 // Dispose objects before losing scope

                if (RequestHandlers == null)
                {
                    RequestHandlers = new List <StreamingRequestHandler>();
                }

                RequestHandlers.Add(handler);

                return(await handler.SendActivityAsync(activity, cancellationToken).ConfigureAwait(false));
            }

            return(null);
        }
        /// <summary>
        /// Creates a new StreamingRequestHandler to listen to the specififed Named Pipe
        /// and pass requests to this adapter.
        /// </summary>
        /// <param name="pipeName">The name of the Named Pipe to connect to.</param>
        /// <param name="bot">The bot to use when processing activities received over the Named Pipe.</param>
        /// <returns>A task that completes only once the StreamingRequestHandler has stopped listening
        /// for incoming requests on the Named Pipe.</returns>
        public async Task ConnectNamedPipeAsync(string pipeName, IBot bot)
        {
            if (string.IsNullOrEmpty(pipeName))
            {
                throw new ArgumentNullException(nameof(pipeName));
            }

            ConnectedBot   = bot ?? throw new ArgumentNullException(nameof(bot));
            ClaimsIdentity = ClaimsIdentity ?? new ClaimsIdentity();

            if (RequestHandlers == null)
            {
                RequestHandlers = new List <StreamingRequestHandler>();
            }

            var requestHandler = new StreamingRequestHandler(bot, this, pipeName, Logger);

            RequestHandlers.Add(requestHandler);

            await requestHandler.ListenAsync().ConfigureAwait(false);
        }
        /// <summary>
        /// Creates a new StreamingRequestHandler to listen to the specified Named Pipe
        /// and pass requests to this adapter.
        /// </summary>
        /// <param name="pipeName">The name of the Named Pipe to connect to.</param>
        /// <param name="bot">The bot to use when processing activities received over the Named Pipe.</param>
        /// <param name="audience">The specified recipient of all outgoing activities.</param>
        /// <returns>A task that completes only once the StreamingRequestHandler has stopped listening
        /// for incoming requests on the Named Pipe.</returns>
        public async Task ConnectNamedPipeAsync(string pipeName, IBot bot, string audience = null)
        {
            if (string.IsNullOrEmpty(pipeName))
            {
                throw new ArgumentNullException(nameof(pipeName));
            }

            ConnectedBot   = bot ?? throw new ArgumentNullException(nameof(bot));
            ClaimsIdentity = ClaimsIdentity ?? new ClaimsIdentity();

            if (RequestHandlers == null)
            {
                RequestHandlers = new List <StreamingRequestHandler>();
            }

#pragma warning disable CA2000 // Dispose objects before losing scope (We'll dispose this when the adapter gets disposed or when elements are removed)
            var requestHandler = new StreamingRequestHandler(bot, this, pipeName, audience, Logger);
#pragma warning restore CA2000 // Dispose objects before losing scope
            RequestHandlers.Add(requestHandler);

            await requestHandler.ListenAsync().ConfigureAwait(false);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="StreamingHttpClient"/> class.
 /// An implementation of <see cref="HttpClient"/> that adds compatibility with streaming connections.
 /// </summary>
 /// <param name="requestHandler">The <see cref="StreamingRequestHandler"/> to send requests through.</param>
 /// <param name="logger">A logger.</param>
 public StreamingHttpClient(StreamingRequestHandler requestHandler, ILogger logger = null)
 {
     _requestHandler = requestHandler ?? throw new ArgumentNullException(nameof(requestHandler));
     _logger         = logger ?? NullLogger.Instance;
 }