コード例 #1
0
        /// <summary>
        /// Closes the write stream and associated socket (if open),
        /// </summary>
        /// <param name="ctsToken"></param>
        /// <returns></returns>
        public async Task CloseFileDataStreamAsync(CancellationToken ctsToken = default(CancellationToken))
        {
            Logger?.LogDebug("[FtpClient] Closing write file stream");
            dataStream.Dispose();

            await ControlStream.GetResponseAsync(ctsToken);
        }
コード例 #2
0
        /// <summary>
        /// Renames a file on the FTP server
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public async Task RenameAsync(string from, string to)
        {
            EnsureLoggedIn();
            Logger?.LogDebug($"[FtpClient] Renaming from {from}, to {to}");
            var renameFromResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.RNFR,
                Data       = from
            });

            if (renameFromResponse.FtpStatusCode != FtpStatusCode.FileCommandPending)
            {
                throw new FtpException(renameFromResponse.ResponseMessage);
            }

            var renameToResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.RNTO,
                Data       = to
            });

            if (renameToResponse.FtpStatusCode != FtpStatusCode.FileActionOK && renameToResponse.FtpStatusCode != FtpStatusCode.ClosingData)
            {
                throw new FtpException(renameFromResponse.ResponseMessage);
            }
        }
コード例 #3
0
        /// <summary>
        /// Changes the working directory to the given value for the current session
        /// </summary>
        /// <param name="directory"></param>
        /// <returns></returns>
        public async Task ChangeWorkingDirectoryAsync(string directory)
        {
            Logger?.LogTrace($"[FtpClient] changing directory to {directory}");
            if (directory.IsNullOrWhiteSpace() || directory.Equals("."))
            {
                throw new ArgumentOutOfRangeException(nameof(directory), "Directory supplied was incorrect");
            }

            EnsureLoggedIn();

            var response = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.CWD,
                Data       = directory
            });

            if (!response.IsSuccess)
            {
                throw new FtpException(response.ResponseMessage);
            }

            var pwdResponse = await ControlStream.SendCommandAsync(FtpCommand.PWD);

            if (!response.IsSuccess)
            {
                throw new FtpException(response.ResponseMessage);
            }

            WorkingDirectory = pwdResponse.ResponseMessage.Split('"')[1];
        }
コード例 #4
0
        /// <summary>
        /// Produces a data socket using Passive (PASV) or Extended Passive (EPSV) mode
        /// </summary>
        /// <returns></returns>
        internal async Task <Stream> ConnectDataStreamAsync()
        {
            Logger?.LogTrace("[FtpClient] Connecting to a data socket");

            var epsvResult = await ControlStream.SendCommandAsync(FtpCommand.EPSV);

            int?passivePortNumber;

            if (epsvResult.FtpStatusCode == FtpStatusCode.EnteringExtendedPassive)
            {
                passivePortNumber = epsvResult.ResponseMessage.ExtractEpsvPortNumber();
            }
            else
            {
                // EPSV failed - try regular PASV
                var pasvResult = await ControlStream.SendCommandAsync(FtpCommand.PASV);

                if (pasvResult.FtpStatusCode != FtpStatusCode.EnteringPassive)
                {
                    throw new FtpException(pasvResult.ResponseMessage);
                }

                passivePortNumber = pasvResult.ResponseMessage.ExtractPasvPortNumber();
            }

            if (!passivePortNumber.HasValue)
            {
                throw new FtpException("Could not determine EPSV/PASV data port");
            }

            return(await ControlStream.OpenDataStreamAsync(Configuration.Host, passivePortNumber.Value, CancellationToken.None));
        }
コード例 #5
0
 public void Dispose()
 {
     Logger?.LogDebug("Disposing of FtpClient");
     Task.WaitAny(LogOutAsync());
     ControlStream?.Dispose();
     dataSocketSemaphore?.Dispose();
 }
