< Summary

Information
Class: Renci.SshNet.Abstractions.SocketAbstraction
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Abstractions\SocketAbstraction.cs
Line coverage
71%
Covered lines: 131
Uncovered lines: 53
Coverable lines: 184
Total lines: 395
Line coverage: 71.1%
Branch coverage
65%
Covered branches: 39
Total branches: 60
Branch coverage: 65%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
CanRead(...)0%40%
CanWrite(...)100%4100%
Connect(...)100%1100%
Connect(...)100%1100%
ConnectAsync()100%1100%
ConnectCore(...)78.57%1482.85%
ClearReadBuffer(...)0%20%
ReadPartial(...)0%20%
ReadContinuous(...)70%1081.81%
ReadByte(...)100%2100%
SendByte(...)100%1100%
Read(...)100%1100%
Read(...)87.5%889.28%
ReadAsync(...)100%1100%
Send(...)100%1100%
Send(...)50%650%
IsErrorResumable(...)50%680%
ConnectCompleted(...)100%2100%

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Abstractions\SocketAbstraction.cs

#LineLine coverage
 1using System;
 2using System.Globalization;
 3using System.Net;
 4using System.Net.Sockets;
 5using System.Threading;
 6using System.Threading.Tasks;
 7
 8using Renci.SshNet.Common;
 9using Renci.SshNet.Messages.Transport;
 10
 11namespace Renci.SshNet.Abstractions
 12{
 13    internal static class SocketAbstraction
 14    {
 15        public static bool CanRead(Socket socket)
 016        {
 017            if (socket.Connected)
 018            {
 019                return socket.Poll(-1, SelectMode.SelectRead) && socket.Available > 0;
 20            }
 21
 022            return false;
 023        }
 24
 25        /// <summary>
 26        /// Returns a value indicating whether the specified <see cref="Socket"/> can be used
 27        /// to send data.
 28        /// </summary>
 29        /// <param name="socket">The <see cref="Socket"/> to check.</param>
 30        /// <returns>
 31        /// <see langword="true"/> if <paramref name="socket"/> can be written to; otherwise, <see langword="false"/>.
 32        /// </returns>
 33        public static bool CanWrite(Socket socket)
 5312634        {
 5312635            if (socket != null && socket.Connected)
 5304036            {
 5304037                return socket.Poll(-1, SelectMode.SelectWrite);
 38            }
 39
 8640            return false;
 5312641        }
 42
 43        public static Socket Connect(IPEndPoint remoteEndpoint, TimeSpan connectTimeout)
 2144        {
 2145            var socket = new Socket(remoteEndpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp) { NoDelay = true 
 2146            ConnectCore(socket, remoteEndpoint, connectTimeout, ownsSocket: true);
 2147            return socket;
 2148        }
 49
 50        public static void Connect(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout)
 226251        {
 226252            ConnectCore(socket, remoteEndpoint, connectTimeout, ownsSocket: false);
 217853        }
 54
 55        public static async Task ConnectAsync(Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationTo
 256        {
 257            await socket.ConnectAsync(remoteEndpoint, cancellationToken).ConfigureAwait(false);
 258        }
 59
 60        private static void ConnectCore(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout, bool ownsSock
 228361        {
 62#if FEATURE_SOCKET_EAP
 228363            var connectCompleted = new ManualResetEvent(initialState: false);
 228364            var args = new SocketAsyncEventArgs
 228365                {
 228366                    UserToken = connectCompleted,
 228367                    RemoteEndPoint = remoteEndpoint
 228368                };
 228369            args.Completed += ConnectCompleted;
 70
 228371            if (socket.ConnectAsync(args))
 210272            {
 210273                if (!connectCompleted.WaitOne(connectTimeout))
 2474                {
 75                    // avoid ObjectDisposedException in ConnectCompleted
 2476                    args.Completed -= ConnectCompleted;
 2477                    if (ownsSocket)
 078                    {
 79                        // dispose Socket
 080                        socket.Dispose();
 081                    }
 82
 83                    // dispose ManualResetEvent
 2484                    connectCompleted.Dispose();
 85
 86                    // dispose SocketAsyncEventArgs
 2487                    args.Dispose();
 88
 2489                    throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
 2490                                                                         "Connection failed to establish within {0:F0} m
 2491                                                                         connectTimeout.TotalMilliseconds));
 92                }
 207893            }
 94
 95            // dispose ManualResetEvent
 225996            connectCompleted.Dispose();
 97
 225998            if (args.SocketError != SocketError.Success)
 6099            {
 60100                var socketError = (int) args.SocketError;
 101
 60102                if (ownsSocket)
 0103                {
 104                    // dispose Socket
 0105                    socket.Dispose();
 0106                }
 107
 108                // dispose SocketAsyncEventArgs
 60109                args.Dispose();
 110
 60111                throw new SocketException(socketError);
 112            }
 113
 114            // dispose SocketAsyncEventArgs
 2199115            args.Dispose();
 116#elif FEATURE_SOCKET_APM
 117            var connectResult = socket.BeginConnect(remoteEndpoint, null, null);
 118            if (!connectResult.AsyncWaitHandle.WaitOne(connectTimeout, false))
 119                throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
 120                    "Connection failed to establish within {0:F0} milliseconds.", connectTimeout.TotalMilliseconds));
 121            socket.EndConnect(connectResult);
 122#elif FEATURE_SOCKET_TAP
 123            if (!socket.ConnectAsync(remoteEndpoint).Wait(connectTimeout))
 124                throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
 125                    "Connection failed to establish within {0:F0} milliseconds.", connectTimeout.TotalMilliseconds));
 126#else
 127            #error Connecting to a remote endpoint is not implemented.
 128#endif
 2199129        }
 130
 131        public static void ClearReadBuffer(Socket socket)
 0132        {
 0133            var timeout = TimeSpan.FromMilliseconds(500);
 0134            var buffer = new byte[256];
 135            int bytesReceived;
 136
 137            do
 0138            {
 0139                bytesReceived = ReadPartial(socket, buffer, 0, buffer.Length, timeout);
 0140            }
 0141            while (bytesReceived > 0);
 0142        }
 143
 144        public static int ReadPartial(Socket socket, byte[] buffer, int offset, int size, TimeSpan timeout)
 0145        {
 0146            socket.ReceiveTimeout = (int) timeout.TotalMilliseconds;
 147
 148            try
 0149            {
 0150                return socket.Receive(buffer, offset, size, SocketFlags.None);
 151            }
 0152            catch (SocketException ex)
 0153            {
 0154                if (ex.SocketErrorCode == SocketError.TimedOut)
 0155                {
 0156                    throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
 0157                                                                         "Socket read operation has timed out after {0:F
 0158                                                                         timeout.TotalMilliseconds));
 159                }
 160
 0161                throw;
 162            }
 0163        }
 164
 165        public static void ReadContinuous(Socket socket, byte[] buffer, int offset, int size, Action<byte[], int, int> p
 48166        {
 167            // do not time-out receive
 48168            socket.ReceiveTimeout = 0;
 169
 56170            while (socket.Connected)
 56171            {
 172                try
 56173                {
 56174                    var bytesRead = socket.Receive(buffer, offset, size, SocketFlags.None);
 48175                    if (bytesRead == 0)
 40176                    {
 40177                        break;
 178                    }
 179
 8180                    processReceivedBytesAction(buffer, offset, bytesRead);
 8181                }
 8182                catch (SocketException ex)
 8183                {
 8184                    if (IsErrorResumable(ex.SocketErrorCode))
 0185                    {
 0186                        continue;
 187                    }
 188
 189#pragma warning disable IDE0010 // Add missing cases
 8190                    switch (ex.SocketErrorCode)
 191                    {
 192                        case SocketError.ConnectionAborted:
 193                        case SocketError.ConnectionReset:
 194                            // connection was closed
 8195                            return;
 196                        case SocketError.Interrupted:
 197                            // connection was closed because FIN/ACK was not received in time after
 198                            // shutting down the (send part of the) socket
 0199                            return;
 200                        default:
 0201                            throw; // throw any other error
 202                    }
 203#pragma warning restore IDE0010 // Add missing cases
 204                }
 8205            }
 48206        }
 207
 208        /// <summary>
 209        /// Reads a byte from the specified <see cref="Socket"/>.
 210        /// </summary>
 211        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
 212        /// <param name="timeout">Specifies the amount of time after which the call will time out.</param>
 213        /// <returns>
 214        /// The byte read, or <c>-1</c> if the socket was closed.
 215        /// </returns>
 216        /// <exception cref="SshOperationTimeoutException">The read operation timed out.</exception>
 217        /// <exception cref="SocketException">The read failed.</exception>
 218        public static int ReadByte(Socket socket, TimeSpan timeout)
 249219        {
 249220            var buffer = new byte[1];
 249221            if (Read(socket, buffer, 0, 1, timeout) == 0)
 22222            {
 22223                return -1;
 224            }
 225
 195226            return buffer[0];
 217227        }
 228
 229        /// <summary>
 230        /// Sends a byte using the specified <see cref="Socket"/>.
 231        /// </summary>
 232        /// <param name="socket">The <see cref="Socket"/> to write to.</param>
 233        /// <param name="value">The value to send.</param>
 234        /// <exception cref="SocketException">The write failed.</exception>
 235        public static void SendByte(Socket socket, byte value)
 150236        {
 150237            var buffer = new[] { value };
 150238            Send(socket, buffer, 0, 1);
 150239        }
 240
 241        /// <summary>
 242        /// Receives data from a bound <see cref="Socket"/>.
 243        /// </summary>
 244        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
 245        /// <param name="size">The number of bytes to receive.</param>
 246        /// <param name="timeout">Specifies the amount of time after which the call will time out.</param>
 247        /// <returns>
 248        /// The bytes received.
 249        /// </returns>
 250        /// <remarks>
 251        /// If no data is available for reading, the <see cref="Read(Socket, int, TimeSpan)"/> method will
 252        /// block until data is available or the time-out value is exceeded. If the time-out value is exceeded, the
 253        /// <see cref="Read(Socket, int, TimeSpan)"/> call will throw a <see cref="SshOperationTimeoutException"/>.
 254        ///  If you are in non-blocking mode, and there is no data available in the in the protocol stack buffer, the
 255        /// <see cref="Read(Socket, int, TimeSpan)"/> method will complete immediately and throw a <see cref="SocketExce
 256        /// </remarks>
 257        public static byte[] Read(Socket socket, int size, TimeSpan timeout)
 30258        {
 30259            var buffer = new byte[size];
 30260            _ = Read(socket, buffer, 0, size, timeout);
 30261            return buffer;
 30262        }
 263
 264        /// <summary>
 265        /// Receives data from a bound <see cref="Socket"/> into a receive buffer.
 266        /// </summary>
 267        /// <param name="socket">The <see cref="Socket"/> to read from.</param>
 268        /// <param name="buffer">An array of type <see cref="byte"/> that is the storage location for the received data.
 269        /// <param name="offset">The position in <paramref name="buffer"/> parameter to store the received data.</param>
 270        /// <param name="size">The number of bytes to receive.</param>
 271        /// <param name="readTimeout">The maximum time to wait until <paramref name="size"/> bytes have been received.</
 272        /// <returns>
 273        /// The number of bytes received.
 274        /// </returns>
 275        /// <remarks>
 276        /// <para>
 277        /// If no data is available for reading, the <see cref="Read(Socket, byte[], int, int, TimeSpan)"/> method will
 278        /// block until data is available or the time-out value is exceeded. If the time-out value is exceeded, the
 279        /// <see cref="Read(Socket, byte[], int, int, TimeSpan)"/> call will throw a <see cref="SshOperationTimeoutExcep
 280        /// </para>
 281        /// <para>
 282        /// If you are in non-blocking mode, and there is no data available in the in the protocol stack buffer, the
 283        /// <see cref="Read(Socket, byte[], int, int, TimeSpan)"/> method will complete immediately and throw a <see cre
 284        /// </para>
 285        /// </remarks>
 286        public static int Read(Socket socket, byte[] buffer, int offset, int size, TimeSpan readTimeout)
 156307287        {
 156307288            var totalBytesRead = 0;
 156307289            var totalBytesToRead = size;
 290
 156307291            socket.ReceiveTimeout = (int) readTimeout.TotalMilliseconds;
 292
 293            do
 156599294            {
 295                try
 156599296                {
 156599297                    var bytesRead = socket.Receive(buffer, offset + totalBytesRead, totalBytesToRead - totalBytesRead, S
 156480298                    if (bytesRead == 0)
 109299                    {
 109300                        return 0;
 301                    }
 302
 156371303                    totalBytesRead += bytesRead;
 156371304                }
 119305                catch (SocketException ex)
 119306                {
 119307                    if (IsErrorResumable(ex.SocketErrorCode))
 0308                    {
 0309                        ThreadAbstraction.Sleep(30);
 0310                        continue;
 311                    }
 312
 119313                    if (ex.SocketErrorCode == SocketError.TimedOut)
 87314                    {
 87315                        throw new SshOperationTimeoutException(string.Format(CultureInfo.InvariantCulture,
 87316                                                               "Socket read operation has timed out after {0:F0} millise
 87317                                                               readTimeout.TotalMilliseconds));
 318                    }
 319
 32320                    throw;
 321                }
 156371322            }
 156371323            while (totalBytesRead < totalBytesToRead);
 324
 156079325            return totalBytesRead;
 156188326        }
 327
 328        public static Task<int> ReadAsync(Socket socket, byte[] buffer, int offset, int length, CancellationToken cancel
 42329        {
 42330            return socket.ReceiveAsync(buffer, offset, length, cancellationToken);
 42331        }
 332
 333        public static void Send(Socket socket, byte[] data)
 2070334        {
 2070335            Send(socket, data, 0, data.Length);
 2070336        }
 337
 338        public static void Send(Socket socket, byte[] data, int offset, int size)
 55386339        {
 55386340            var totalBytesSent = 0;  // how many bytes are already sent
 55386341            var totalBytesToSend = size;
 342
 343            do
 55386344            {
 345                try
 55386346                {
 55386347                    var bytesSent = socket.Send(data, offset + totalBytesSent, totalBytesToSend - totalBytesSent, Socket
 55386348                    if (bytesSent == 0)
 0349                    {
 0350                        throw new SshConnectionException("An established connection was aborted by the server.",
 0351                                                         DisconnectReason.ConnectionLost);
 352                    }
 353
 55386354                    totalBytesSent += bytesSent;
 55386355                }
 0356                catch (SocketException ex)
 0357                {
 0358                    if (IsErrorResumable(ex.SocketErrorCode))
 0359                    {
 360                        // socket buffer is probably full, wait and try again
 0361                        ThreadAbstraction.Sleep(30);
 0362                    }
 363                    else
 0364                    {
 0365                        throw; // any serious error occurr
 366                    }
 0367                }
 55386368            }
 55386369            while (totalBytesSent < totalBytesToSend);
 55386370        }
 371
 372        public static bool IsErrorResumable(SocketError socketError)
 127373        {
 374#pragma warning disable IDE0010 // Add missing cases
 127375            switch (socketError)
 376            {
 377                case SocketError.WouldBlock:
 378                case SocketError.IOPending:
 379                case SocketError.NoBufferSpaceAvailable:
 0380                    return true;
 381                default:
 127382                    return false;
 383            }
 384#pragma warning restore IDE0010 // Add missing cases
 127385        }
 386
 387#if FEATURE_SOCKET_EAP
 388        private static void ConnectCompleted(object sender, SocketAsyncEventArgs e)
 2078389        {
 2078390            var eventWaitHandle = (ManualResetEvent) e.UserToken;
 2078391            _ = eventWaitHandle?.Set();
 2078392        }
 393#endif // FEATURE_SOCKET_EAP
 394    }
 395}