|  | @@ -1027,6 +1027,8 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// <inheritdoc/>
 | 
	
		
			
				|  |  |          public void UploadFile(Stream input, string path, bool canOverride, Action<ulong>? uploadCallback = null)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            ThrowHelper.ThrowIfNull(input);
 | 
	
		
			
				|  |  | +            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 | 
	
		
			
				|  |  |              CheckDisposed();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var flags = Flags.Write | Flags.Truncate;
 | 
	
	
		
			
				|  | @@ -1040,15 +1042,31 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                  flags |= Flags.CreateNew;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            InternalUploadFile(input, path, flags, asyncResult: null, uploadCallback);
 | 
	
		
			
				|  |  | +            InternalUploadFile(
 | 
	
		
			
				|  |  | +                input,
 | 
	
		
			
				|  |  | +                path,
 | 
	
		
			
				|  |  | +                flags,
 | 
	
		
			
				|  |  | +                asyncResult: null,
 | 
	
		
			
				|  |  | +                uploadCallback,
 | 
	
		
			
				|  |  | +                isAsync: false,
 | 
	
		
			
				|  |  | +                default).GetAwaiter().GetResult();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <inheritdoc />
 | 
	
		
			
				|  |  |          public Task UploadFileAsync(Stream input, string path, CancellationToken cancellationToken = default)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | +            ThrowHelper.ThrowIfNull(input);
 | 
	
		
			
				|  |  | +            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 | 
	
		
			
				|  |  |              CheckDisposed();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            return InternalUploadFileAsync(input, path, cancellationToken);
 | 
	
		
			
				|  |  | +            return InternalUploadFile(
 | 
	
		
			
				|  |  | +                input,
 | 
	
		
			
				|  |  | +                path,
 | 
	
		
			
				|  |  | +                Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen,
 | 
	
		
			
				|  |  | +                asyncResult: null,
 | 
	
		
			
				|  |  | +                uploadCallback: null,
 | 
	
		
			
				|  |  | +                isAsync: true,
 | 
	
		
			
				|  |  | +                cancellationToken);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
	
		
			
				|  | @@ -1163,9 +1181,9 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |          /// </remarks>
 | 
	
		
			
				|  |  |          public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            CheckDisposed();
 | 
	
		
			
				|  |  |              ThrowHelper.ThrowIfNull(input);
 | 
	
		
			
				|  |  |              ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 | 
	
		
			
				|  |  | +            CheckDisposed();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var flags = Flags.Write | Flags.Truncate;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1180,19 +1198,28 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              var asyncResult = new SftpUploadAsyncResult(asyncCallback, state);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            ThreadAbstraction.ExecuteThread(() =>
 | 
	
		
			
				|  |  | +            _ = DoUploadAndSetResult();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            async Task DoUploadAndSetResult()
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  try
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    InternalUploadFile(input, path, flags, asyncResult, uploadCallback);
 | 
	
		
			
				|  |  | +                    await InternalUploadFile(
 | 
	
		
			
				|  |  | +                        input,
 | 
	
		
			
				|  |  | +                        path,
 | 
	
		
			
				|  |  | +                        flags,
 | 
	
		
			
				|  |  | +                        asyncResult,
 | 
	
		
			
				|  |  | +                        uploadCallback,
 | 
	
		
			
				|  |  | +                        isAsync: true,
 | 
	
		
			
				|  |  | +                        CancellationToken.None).ConfigureAwait(false);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      asyncResult.SetAsCompleted(exception: null, completedSynchronously: false);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |                  catch (Exception exp)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    asyncResult.SetAsCompleted(exception: exp, completedSynchronously: false);
 | 
	
		
			
				|  |  | +                    asyncResult.SetAsCompleted(exp, completedSynchronously: false);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -            });
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              return asyncResult;
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -2284,11 +2311,16 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |                          var remoteFileName = string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", destinationPath, localFile.Name);
 | 
	
		
			
				|  |  |                          try
 | 
	
		
			
				|  |  |                          {
 | 
	
		
			
				|  |  | -#pragma warning disable CA2000 // Dispose objects before losing scope; false positive
 | 
	
		
			
				|  |  |                              using (var file = File.OpenRead(localFile.FullName))
 | 
	
		
			
				|  |  | -#pragma warning restore CA2000 // Dispose objects before losing scope; false positive
 | 
	
		
			
				|  |  |                              {
 | 
	
		
			
				|  |  | -                                InternalUploadFile(file, remoteFileName, uploadFlag, asyncResult: null, uploadCallback: null);
 | 
	
		
			
				|  |  | +                                InternalUploadFile(
 | 
	
		
			
				|  |  | +                                    file,
 | 
	
		
			
				|  |  | +                                    remoteFileName,
 | 
	
		
			
				|  |  | +                                    uploadFlag,
 | 
	
		
			
				|  |  | +                                    asyncResult: null,
 | 
	
		
			
				|  |  | +                                    uploadCallback: null,
 | 
	
		
			
				|  |  | +                                    isAsync: false,
 | 
	
		
			
				|  |  | +                                    CancellationToken.None).GetAwaiter().GetResult();
 | 
	
		
			
				|  |  |                              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                              uploadedFiles.Add(localFile);
 | 
	
	
		
			
				|  | @@ -2455,37 +2487,42 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Internals the upload file.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="input">The input.</param>
 | 
	
		
			
				|  |  | -        /// <param name="path">The path.</param>
 | 
	
		
			
				|  |  | -        /// <param name="flags">The flags.</param>
 | 
	
		
			
				|  |  | -        /// <param name="asyncResult">An <see cref="IAsyncResult"/> that references the asynchronous request.</param>
 | 
	
		
			
				|  |  | -        /// <param name="uploadCallback">The upload callback.</param>
 | 
	
		
			
				|  |  | -        /// <exception cref="ArgumentNullException"><paramref name="input" /> is <see langword="null"/>.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="ArgumentException"><paramref name="path" /> is <see langword="null"/> or contains whitespace.</exception>
 | 
	
		
			
				|  |  | -        /// <exception cref="SshConnectionException">Client not connected.</exception>
 | 
	
		
			
				|  |  | -        private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult? asyncResult, Action<ulong>? uploadCallback)
 | 
	
		
			
				|  |  | +#pragma warning disable S6966 // Awaitable method should be used
 | 
	
		
			
				|  |  | +        private async Task InternalUploadFile(
 | 
	
		
			
				|  |  | +            Stream input,
 | 
	
		
			
				|  |  | +            string path,
 | 
	
		
			
				|  |  | +            Flags flags,
 | 
	
		
			
				|  |  | +            SftpUploadAsyncResult? asyncResult,
 | 
	
		
			
				|  |  | +            Action<ulong>? uploadCallback,
 | 
	
		
			
				|  |  | +            bool isAsync,
 | 
	
		
			
				|  |  | +            CancellationToken cancellationToken)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            ThrowHelper.ThrowIfNull(input);
 | 
	
		
			
				|  |  | -            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 | 
	
		
			
				|  |  | +            Debug.Assert(isAsync || cancellationToken == default);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if (_sftpSession is null)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  throw new SshConnectionException("Client not connected.");
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var fullPath = _sftpSession.GetCanonicalPath(path);
 | 
	
		
			
				|  |  | +            string fullPath;
 | 
	
		
			
				|  |  | +            byte[] handle;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            var handle = _sftpSession.RequestOpen(fullPath, flags);
 | 
	
		
			
				|  |  | +            if (isAsync)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                handle = await _sftpSession.RequestOpenAsync(fullPath, flags, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                fullPath = _sftpSession.GetCanonicalPath(path);
 | 
	
		
			
				|  |  | +                handle = _sftpSession.RequestOpen(fullPath, flags);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              ulong offset = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // create buffer of optimal length
 | 
	
		
			
				|  |  |              var buffer = new byte[_sftpSession.CalculateOptimalWriteLength(_bufferSize, handle)];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            int bytesRead;
 | 
	
		
			
				|  |  |              var expectedResponses = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // We will send out all the write requests without waiting for each response.
 | 
	
	
		
			
				|  | @@ -2495,8 +2532,21 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              ExceptionDispatchInfo? exception = null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
 | 
	
		
			
				|  |  | +            while (true)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | +                var bytesRead = isAsync
 | 
	
		
			
				|  |  | +#if NET
 | 
	
		
			
				|  |  | +                    ? await input.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +                    ? await input.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +                    : input.Read(buffer, 0, buffer.Length);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (bytesRead == 0)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    break;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |                  if (asyncResult is not null && asyncResult.IsUploadCanceled)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      break;
 | 
	
	
		
			
				|  | @@ -2555,34 +2605,28 @@ namespace Renci.SshNet
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if (Volatile.Read(ref expectedResponses) != 0)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                _sftpSession.WaitOnHandle(mres.WaitHandle, _operationTimeout);
 | 
	
		
			
				|  |  | +                if (isAsync)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    await _sftpSession.WaitOnHandleAsync(mres.WaitHandle, _operationTimeout, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                else
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    _sftpSession.WaitOnHandle(mres.WaitHandle, _operationTimeout);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              exception?.Throw();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            _sftpSession.RequestClose(handle);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private async Task InternalUploadFileAsync(Stream input, string path, CancellationToken cancellationToken)
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            ThrowHelper.ThrowIfNull(input);
 | 
	
		
			
				|  |  | -            ThrowHelper.ThrowIfNullOrWhiteSpace(path);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            if (_sftpSession is null)
 | 
	
		
			
				|  |  | +            if (isAsync)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new SshConnectionException("Client not connected.");
 | 
	
		
			
				|  |  | +                await _sftpSession.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            cancellationToken.ThrowIfCancellationRequested();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | -            var openStreamTask = SftpFileStream.OpenAsync(_sftpSession, fullPath, FileMode.Create, FileAccess.Write, (int)_bufferSize, cancellationToken);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            using (var output = await openStreamTask.ConfigureAwait(false))
 | 
	
		
			
				|  |  | +            else
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                await input.CopyToAsync(output, 81920, cancellationToken).ConfigureAwait(false);
 | 
	
		
			
				|  |  | +                _sftpSession.RequestClose(handle);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +#pragma warning restore S6966 // Awaitable method should be used
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Called when client is connected to the server.
 |