コード例 #6
0
        /// <summary>
        ///     Attempts to log the user in to the FTP Server
        /// </summary>
        /// <returns></returns>
        public async Task LoginAsync()
        {
            if (IsConnected)
            {
                await LogOutAsync();
            }

            string username = Configuration.Username.IsNullOrWhiteSpace()
                ? Constants.ANONYMOUS_USER
                : Configuration.Username;

            await ControlStream.ConnectAsync();

            var usrResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.USER,
                Data       = username
            });

            await BailIfResponseNotAsync(usrResponse, FtpStatusCode.SendUserCommand, FtpStatusCode.SendPasswordCommand, FtpStatusCode.LoggedInProceed);

            var passResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.PASS,
                Data       = username != Constants.ANONYMOUS_USER ? Configuration.Password : string.Empty
            });

            await BailIfResponseNotAsync(passResponse, FtpStatusCode.LoggedInProceed);

            IsAuthenticated = true;

            if (ControlStream.IsEncrypted)
            {
                await ControlStream.SendCommandAsync(new FtpCommandEnvelope
                {
                    FtpCommand = FtpCommand.PBSZ,
                    Data       = "0"
                });

                await ControlStream.SendCommandAsync(new FtpCommandEnvelope
                {
                    FtpCommand = FtpCommand.PROT,
                    Data       = "P"
                });
            }

            Features = await DetermineFeaturesAsync();

            directoryProvider = DetermineDirectoryProvider();
            await EnableUTF8IfPossible();
            await SetTransferMode(Configuration.Mode, Configuration.ModeSecondType);

            if (Configuration.BaseDirectory != "/")
            {
                await CreateDirectoryAsync(Configuration.BaseDirectory);
            }

            await ChangeWorkingDirectoryAsync(Configuration.BaseDirectory);
        }
コード例 #7
0
ファイル: IRpcClient.cs プロジェクト: trumsuhu/gsudo
 public void Dispose()
 {
     DataStream?.Close();
     DataStream?.Dispose();
     ControlStream?.Close();
     ControlStream?.Dispose();
     IsAlive = false;
 }
コード例 #8
0
        private async Task <int> executeCommandAsync(string command, string data = null, bool getResponse = true, bool force = false)
        {
            ConnectTicker = 0;
            if (IsCanceled && !force)
            {
                commandIn = false; return(0);
            }

            int    response = 0;
            string fullCommand;

            if (data.NullEmpty())
            {
                fullCommand = command;
            }
            else
            {
                fullCommand = command + ' ' + data;
            }

            if (commandIn)
            {
                while (commandIn && !IsCanceled)
                {
                    await Task.Delay(200);
                }
            }

            commandIn = true;
            byte[] buffer = _encoding.GetBytes(fullCommand + "\r\n");
            try { await ControlStream.WriteAsync(buffer, 0, buffer.Length); }
            catch { commandIn = false; return(0); }

            if (getResponse)
            {
                bool notify = true;
                if ("PASS FEAT CLNT SYST".Contains(command))
                {
                    if (command == "PASS")
                    {
                        InfoMessage("PASS *** Hidden ***", MessageType.Sent);
                    }
                    else
                    {
                        notify = false;
                    }
                }
                else
                {
                    InfoMessage(fullCommand, MessageType.Sent);
                }

                response = await getResponseAsync(notify, force);
            }

            commandIn = false;
            return(response);
        }
コード例 #9
0
        /// <summary>
        /// Ignore any stale data we may have waiting on the stream
        /// </summary>
        /// <returns></returns>
        private async Task IgnoreStaleData()
        {
            if (IsConnected && ControlStream.SocketDataAvailable())
            {
                var staleData = await ControlStream.GetResponseAsync();

                Logger?.LogWarning($"Stale data detected: {staleData.ResponseMessage}");
            }
        }
コード例 #10
0
        /// <summary>
        /// Closes the write stream and associated socket (if open),
        /// </summary>
        /// <param name="ctsToken"></param>
        /// <returns></returns>
        public async Task CloseFileDataStreamAsync(CancellationToken ctsToken = default(CancellationToken))
        {
            Logger?.LogTrace("[FTPClient] Closing write file stream");
            dataStream.Dispose();

            if (ControlStream != null)
            {
                await ControlStream.GetResponseAsync(ctsToken);
            }
        }
