Skip to content

Commit dfa72c3

Browse files
committed
Remove "IsErrorResumable" and SocketAbstraction.Send{Async}
Some methods in SocketAbstraction have code to retry a socket operation if it returns certain error codes. However AFAIK, these errors are only pertinent to nonblocking sockets, which we do not use. For blocking sockets, Socket.Send{Async} only returns when all of the bytes are sent. There is no need for a loop. These changes combined mean there is no need for Send methods in SocketAbstraction.
1 parent 983792a commit dfa72c3

File tree

11 files changed

+30
-138
lines changed

11 files changed

+30
-138
lines changed
Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#if NET6_0_OR_GREATER
22

3-
using System;
4-
using System.Diagnostics;
53
using System.Net.Sockets;
64
using System.Threading;
75
using System.Threading.Tasks;
@@ -14,37 +12,6 @@ public static ValueTask<int> ReadAsync(Socket socket, byte[] buffer, Cancellatio
1412
{
1513
return socket.ReceiveAsync(buffer, SocketFlags.None, cancellationToken);
1614
}
17-
18-
public static ValueTask SendAsync(Socket socket, ReadOnlyMemory<byte> data, CancellationToken cancellationToken = default)
19-
{
20-
Debug.Assert(socket != null);
21-
Debug.Assert(data.Length > 0);
22-
23-
if (cancellationToken.IsCancellationRequested)
24-
{
25-
return ValueTask.FromCanceled(cancellationToken);
26-
}
27-
28-
return SendAsyncCore(socket, data, cancellationToken);
29-
30-
static async ValueTask SendAsyncCore(Socket socket, ReadOnlyMemory<byte> data, CancellationToken cancellationToken)
31-
{
32-
do
33-
{
34-
try
35-
{
36-
var bytesSent = await socket.SendAsync(data, SocketFlags.None, cancellationToken).ConfigureAwait(false);
37-
data = data.Slice(bytesSent);
38-
}
39-
catch (SocketException ex) when (IsErrorResumable(ex.SocketErrorCode))
40-
{
41-
// Buffer may be full; attempt a short delay and retry
42-
await Task.Delay(30, cancellationToken).ConfigureAwait(false);
43-
}
44-
}
45-
while (data.Length > 0);
46-
}
47-
}
4815
}
4916
}
5017
#endif // NET6_0_OR_GREATER

src/Renci.SshNet/Abstractions/SocketAbstraction.cs

Lines changed: 6 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Threading.Tasks;
77

88
using Renci.SshNet.Common;
9-
using Renci.SshNet.Messages.Transport;
109

1110
namespace Renci.SshNet.Abstractions
1211
{
@@ -167,11 +166,6 @@ public static void ReadContinuous(Socket socket, byte[] buffer, int offset, int
167166
}
168167
catch (SocketException ex)
169168
{
170-
if (IsErrorResumable(ex.SocketErrorCode))
171-
{
172-
continue;
173-
}
174-
175169
#pragma warning disable IDE0010 // Add missing cases
176170
switch (ex.SocketErrorCode)
177171
{
@@ -221,7 +215,7 @@ public static int ReadByte(Socket socket, TimeSpan timeout)
221215
public static void SendByte(Socket socket, byte value)
222216
{
223217
var buffer = new[] { value };
224-
Send(socket, buffer, 0, 1);
218+
_ = socket.Send(buffer);
225219
}
226220

227221
/// <summary>
@@ -288,22 +282,12 @@ public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeS
288282

289283
totalBytesRead += bytesRead;
290284
}
291-
catch (SocketException ex)
285+
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut)
292286
{
293-
if (IsErrorResumable(ex.SocketErrorCode))
294-
{
295-
ThreadAbstraction.Sleep(30);
296-
continue;
297-
}
298-
299-
if (ex.SocketErrorCode == SocketError.TimedOut)
300-
{
301-
throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
302-
"Socket read operation has timed out after {0:F0} milliseconds.",
303-
readTimeout.TotalMilliseconds));
304-
}
305-
306-
throw;
287+
throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
288+
"Socket read operation has timed out after {0:F0} milliseconds.",
289+
readTimeout.TotalMilliseconds),
290+
ex);
307291
}
308292
}
309293
while (totalBytesRead < totalBytesToRead);
@@ -317,61 +301,6 @@ public static ValueTask<int> ReadAsync(Socket socket, byte[] buffer, Cancellatio
317301
return socket.ReceiveAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), SocketFlags.None, cancellationToken);
318302
}
319303
#endif
320-
321-
public static void Send(Socket socket, byte[] data)
322-
{
323-
Send(socket, data, 0, data.Length);
324-
}
325-
326-
public static void Send(Socket socket, byte[] data, int offset, int size)
327-
{
328-
var totalBytesSent = 0; // how many bytes are already sent
329-
var totalBytesToSend = size;
330-
331-
do
332-
{
333-
try
334-
{
335-
var bytesSent = socket.Send(data, offset + totalBytesSent, totalBytesToSend - totalBytesSent, SocketFlags.None);
336-
if (bytesSent == 0)
337-
{
338-
throw new SshConnectionException("An established connection was aborted by the server.",
339-
DisconnectReason.ConnectionLost);
340-
}
341-
342-
totalBytesSent += bytesSent;
343-
}
344-
catch (SocketException ex)
345-
{
346-
if (IsErrorResumable(ex.SocketErrorCode))
347-
{
348-
// socket buffer is probably full, wait and try again
349-
ThreadAbstraction.Sleep(30);
350-
}
351-
else
352-
{
353-
throw; // any serious error occurr
354-
}
355-
}
356-
}
357-
while (totalBytesSent < totalBytesToSend);
358-
}
359-
360-
public static bool IsErrorResumable(SocketError socketError)
361-
{
362-
#pragma warning disable IDE0010 // Add missing cases
363-
switch (socketError)
364-
{
365-
case SocketError.WouldBlock:
366-
case SocketError.IOPending:
367-
case SocketError.NoBufferSpaceAvailable:
368-
return true;
369-
default:
370-
return false;
371-
}
372-
#pragma warning restore IDE0010 // Add missing cases
373-
}
374-
375304
private static void ConnectCompleted(object sender, SocketAsyncEventArgs e)
376305
{
377306
var eventWaitHandle = (ManualResetEvent) e.UserToken;

src/Renci.SshNet/Channels/ChannelDirectTcpip.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ protected override void OnData(byte[] data)
201201
{
202202
if (_socket.IsConnected())
203203
{
204-
SocketAbstraction.Send(_socket, data, 0, data.Length);
204+
_ = _socket.Send(data);
205205
}
206206
}
207207
}

