-
-
Notifications
You must be signed in to change notification settings - Fork 933
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add async support to SftpClient and SftpFileStream (#819)
* Add FEATURE_TAP and net472 target * Add TAP async support to SftpClient and SftpFileStream * Add async support to DnsAbstraction and SocketAbstraction * Add async support to *Connector and refactor the hierarchy * Add ConnectAsync to BaseClient
- Loading branch information
1 parent
30d79c7
commit 7bdfc9e
Showing
21 changed files
with
1,611 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
#if FEATURE_TAP | ||
using System; | ||
using System.Net; | ||
using System.Net.Sockets; | ||
using System.Runtime.CompilerServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
namespace Renci.SshNet.Abstractions | ||
{ | ||
// Async helpers based on https://devblogs.microsoft.com/pfxteam/awaiting-socket-operations/ | ||
|
||
internal static class SocketExtensions | ||
{ | ||
sealed class SocketAsyncEventArgsAwaitable : SocketAsyncEventArgs, INotifyCompletion | ||
{ | ||
private readonly static Action SENTINEL = () => { }; | ||
|
||
private bool isCancelled; | ||
private Action continuationAction; | ||
|
||
public SocketAsyncEventArgsAwaitable() | ||
{ | ||
Completed += delegate { SetCompleted(); }; | ||
} | ||
|
||
public SocketAsyncEventArgsAwaitable ExecuteAsync(Func<SocketAsyncEventArgs, bool> func) | ||
{ | ||
if (!func(this)) | ||
{ | ||
SetCompleted(); | ||
} | ||
return this; | ||
} | ||
|
||
public void SetCompleted() | ||
{ | ||
IsCompleted = true; | ||
var continuation = continuationAction ?? Interlocked.CompareExchange(ref continuationAction, SENTINEL, null); | ||
if (continuation != null) | ||
{ | ||
continuation(); | ||
} | ||
} | ||
|
||
public void SetCancelled() | ||
{ | ||
isCancelled = true; | ||
SetCompleted(); | ||
} | ||
|
||
public SocketAsyncEventArgsAwaitable GetAwaiter() { return this; } | ||
|
||
public bool IsCompleted { get; private set; } | ||
|
||
void INotifyCompletion.OnCompleted(Action continuation) | ||
{ | ||
if (continuationAction == SENTINEL || Interlocked.CompareExchange(ref continuationAction, continuation, null) == SENTINEL) | ||
{ | ||
// We have already completed; run continuation asynchronously | ||
Task.Run(continuation); | ||
} | ||
} | ||
|
||
public void GetResult() | ||
{ | ||
if (isCancelled) | ||
{ | ||
throw new TaskCanceledException(); | ||
} | ||
else if (IsCompleted) | ||
{ | ||
if (SocketError != SocketError.Success) | ||
{ | ||
throw new SocketException((int)SocketError); | ||
} | ||
} | ||
else | ||
{ | ||
// We don't support sync/async | ||
throw new InvalidOperationException("The asynchronous operation has not yet completed."); | ||
} | ||
} | ||
} | ||
|
||
public static async Task ConnectAsync(this Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
using (var args = new SocketAsyncEventArgsAwaitable()) | ||
{ | ||
args.RemoteEndPoint = remoteEndpoint; | ||
|
||
using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, false)) | ||
{ | ||
await args.ExecuteAsync(socket.ConnectAsync); | ||
} | ||
} | ||
} | ||
|
||
public static async Task<int> ReceiveAsync(this Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken) | ||
{ | ||
cancellationToken.ThrowIfCancellationRequested(); | ||
|
||
using (var args = new SocketAsyncEventArgsAwaitable()) | ||
{ | ||
args.SetBuffer(buffer, offset, length); | ||
|
||
using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, false)) | ||
{ | ||
await args.ExecuteAsync(socket.ReceiveAsync); | ||
} | ||
|
||
return args.BytesTransferred; | ||
} | ||
} | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.