コード例 #11
0
        /// <summary>
        /// Informs the FTP server of the client being used
        /// </summary>
        /// <param name="clientName"></param>
        /// <returns></returns>
        public async Task <FtpResponse> SetClientName(string clientName)
        {
            EnsureLoggedIn();
            Logger?.LogDebug($"[FtpClient] Setting client name to {clientName}");

            return(await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.CLNT,
                Data = clientName
            }));
        }
コード例 #12
0
 /// <summary>
 /// Lists all directories in the current working directory
 /// </summary>
 /// <returns></returns>
 public async Task <ReadOnlyCollection <FtpNodeInformation> > ListDirectoriesAsync()
 {
     try
     {
         EnsureLoggedIn();
         Logger?.LogDebug($"[FtpClient] Listing directories in {WorkingDirectory}");
         return(await directoryProvider.ListDirectoriesAsync());
     }
     finally
     {
         await ControlStream.GetResponseAsync();
     }
 }
コード例 #13
0
 /// <summary>
 /// Lists all files in the current working directory
 /// </summary>
 /// <returns></returns>
 public async Task <IEnumerable <FtpNodeInformation> > ListFilesAsync()
 {
     try
     {
         EnsureLoggedIn();
         LoggerHelper.Debug($"[FtpClient] Listing files in {WorkingDirectory}");
         return(await directoryProvider.ListFilesAsync());
     }
     finally
     {
         await ControlStream.GetResponseAsync();
     }
 }
コード例 #14
0
        public void AddSiteStreamUser_Returns_ControlStream()
        {
            const ulong  UserID   = 1;
            const string StreamID = "1_1_54e345d655ee3e8df359ac033648530bfbe26c5f";

            var ctx = InitializeTwitterContext();

            ControlStream cs = ctx.AddSiteStreamUser(new List <ulong> {
                UserID
            }, StreamID);

            Assert.NotNull(cs);
            Assert.Equal(CommandResponse, cs.CommandResponse);
        }
コード例 #15
0
        /// <summary>
        /// Lists all directories in the current working directory
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public async Task DeleteFileAsync(string fileName)
        {
            EnsureLoggedIn();
            Logger?.LogDebug($"[FtpClient] Deleting file {fileName}");
            var response = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.DELE,
                Data       = fileName
            });

            if (!response.IsSuccess)
            {
                throw new FtpException(response.ResponseMessage);
            }
        }
コード例 #16
0
        /// <summary>
        /// Determine if the FTP server supports UTF8 encoding, and set it to the default if possible
        /// </summary>
        /// <returns></returns>
        private async Task EnableUTF8IfPossible()
        {
            if (Equals(ControlStream.Encoding, Encoding.ASCII) && Features.Any(x => x == Constants.UTF8))
            {
                ControlStream.Encoding = Encoding.UTF8;
            }

            if (Equals(ControlStream.Encoding, Encoding.UTF8))
            {
                // If the server supports UTF8 it should already be enabled and this
                // command should not matter however there are conflicting drafts
                // about this so we'll just execute it to be safe.
                await ControlStream.SendCommandAsync("OPTS UTF8 ON");
            }
        }
コード例 #17
0
        /// <summary>
        ///     Attemps to log the user out asynchronously, sends the QUIT command and terminates the command socket.
        /// </summary>
        public async Task LogOutAsync()
        {
            await IgnoreStaleData();

            if (!IsConnected)
            {
                return;
            }

            Logger?.LogTrace("[FtpClient] Logging out");
            await ControlStream.SendCommandAsync(FtpCommand.QUIT);

            ControlStream.Disconnect();
            IsAuthenticated = false;
        }
コード例 #18
0
ファイル: IRpcClient.cs プロジェクト: trumsuhu/gsudo
        public async Task WriteElevationRequest(ElevationRequest elevationRequest)
        {
            // Using Binary instead of Newtonsoft.JSON to reduce load times.
            var ms = new System.IO.MemoryStream();
            new BinaryFormatter()
                    { TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesAlways, Binder = new MySerializationBinder() }
                .Serialize(ms, elevationRequest);
            ms.Seek(0, System.IO.SeekOrigin.Begin);

            byte[] lengthArray = BitConverter.GetBytes(ms.Length);
            Logger.Instance.Log($"ElevationRequest length {ms.Length}", LogLevel.Debug);

            await ControlStream.WriteAsync(lengthArray, 0, sizeof(int)).ConfigureAwait(false);
            await ControlStream.WriteAsync(ms.ToArray(), 0, (int)ms.Length).ConfigureAwait(false);
            await ControlStream.FlushAsync().ConfigureAwait(false);
        }