src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ protected override void OnData(byte[] data)
201201
var socket = _socket;
202202
if (socket.IsConnected())
203203
{
204-
SocketAbstraction.Send(socket, data, 0, data.Length);
204+
_ = socket.Send(data);
205205
}
206206
}
207207
}

src/Renci.SshNet/Connection/HttpConnector.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Globalization;
44
using System.Net;
55
using System.Net.Sockets;
6+
using System.Text;
67
using System.Text.RegularExpressions;
78

89
using Renci.SshNet.Abstractions;
@@ -41,7 +42,7 @@ protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socke
4142
var httpResponseRe = new Regex(@"HTTP/(?<version>\d[.]\d) (?<statusCode>\d{3}) (?<reasonPhrase>.+)$");
4243
var httpHeaderRe = new Regex(@"(?<fieldName>[^\[\]()<>@,;:\""/?={} \t]+):(?<fieldValue>.+)?");
4344

44-
SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(string.Format(CultureInfo.InvariantCulture,
45+
_ = socket.Send(Encoding.ASCII.GetBytes(string.Format(CultureInfo.InvariantCulture,
4546
"CONNECT {0}:{1} HTTP/1.0\r\n",
4647
connectionInfo.Host,
4748
connectionInfo.Port)));
@@ -51,11 +52,11 @@ protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socke
5152
{
5253
var authorization = string.Format(CultureInfo.InvariantCulture,
5354
"Proxy-Authorization: Basic {0}\r\n",
54-
Convert.ToBase64String(SshData.Ascii.GetBytes($"{connectionInfo.ProxyUsername}:{connectionInfo.ProxyPassword}")));
55-
SocketAbstraction.Send(socket, SshData.Ascii.GetBytes(authorization));
55+
Convert.ToBase64String(Encoding.ASCII.GetBytes($"{connectionInfo.ProxyUsername}:{connectionInfo.ProxyPassword}")));
56+
_ = socket.Send(Encoding.ASCII.GetBytes(authorization));
5657
}
5758

58-
SocketAbstraction.Send(socket, SshData.Ascii.GetBytes("\r\n"));
59+
_ = socket.Send(Encoding.ASCII.GetBytes("\r\n"));
5960

6061
HttpStatusCode? statusCode = null;
6162
var contentLength = 0;

src/Renci.SshNet/Connection/ProtocolVersionExchange.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public SshIdentification Start(string clientVersion, Socket socket, TimeSpan tim
3838
{
3939
// Immediately send the identification string since the spec states both sides MUST send an identification string
4040
// when the connection has been established
41-
SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A"));
41+
_ = socket.Send(Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A"));
4242

4343
var bytesReceived = new List<byte>();
4444

@@ -81,11 +81,7 @@ public async Task<SshIdentification> StartAsync(string clientVersion, Socket soc
8181
{
8282
// Immediately send the identification string since the spec states both sides MUST send an identification string
8383
// when the connection has been established
84-
#if NET6_0_OR_GREATER
85-
await SocketAbstraction.SendAsync(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A"), cancellationToken).ConfigureAwait(false);
86-
#else
87-
SocketAbstraction.Send(socket, Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A"));
88-
#endif // NET6_0_OR_GREATER
84+
_ = await socket.SendAsync(Encoding.UTF8.GetBytes(clientVersion + "\x0D\x0A"), SocketFlags.None, cancellationToken).ConfigureAwait(false);
8985

9086
var bytesReceived = new List<byte>();
9187

src/Renci.SshNet/Connection/Socks4Connector.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System.Net.Sockets;
44
using System.Text;
55

6-
using Renci.SshNet.Abstractions;
76
using Renci.SshNet.Common;
87

98
namespace Renci.SshNet.Connection
@@ -29,7 +28,7 @@ public Socks4Connector(ISocketFactory socketFactory)
2928
protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socket socket)
3029
{
3130
var connectionRequest = CreateSocks4ConnectionRequest(connectionInfo.Host, (ushort)connectionInfo.Port, connectionInfo.ProxyUsername);
32-
SocketAbstraction.Send(socket, connectionRequest);
31+
_ = socket.Send(connectionRequest);
3332

3433
// Read reply version
3534
if (SocketReadByte(socket, connectionInfo.Timeout) != 0x00)

src/Renci.SshNet/Connection/Socks5Connector.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socke
4141
// Username/Password authentication
4242
0x02
4343
};
44-
SocketAbstraction.Send(socket, greeting);
44+
_ = socket.Send(greeting);
4545

4646
var socksVersion = SocketReadByte(socket);
4747
if (socksVersion != 0x05)
@@ -60,7 +60,7 @@ protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socke
6060
var authenticationRequest = CreateSocks5UserNameAndPasswordAuthenticationRequest(connectionInfo.ProxyUsername, connectionInfo.ProxyPassword);
6161

6262
// Send authentication request
63-
SocketAbstraction.Send(socket, authenticationRequest);
63+
_ = socket.Send(authenticationRequest);
6464

6565
// Read authentication result
6666
var authenticationResult = SocketAbstraction.Read(socket, 2, connectionInfo.Timeout);
@@ -83,7 +83,7 @@ protected override void HandleProxyConnect(IConnectionInfo connectionInfo, Socke
8383
}
8484

8585
var connectionRequest = CreateSocks5ConnectionRequest(connectionInfo.Host, (ushort) connectionInfo.Port);
86-
SocketAbstraction.Send(socket, connectionRequest);
86+
_ = socket.Send(connectionRequest);
8787

8888
// Read Server SOCKS5 version
8989
if (SocketReadByte(socket) != 5)

src/Renci.SshNet/ForwardedPortDynamic.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -508,8 +508,8 @@ private bool HandleSocks4(Socket socket, IChannelDirectTcpip channel, TimeSpan t
508508
if (channel.IsOpen)
509509
{
510510
SocketAbstraction.SendByte(socket, 0x5a);
511-
SocketAbstraction.Send(socket, portBuffer, 0, portBuffer.Length);
512-
SocketAbstraction.Send(socket, ipBuffer, 0, ipBuffer.Length);
511+
_ = socket.Send(portBuffer);
512+
_ = socket.Send(ipBuffer);
513513
return true;
514514
}
515515

@@ -538,12 +538,12 @@ private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan t
538538
{
539539
// no user authentication is one of the authentication methods supported
540540
// by the SOCKS client
541-
SocketAbstraction.Send(socket, new byte[] { 0x05, 0x00 }, 0, 2);
541+
_ = socket.Send([0x05, 0x00]);
542542
}
543543
else
544544
{
545545
// the SOCKS client requires authentication, which we currently do not support
546-
SocketAbstraction.Send(socket, new byte[] { 0x05, 0xFF }, 0, 2);
546+
_ = socket.Send([0x05, 0xFF]);
547547

548548
// we continue business as usual but expect the client to close the connection
549549
// so one of the subsequent reads should return -1 signaling that the client
@@ -610,7 +610,7 @@ private bool HandleSocks5(Socket socket, IChannelDirectTcpip channel, TimeSpan t
610610

611611
var socksReply = CreateSocks5Reply(channel.IsOpen);
612612

613-
SocketAbstraction.Send(socket, socksReply, 0, socksReply.Length);
613+
_ = socket.Send(socksReply);
614614

615615
return true;
616616
}

src/Renci.SshNet/Session.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1160,7 +1160,7 @@ private void SendPacket(byte[] packet, int offset, int length)
11601160
throw new SshConnectionException("Client not connected.");
11611161
}
11621162

1163-
SocketAbstraction.Send(_socket, packet, offset, length);
1163+
_ = _socket.Send(packet, offset, length, SocketFlags.None);
11641164
}
11651165
finally
11661166
{

0 commit comments

Comments
 (0)