コード例 #19
0
        /// <summary>
        /// Determines the file size of the given file
        /// </summary>
        /// <param name="transferMode"></param>
        /// <param name="secondType"></param>
        /// <returns></returns>
        public async Task SetTransferMode(FtpTransferMode transferMode, char secondType = '\0')
        {
            EnsureLoggedIn();
            Logger?.LogTrace($"[FtpClient] Setting transfer mode {transferMode}, {secondType}");
            var response = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.TYPE,
                Data       = secondType != '\0'
                    ? $"{(char) transferMode} {secondType}"
                    : $"{(char) transferMode}"
            });

            if (!response.IsSuccess)
            {
                throw new FtpException(response.ResponseMessage);
            }
        }
コード例 #20
0
        private async Task <IEnumerable <string> > DetermineFeaturesAsync()
        {
            EnsureLoggedIn();
            Logger?.LogTrace("[FTPClient] Determining features");
            var response = await ControlStream.SendCommandAsync(FTPCommand.FEAT);

            if (response.FTPStatusCode == FTPStatusCode.COMMANDSYNTAXERROR || response.FTPStatusCode == FTPStatusCode.COMMANDNOTIMPLEMENTED)
            {
                return(Enumerable.Empty <string>());
            }

            var features = response.Data.Where(x => !x.StartsWith(((int)FTPStatusCode.SYSTEMHELPREPLY).ToString()) && !x.IsNullOrWhiteSpace())
                           .Select(x => x.Replace(Constants.CARRIAGE_RETURN, string.Empty).Trim())
                           .ToList();

            return(features);
        }
コード例 #21
0
        private async Task <IEnumerable <string> > DetermineFeaturesAsync()
        {
            EnsureLoggedIn();
            Logger?.LogTrace("[FtpClient] Determining features");
            var response = await ControlStream.SendCommandAsync(FtpCommand.FEAT);

            if (response.FtpStatusCode == FtpStatusCode.CommandSyntaxError || response.FtpStatusCode == FtpStatusCode.CommandNotImplemented)
            {
                return(Enumerable.Empty <string>());
            }

            var features = response.Data.Where(x => !x.StartsWith(((int)FtpStatusCode.SystemHelpReply).ToString()) && !x.IsNullOrWhiteSpace())
                           .Select(x => x.Replace(Constants.CARRIAGE_RETURN, string.Empty).Trim())
                           .ToList();

            return(features);
        }
コード例 #22
0
        /// <summary>
        /// 暂不支持EPRT
        /// 1:ipV4;2 ipV6
        /// EPRT |1|132.235.1.2|6275|
        /// EPRT |2|1080::8:800:200C:417A|5282|
        ///
        /// PORT 192,168,191,11,206,97
        /// </summary>
        internal async Task <Stream> PortDataStreamAsync()
        {
            // 主动模式时,客户端必须告知服务器接收数据的端口号,PORT 命令格式为:PORT address
            //PORT 192,168,191,11,206,97
            // address参数的格式为i1、i2、i3、i4、p1、p2,其中i1、i2、i3、i4表示IP地址
            // 下面通过.字符串来组合这四个参数得到IP地址
            System.Net.Sockets.TcpListener dataListener;
            string portListener;
            string sendString = string.Empty;
            var    localip = (System.Net.IPAddress)LocalEndPoint;
            Random random = new Random();
            int    random1, random2;
            int    port;
            string ip = string.Empty;

            while (true)
            {
                // 随机生成一个端口进行数据传输
                random1 = random.Next(5, 200);
                random2 = random.Next(0, 200);
                // 生成的端口号控制>1024的随机端口
                // 下面这个运算算法只是为了得到一个大于1024的端口值
                port = random1 << 8 | random2;
                try
                {
                    dataListener = new System.Net.Sockets.TcpListener(localip, port);
                    dataListener.Start();
                }
                catch
                {
                    continue;
                }
                ip           = localip.ToString().Replace('.', ',');
                portListener = string.Format("{0},{1},{2}", ip, random1, random2);
                // 必须把端口号IP地址告诉客户端,客户端接收到响应命令后,
                // 再通过新的端口连接服务器的端口P,然后进行文件数据传输
                break;
            }
            var pasvResult = await ControlStream.SendCommandAsync(new FtpCommandEnvelope()
            {
                FtpCommand = FtpCommand.PORT,
                Data       = portListener
            });

            return(await ControlStream.AcceptDataStreamAsync(dataListener));
        }
コード例 #23
0
        /// <summary>
        ///     Attemps to log the user out asynchronously, sends the QUIT command and terminates the command socket.
        /// </summary>
        public async Task LogOutAsync()
        {
            await IgnoreStaleData();

            if (!IsConnected)
            {
                return;
            }

            LoggerHelper.Trace("[FtpClient] Logging out");
            await ControlStream.SendCommandAsync(FtpCommand.QUIT);

            ControlStream.Disconnect();
            if (LocalEndPoint != null)
            {
                LocalEndPoint = null;
            }
            IsAuthenticated = false;
        }
コード例 #24
0
        /// <summary>
        /// Determines the file size of the given file
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public async Task <long> GetFileSizeAsync(string fileName)
        {
            EnsureLoggedIn();
            Logger?.LogDebug($"[FtpClient] Getting file size for {fileName}");
            var sizeResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.SIZE,
                Data       = fileName
            });

            if (sizeResponse.FtpStatusCode != FtpStatusCode.FileStatus)
            {
                throw new FtpException(sizeResponse.ResponseMessage);
            }

            long fileSize = long.Parse(sizeResponse.ResponseMessage);

            return(fileSize);
        }
コード例 #25
0
        public void Abort(ConnectionAbortedException ex)
        {
            lock (_sync)
            {
                if (ControlStream != null)
                {
                    // TODO need to await this somewhere or allow this to be called elsewhere?
                    ControlStream.SendGoAway(_highestOpenedStreamId).GetAwaiter().GetResult();
                }
            }

            _haveSentGoAway = true;

            // Abort currently active streams
            foreach (var stream in _streams.Values)
            {
                stream.Abort(new ConnectionAbortedException("The Http3Connection has been aborted"), Http3ErrorCode.UnexpectedFrame);
            }
            // TODO need to figure out if there is server initiated connection close rather than stream close?
        }
コード例 #26
0
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    if (ControlClient != null)
                    {
                        ControlClient.Close();
                    }

                    if (ControlStream != null)
                    {
                        ControlStream.Close();
                    }
                }
            }

            _disposed = true;
            OnDisposed();
        }
コード例 #27
0
        /// <summary>
        /// Opens a filestream to the given filename
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="command"></param>
        /// <returns></returns>
        private async Task <Stream> OpenFileStreamAsync(string fileName, FTPCommand command)
        {
            EnsureLoggedIn();
            Logger?.LogDebug($"[FTPClient] Opening filestream for {fileName}, {command}");
            dataStream = await ConnectDataStreamAsync();

            var retrResponse = await ControlStream.SendCommandAsync(new FTPCommandEnvelope
            {
                FTPCommand = command,
                Data       = fileName
            });

            if ((retrResponse.FTPStatusCode != FTPStatusCode.DATAALREADYOPEN) &&
                (retrResponse.FTPStatusCode != FTPStatusCode.OPENINGDATA) &&
                (retrResponse.FTPStatusCode != FTPStatusCode.CLOSINGDATA))
            {
                throw new FTPException(retrResponse.ResponseMessage);
            }

            return(dataStream);
        }
コード例 #28
0
        /// <summary>
        /// Opens a filestream to the given filename
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="command"></param>
        /// <returns></returns>
        private async Task <Stream> OpenFileStreamAsync(string fileName, FtpCommand command)
        {
            EnsureLoggedIn();
            Logger?.LogDebug($"[FtpClient] Opening filestream for {fileName}, {command}");
            dataStream = await ConnectDataStreamAsync();

            var retrResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = command,
                Data       = fileName
            });

            if ((retrResponse.FtpStatusCode != FtpStatusCode.DataAlreadyOpen) &&
                (retrResponse.FtpStatusCode != FtpStatusCode.OpeningData) &&
                (retrResponse.FtpStatusCode != FtpStatusCode.ClosingData))
            {
                throw new FtpException(retrResponse.ResponseMessage);
            }

            return(dataStream);
        }
コード例 #29
0
        /// <summary>
        /// Deletes the given directory from the FTP server
        /// </summary>
        /// <param name="directory"></param>
        /// <returns></returns>
        public async Task DeleteDirectoryAsync(string directory)
        {
            if (directory.IsNullOrWhiteSpace() || directory.Equals("."))
            {
                throw new ArgumentOutOfRangeException(nameof(directory), "Directory supplied was not valid");
            }

            if (directory == "/")
            {
                return;
            }

            Logger?.LogDebug($"[FtpClient] Deleting directory {directory}");

            EnsureLoggedIn();

            var rmdResponse = await ControlStream.SendCommandAsync(new FtpCommandEnvelope
            {
                FtpCommand = FtpCommand.RMD,
                Data       = directory
            });

            switch (rmdResponse.FtpStatusCode)
            {
            case FtpStatusCode.CommandOK:
            case FtpStatusCode.FileActionOK:
                return;

            case FtpStatusCode.ActionNotTakenFileUnavailable:
                await DeleteNonEmptyDirectory(directory);

                return;

            default:
                throw new FtpException(rmdResponse.ResponseMessage);
            }
        }
コード例 #30
0
        internal async Task InnerProcessRequestsAsync <TContext>(IHttpApplication <TContext> application)
        {
            // Start other three unidirectional streams here.
            var controlTask = CreateControlStream(application);
            var encoderTask = CreateEncoderStream(application);
            var decoderTask = CreateDecoderStream(application);

            try
            {
                while (true)
                {
                    var streamContext = await _multiplexedContext.AcceptAsync();

                    if (streamContext == null || _haveSentGoAway)
                    {
                        break;
                    }

                    var quicStreamFeature = streamContext.Features.Get <IStreamDirectionFeature>();
                    var streamIdFeature   = streamContext.Features.Get <IStreamIdFeature>();

                    Debug.Assert(quicStreamFeature != null);

                    var httpConnectionContext = new Http3StreamContext
                    {
                        ConnectionId  = streamContext.ConnectionId,
                        StreamContext = streamContext,
                        // TODO connection context is null here. Should we set it to anything?
                        ServiceContext     = _context.ServiceContext,
                        ConnectionFeatures = streamContext.Features,
                        MemoryPool         = _context.MemoryPool,
                        Transport          = streamContext.Transport,
                        TimeoutControl     = _context.TimeoutControl,
                        LocalEndPoint      = streamContext.LocalEndPoint as IPEndPoint,
                        RemoteEndPoint     = streamContext.RemoteEndPoint as IPEndPoint
                    };

                    if (!quicStreamFeature.CanWrite)
                    {
                        // Unidirectional stream
                        var stream = new Http3ControlStream <TContext>(application, this, httpConnectionContext);
                        ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                    }
                    else
                    {
                        // Keep track of highest stream id seen for GOAWAY
                        var streamId = streamIdFeature.StreamId;
                        HighestStreamId = streamId;

                        var http3Stream = new Http3Stream <TContext>(application, this, httpConnectionContext);
                        var stream      = http3Stream;
                        lock (_streams)
                        {
                            _streams[streamId] = http3Stream;
                        }
                        KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3);
                        ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false);
                    }
                }
            }
            finally
            {
                // Abort all streams as connection has shutdown.
                lock (_streams)
                {
                    foreach (var stream in _streams.Values)
                    {
                        stream.Abort(new ConnectionAbortedException("Connection is shutting down."));
                    }
                }

                ControlStream?.Abort(new ConnectionAbortedException("Connection is shutting down."));
                EncoderStream?.Abort(new ConnectionAbortedException("Connection is shutting down."));
                DecoderStream?.Abort(new ConnectionAbortedException("Connection is shutting down."));

                await controlTask;
                await encoderTask;
                await decoderTask;
            }
        }