< Summary

Information
Class: Renci.SshNet.ShellStream
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\ShellStream.cs
Line coverage
45%
Covered lines: 168
Uncovered lines: 203
Coverable lines: 371
Total lines: 819
Line coverage: 45.2%
Branch coverage
39%
Covered branches: 43
Total branches: 108
Branch coverage: 39.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%4100%
get_DataAvailable()100%10%
get_BufferSize()100%1100%
get_CanRead()100%10%
get_CanSeek()100%10%
get_CanWrite()100%10%
Flush()100%2100%
get_Length()100%10%
get_Position()100%10%
set_Position(...)100%10%
Seek(...)100%10%
SetLength(...)100%10%
Expect(...)100%10%
Expect(...)0%200%
Expect(...)100%1100%
Expect(...)100%10%
Expect(...)100%10%
Expect(...)75%1283.87%
BeginExpect(...)100%10%
BeginExpect(...)100%10%
BeginExpect(...)100%10%
BeginExpect(...)0%220%
EndExpect(...)0%40%
ReadLine()100%1100%
ReadLine(...)70%1084.84%
Read()100%10%
Read(...)0%40%
Write(...)100%2100%
Write(...)100%4100%
WriteLine(...)100%1100%
Dispose(...)87.5%888.46%
UnsubscribeFromSessionEvents(...)50%271.42%
Session_ErrorOccured(...)100%10%
Session_Disconnected(...)0%20%
Channel_Closed(...)100%1100%
Channel_DataReceived(...)100%4100%
OnRaiseError(...)0%20%
OnDataReceived(...)50%2100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Text;
 5using System.Text.RegularExpressions;
 6using System.Threading;
 7
 8using Renci.SshNet.Abstractions;
 9using Renci.SshNet.Channels;
 10using Renci.SshNet.Common;
 11
 12namespace Renci.SshNet
 13{
 14    /// <summary>
 15    /// Contains operation for working with SSH Shell.
 16    /// </summary>
 17    public class ShellStream : Stream
 18    {
 19        private const string CrLf = "\r\n";
 20
 21        private readonly ISession _session;
 22        private readonly Encoding _encoding;
 23        private readonly int _bufferSize;
 24        private readonly Queue<byte> _incoming;
 25        private readonly Queue<byte> _outgoing;
 26        private IChannelSession _channel;
 12327        private AutoResetEvent _dataReceived = new AutoResetEvent(initialState: false);
 28        private bool _isDisposed;
 29
 30        /// <summary>
 31        /// Occurs when data was received.
 32        /// </summary>
 33        public event EventHandler<ShellDataEventArgs> DataReceived;
 34
 35        /// <summary>
 36        /// Occurs when an error occurred.
 37        /// </summary>
 38        public event EventHandler<ExceptionEventArgs> ErrorOccurred;
 39
 40        /// <summary>
 41        /// Gets a value indicating whether data is available on the <see cref="ShellStream"/> to be read.
 42        /// </summary>
 43        /// <value>
 44        /// <see langword="true"/> if data is available to be read; otherwise, <see langword="false"/>.
 45        /// </value>
 46        public bool DataAvailable
 47        {
 48            get
 049            {
 050                lock (_incoming)
 051                {
 052                    return _incoming.Count > 0;
 53                }
 054            }
 55        }
 56
 57        /// <summary>
 58        /// Gets the number of bytes that will be written to the internal buffer.
 59        /// </summary>
 60        /// <value>
 61        /// The number of bytes that will be written to the internal buffer.
 62        /// </value>
 63        internal int BufferSize
 64        {
 965            get { return _bufferSize; }
 66        }
 67
 68        /// <summary>
 69        /// Initializes a new instance of the <see cref="ShellStream"/> class.
 70        /// </summary>
 71        /// <param name="session">The SSH session.</param>
 72        /// <param name="terminalName">The <c>TERM</c> environment variable.</param>
 73        /// <param name="columns">The terminal width in columns.</param>
 74        /// <param name="rows">The terminal width in rows.</param>
 75        /// <param name="width">The terminal width in pixels.</param>
 76        /// <param name="height">The terminal height in pixels.</param>
 77        /// <param name="terminalModeValues">The terminal mode values.</param>
 78        /// <param name="bufferSize">The size of the buffer.</param>
 79        /// <exception cref="SshException">The channel could not be opened.</exception>
 80        /// <exception cref="SshException">The pseudo-terminal request was not accepted by the server.</exception>
 81        /// <exception cref="SshException">The request to start a shell was not accepted by the server.</exception>
 12382        internal ShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, ID
 12383        {
 12384            _encoding = session.ConnectionInfo.Encoding;
 12385            _session = session;
 12386            _bufferSize = bufferSize;
 12387            _incoming = new Queue<byte>();
 12388            _outgoing = new Queue<byte>();
 89
 12390            _channel = _session.CreateChannelSession();
 12391            _channel.DataReceived += Channel_DataReceived;
 12392            _channel.Closed += Channel_Closed;
 12393            _session.Disconnected += Session_Disconnected;
 12394            _session.ErrorOccured += Session_ErrorOccured;
 95
 96            try
 12397            {
 12398                _channel.Open();
 99
 117100                if (!_channel.SendPseudoTerminalRequest(terminalName, columns, rows, width, height, terminalModeValues))
 6101                {
 6102                    throw new SshException("The pseudo-terminal request was not accepted by the server. Consult the serv
 103                }
 104
 105105                if (!_channel.SendShellRequest())
 6106                {
 6107                    throw new SshException("The request to start a shell was not accepted by the server. Consult the ser
 108                }
 93109            }
 30110            catch
 30111            {
 30112                UnsubscribeFromSessionEvents(session);
 30113                _channel.Dispose();
 30114                throw;
 115            }
 93116        }
 117
 118        /// <summary>
 119        /// Gets a value indicating whether the current stream supports reading.
 120        /// </summary>
 121        /// <returns>
 122        /// <see langword="true"/> if the stream supports reading; otherwise, <see langword="false"/>.
 123        /// </returns>
 124        public override bool CanRead
 125        {
 0126            get { return true; }
 127        }
 128
 129        /// <summary>
 130        /// Gets a value indicating whether the current stream supports seeking.
 131        /// </summary>
 132        /// <returns>
 133        /// <see langword="true"/> if the stream supports seeking; otherwise, <see langword="false"/>.
 134        /// </returns>
 135        public override bool CanSeek
 136        {
 0137            get { return false; }
 138        }
 139
 140        /// <summary>
 141        /// Gets a value indicating whether the current stream supports writing.
 142        /// </summary>
 143        /// <returns>
 144        /// <see langword="true"/> if the stream supports writing; otherwise, <see langword="false"/>.
 145        /// </returns>
 146        public override bool CanWrite
 147        {
 0148            get { return true; }
 149        }
 150
 151        /// <summary>
 152        /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
 153        /// </summary>
 154        /// <exception cref="IOException">An I/O error occurs.</exception>
 155        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 156        public override void Flush()
 53157        {
 158#if NET7_0_OR_GREATER
 36159            ObjectDisposedException.ThrowIf(_channel is null, this);
 160#else
 17161            if (_channel is null)
 0162            {
 0163                throw new ObjectDisposedException(GetType().FullName);
 164            }
 165#endif // NET7_0_OR_GREATER
 166
 53167            if (_outgoing.Count > 0)
 48168            {
 48169                _channel.SendData(_outgoing.ToArray());
 48170                _outgoing.Clear();
 48171            }
 53172        }
 173
 174        /// <summary>
 175        /// Gets the length in bytes of the stream.
 176        /// </summary>
 177        /// <returns>A long value representing the length of the stream in bytes.</returns>
 178        /// <exception cref="NotSupportedException">A class derived from Stream does not support seeking.</exception>
 179        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 180        public override long Length
 181        {
 182            get
 0183            {
 0184                lock (_incoming)
 0185                {
 0186                    return _incoming.Count;
 187                }
 0188            }
 189        }
 190
 191        /// <summary>
 192        /// Gets or sets the position within the current stream.
 193        /// </summary>
 194        /// <returns>
 195        /// The current position within the stream.
 196        /// </returns>
 197        /// <exception cref="IOException">An I/O error occurs.</exception>
 198        /// <exception cref="NotSupportedException">The stream does not support seeking.</exception>
 199        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 200        public override long Position
 201        {
 0202            get { return 0; }
 0203            set { throw new NotSupportedException(); }
 204        }
 205
 206        /// <summary>
 207        /// This method is not supported.
 208        /// </summary>
 209        /// <param name="offset">A byte offset relative to the <paramref name="origin"/> parameter.</param>
 210        /// <param name="origin">A value of type <see cref="SeekOrigin"/> indicating the reference point used to obtain 
 211        /// <returns>
 212        /// The new position within the current stream.
 213        /// </returns>
 214        /// <exception cref="IOException">An I/O error occurs.</exception>
 215        /// <exception cref="NotSupportedException">The stream does not support seeking, such as if the stream is constr
 216        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 217        public override long Seek(long offset, SeekOrigin origin)
 0218        {
 0219            throw new NotSupportedException();
 220        }
 221
 222        /// <summary>
 223        /// This method is not supported.
 224        /// </summary>
 225        /// <param name="value">The desired length of the current stream in bytes.</param>
 226        /// <exception cref="IOException">An I/O error occurs.</exception>
 227        /// <exception cref="NotSupportedException">The stream does not support both writing and seeking, such as if the
 228        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 229        public override void SetLength(long value)
 0230        {
 0231            throw new NotSupportedException();
 232        }
 233
 234        /// <summary>
 235        /// Expects the specified expression and performs action when one is found.
 236        /// </summary>
 237        /// <param name="expectActions">The expected expressions and actions to perform.</param>
 238        public void Expect(params ExpectAction[] expectActions)
 0239        {
 0240            Expect(TimeSpan.Zero, expectActions);
 0241        }
 242
 243        /// <summary>
 244        /// Expects the specified expression and performs action when one is found.
 245        /// </summary>
 246        /// <param name="timeout">Time to wait for input.</param>
 247        /// <param name="expectActions">The expected expressions and actions to perform, if the specified time elapsed a
 248        public void Expect(TimeSpan timeout, params ExpectAction[] expectActions)
 0249        {
 0250            var expectedFound = false;
 0251            var text = string.Empty;
 252
 253            do
 0254            {
 0255                lock (_incoming)
 0256                {
 0257                    if (_incoming.Count > 0)
 0258                    {
 0259                        text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
 0260                    }
 261
 0262                    if (text.Length > 0)
 0263                    {
 0264                        foreach (var expectAction in expectActions)
 0265                        {
 0266                            var match = expectAction.Expect.Match(text);
 267
 0268                            if (match.Success)
 0269                            {
 0270                                var result = text.Substring(0, match.Index + match.Length);
 271
 0272                                for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++)
 0273                                {
 274                                    // Remove processed items from the queue
 0275                                    _ = _incoming.Dequeue();
 0276                                }
 277
 0278                                expectAction.Action(result);
 0279                                expectedFound = true;
 0280                            }
 0281                        }
 0282                    }
 0283                }
 284
 0285                if (!expectedFound)
 0286                {
 0287                    if (timeout.Ticks > 0)
 0288                    {
 0289                        if (!_dataReceived.WaitOne(timeout))
 0290                        {
 0291                            return;
 292                        }
 0293                    }
 294                    else
 0295                    {
 0296                        _ = _dataReceived.WaitOne();
 0297                    }
 0298                }
 0299            }
 0300            while (!expectedFound);
 0301        }
 302
 303        /// <summary>
 304        /// Expects the expression specified by text.
 305        /// </summary>
 306        /// <param name="text">The text to expect.</param>
 307        /// <returns>
 308        /// Text available in the shell that ends with expected text.
 309        /// </returns>
 310        public string Expect(string text)
 4311        {
 4312            return Expect(new Regex(Regex.Escape(text)), Session.InfiniteTimeSpan);
 4313        }
 314
 315        /// <summary>
 316        /// Expects the expression specified by text.
 317        /// </summary>
 318        /// <param name="text">The text to expect.</param>
 319        /// <param name="timeout">Time to wait for input.</param>
 320        /// <returns>
 321        /// The text available in the shell that ends with expected text, or <see langword="null"/> if the specified tim
 322        /// </returns>
 323        public string Expect(string text, TimeSpan timeout)
 0324        {
 0325            return Expect(new Regex(Regex.Escape(text)), timeout);
 0326        }
 327
 328        /// <summary>
 329        /// Expects the expression specified by regular expression.
 330        /// </summary>
 331        /// <param name="regex">The regular expression to expect.</param>
 332        /// <returns>
 333        /// The text available in the shell that contains all the text that ends with expected expression.
 334        /// </returns>
 335        public string Expect(Regex regex)
 0336        {
 0337            return Expect(regex, TimeSpan.Zero);
 0338        }
 339
 340        /// <summary>
 341        /// Expects the expression specified by regular expression.
 342        /// </summary>
 343        /// <param name="regex">The regular expression to expect.</param>
 344        /// <param name="timeout">Time to wait for input.</param>
 345        /// <returns>
 346        /// The text available in the shell that contains all the text that ends with expected expression,
 347        /// or <see langword="null"/> if the specified time has elapsed.
 348        /// </returns>
 349        public string Expect(Regex regex, TimeSpan timeout)
 4350        {
 4351            var text = string.Empty;
 352
 16353            while (true)
 16354            {
 16355                lock (_incoming)
 16356                {
 16357                    if (_incoming.Count > 0)
 13358                    {
 13359                        text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
 13360                    }
 361
 16362                    var match = regex.Match(text);
 363
 16364                    if (match.Success)
 4365                    {
 366                        // Remove processed items from the queue
 412367                        for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++)
 202368                        {
 202369                            _ = _incoming.Dequeue();
 202370                        }
 371
 4372                        break;
 373                    }
 12374                }
 375
 12376                if (timeout.Ticks > 0)
 0377                {
 0378                    if (!_dataReceived.WaitOne(timeout))
 0379                    {
 0380                        return null;
 381                    }
 0382                }
 383                else
 12384                {
 12385                    _ = _dataReceived.WaitOne();
 12386                }
 12387            }
 388
 4389            return text;
 4390        }
 391
 392        /// <summary>
 393        /// Begins the expect.
 394        /// </summary>
 395        /// <param name="expectActions">The expect actions.</param>
 396        /// <returns>
 397        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
 398        /// </returns>
 399        public IAsyncResult BeginExpect(params ExpectAction[] expectActions)
 0400        {
 0401            return BeginExpect(TimeSpan.Zero, callback: null, state: null, expectActions);
 0402        }
 403
 404        /// <summary>
 405        /// Begins the expect.
 406        /// </summary>
 407        /// <param name="callback">The callback.</param>
 408        /// <param name="expectActions">The expect actions.</param>
 409        /// <returns>
 410        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
 411        /// </returns>
 412        public IAsyncResult BeginExpect(AsyncCallback callback, params ExpectAction[] expectActions)
 0413        {
 0414            return BeginExpect(TimeSpan.Zero, callback, state: null, expectActions);
 0415        }
 416
 417        /// <summary>
 418        /// Begins the expect.
 419        /// </summary>
 420        /// <param name="callback">The callback.</param>
 421        /// <param name="state">The state.</param>
 422        /// <param name="expectActions">The expect actions.</param>
 423        /// <returns>
 424        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
 425        /// </returns>
 426        public IAsyncResult BeginExpect(AsyncCallback callback, object state, params ExpectAction[] expectActions)
 0427        {
 0428            return BeginExpect(TimeSpan.Zero, callback, state, expectActions);
 0429        }
 430
 431        /// <summary>
 432        /// Begins the expect.
 433        /// </summary>
 434        /// <param name="timeout">The timeout.</param>
 435        /// <param name="callback">The callback.</param>
 436        /// <param name="state">The state.</param>
 437        /// <param name="expectActions">The expect actions.</param>
 438        /// <returns>
 439        /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
 440        /// </returns>
 441#pragma warning disable CA1859 // Use concrete types when possible for improved performance
 442        public IAsyncResult BeginExpect(TimeSpan timeout, AsyncCallback callback, object state, params ExpectAction[] ex
 443#pragma warning restore CA1859 // Use concrete types when possible for improved performance
 0444        {
 0445            var text = string.Empty;
 446
 447            // Create new AsyncResult object
 0448            var asyncResult = new ExpectAsyncResult(callback, state);
 449
 450            // Execute callback on different thread
 0451            ThreadAbstraction.ExecuteThread(() =>
 0452            {
 0453                string expectActionResult = null;
 0454                try
 0455                {
 0456                    do
 0457                    {
 0458                        lock (_incoming)
 0459                        {
 0460                            if (_incoming.Count > 0)
 0461                            {
 0462                                text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
 0463                            }
 0464
 0465                            if (text.Length > 0)
 0466                            {
 0467                                foreach (var expectAction in expectActions)
 0468                                {
 0469                                    var match = expectAction.Expect.Match(text);
 0470
 0471                                    if (match.Success)
 0472                                    {
 0473                                        var result = text.Substring(0, match.Index + match.Length);
 0474
 0475                                        for (var i = 0; i < match.Index + match.Length && _incoming.Count > 0; i++)
 0476                                        {
 0477                                            // Remove processed items from the queue
 0478                                            _ = _incoming.Dequeue();
 0479                                        }
 0480
 0481                                        expectAction.Action(result);
 0482                                        callback?.Invoke(asyncResult);
 0483                                        expectActionResult = result;
 0484                                    }
 0485                                }
 0486                            }
 0487                        }
 0488
 0489                        if (expectActionResult != null)
 0490                        {
 0491                            break;
 0492                        }
 0493
 0494                        if (timeout.Ticks > 0)
 0495                        {
 0496                            if (!_dataReceived.WaitOne(timeout))
 0497                            {
 0498                                callback?.Invoke(asyncResult);
 0499                                break;
 0500                            }
 0501                        }
 0502                        else
 0503                        {
 0504                            _ = _dataReceived.WaitOne();
 0505                        }
 0506                    }
 0507                    while (true);
 0508
 0509                    asyncResult.SetAsCompleted(expectActionResult, completedSynchronously: true);
 0510                }
 0511                catch (Exception exp)
 0512                {
 0513                    asyncResult.SetAsCompleted(exp, completedSynchronously: true);
 0514                }
 0515            });
 516
 0517            return asyncResult;
 0518        }
 519
 520        /// <summary>
 521        /// Ends the execute.
 522        /// </summary>
 523        /// <param name="asyncResult">The async result.</param>
 524        /// <returns>
 525        /// Text available in the shell that ends with expected text.
 526        /// </returns>
 527        /// <exception cref="ArgumentException">Either the IAsyncResult object did not come from the corresponding async
 528        public string EndExpect(IAsyncResult asyncResult)
 0529        {
 0530            if (asyncResult is not ExpectAsyncResult ar || ar.EndInvokeCalled)
 0531            {
 0532                throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async me
 533            }
 534
 535            // Wait for operation to complete, then return result or throw exception
 0536            return ar.EndInvoke();
 0537        }
 538
 539        /// <summary>
 540        /// Reads the line from the shell. If line is not available it will block the execution and will wait for new li
 541        /// </summary>
 542        /// <returns>
 543        /// The line read from the shell.
 544        /// </returns>
 545        public string ReadLine()
 11546        {
 11547            return ReadLine(TimeSpan.Zero);
 10548        }
 549
 550        /// <summary>
 551        /// Reads a line from the shell. If line is not available it will block the execution and will wait for new line
 552        /// </summary>
 553        /// <param name="timeout">Time to wait for input.</param>
 554        /// <returns>
 555        /// The line read from the shell, or <see langword="null"/> when no input is received for the specified timeout.
 556        /// </returns>
 557        public string ReadLine(TimeSpan timeout)
 11558        {
 11559            var text = string.Empty;
 560
 21561            while (true)
 21562            {
 21563                lock (_incoming)
 21564                {
 21565                    if (_incoming.Count > 0)
 14566                    {
 14567                        text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
 14568                    }
 569
 21570                    var index = text.IndexOf(CrLf, StringComparison.Ordinal);
 571
 21572                    if (index >= 0)
 10573                    {
 10574                        text = text.Substring(0, index);
 575
 576                        // determine how many bytes to remove from buffer
 10577                        var bytesProcessed = _encoding.GetByteCount(text + CrLf);
 578
 579                        // remove processed bytes from the queue
 532580                        for (var i = 0; i < bytesProcessed; i++)
 256581                        {
 256582                            _ = _incoming.Dequeue();
 256583                        }
 584
 10585                        break;
 586                    }
 11587                }
 588
 11589                if (timeout.Ticks > 0)
 0590                {
 0591                    if (!_dataReceived.WaitOne(timeout))
 0592                    {
 0593                        return null;
 594                    }
 0595                }
 596                else
 11597                {
 11598                    _ = _dataReceived.WaitOne();
 10599                }
 10600            }
 601
 10602            return text;
 10603        }
 604
 605        /// <summary>
 606        /// Reads text available in the shell.
 607        /// </summary>
 608        /// <returns>
 609        /// The text available in the shell.
 610        /// </returns>
 611        public string Read()
 0612        {
 613            string text;
 614
 0615            lock (_incoming)
 0616            {
 0617                text = _encoding.GetString(_incoming.ToArray(), 0, _incoming.Count);
 0618                _incoming.Clear();
 0619            }
 620
 0621            return text;
 0622        }
 623
 624        /// <summary>
 625        /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number 
 626        /// </summary>
 627        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte arr
 628        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the d
 629        /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
 630        /// <returns>
 631        /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that 
 632        /// </returns>
 633        /// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is lar
 634        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
 635        /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negat
 636        /// <exception cref="IOException">An I/O error occurs.</exception>
 637        /// <exception cref="NotSupportedException">The stream does not support reading.</exception>
 638        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 639        public override int Read(byte[] buffer, int offset, int count)
 0640        {
 0641            var i = 0;
 642
 0643            lock (_incoming)
 0644            {
 0645                for (; i < count && _incoming.Count > 0; i++)
 0646                {
 0647                    buffer[offset + i] = _incoming.Dequeue();
 0648                }
 0649            }
 650
 0651            return i;
 0652        }
 653
 654        /// <summary>
 655        /// Writes the specified text to the shell.
 656        /// </summary>
 657        /// <param name="text">The text to be written to the shell.</param>
 658        /// <remarks>
 659        /// If <paramref name="text"/> is <see langword="null"/>, nothing is written.
 660        /// </remarks>
 661        public void Write(string text)
 16662        {
 16663            if (text is null)
 3664            {
 3665                return;
 666            }
 667
 668#if NET7_0_OR_GREATER
 12669            ObjectDisposedException.ThrowIf(_channel is null, this);
 670#else
 1671            if (_channel is null)
 0672            {
 0673                throw new ObjectDisposedException(GetType().FullName);
 674            }
 675#endif // NET7_0_OR_GREATER
 676
 12677            var data = _encoding.GetBytes(text);
 12678            _channel.SendData(data);
 15679        }
 680
 681        /// <summary>
 682        /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the
 683        /// </summary>
 684        /// <param name="buffer">An array of bytes. This method copies <paramref name="count"/> bytes from <paramref nam
 685        /// <param name="offset">The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes
 686        /// <param name="count">The number of bytes to be written to the current stream.</param>
 687        /// <exception cref="ArgumentException">The sum of <paramref name="offset"/> and <paramref name="count"/> is gre
 688        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is <see langword="null"/>.</exception>
 689        /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> or <paramref name="count"/> is negat
 690        /// <exception cref="IOException">An I/O error occurs.</exception>
 691        /// <exception cref="NotSupportedException">The stream does not support writing.</exception>
 692        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
 693        public override void Write(byte[] buffer, int offset, int count)
 84694        {
 76414695            foreach (var b in buffer.Take(offset, count))
 38081696            {
 38081697                if (_outgoing.Count == _bufferSize)
 24698                {
 24699                    Flush();
 24700                }
 701
 38081702                _outgoing.Enqueue(b);
 38081703            }
 84704        }
 705
 706        /// <summary>
 707        /// Writes the line to the shell.
 708        /// </summary>
 709        /// <param name="line">The line to be written to the shell.</param>
 710        /// <remarks>
 711        /// If <paramref name="line"/> is <see langword="null"/>, only the line terminator is written.
 712        /// </remarks>
 713        public void WriteLine(string line)
 12714        {
 12715            Write(line + "\r");
 12716        }
 717
 718        /// <summary>
 719        /// Releases the unmanaged resources used by the <see cref="Stream"/> and optionally releases the managed resour
 720        /// </summary>
 721        /// <param name="disposing"><see langword="true"/> to release both managed and unmanaged resources; <see langwor
 722        protected override void Dispose(bool disposing)
 9723        {
 9724            base.Dispose(disposing);
 725
 9726            if (_isDisposed)
 3727            {
 3728                return;
 729            }
 730
 6731            if (disposing)
 6732            {
 6733                UnsubscribeFromSessionEvents(_session);
 734
 6735                if (_channel != null)
 6736                {
 6737                    _channel.DataReceived -= Channel_DataReceived;
 6738                    _channel.Closed -= Channel_Closed;
 6739                    _channel.Dispose();
 6740                    _channel = null;
 6741                }
 742
 6743                if (_dataReceived != null)
 6744                {
 6745                    _dataReceived.Dispose();
 6746                    _dataReceived = null;
 6747                }
 748
 6749                _isDisposed = true;
 6750            }
 751            else
 0752            {
 0753                UnsubscribeFromSessionEvents(_session);
 0754            }
 9755        }
 756
 757        /// <summary>
 758        /// Unsubscribes the current <see cref="ShellStream"/> from session events.
 759        /// </summary>
 760        /// <param name="session">The session.</param>
 761        /// <remarks>
 762        /// Does nothing when <paramref name="session"/> is <see langword="null"/>.
 763        /// </remarks>
 764        private void UnsubscribeFromSessionEvents(ISession session)
 36765        {
 36766            if (session is null)
 0767            {
 0768                return;
 769            }
 770
 36771            session.Disconnected -= Session_Disconnected;
 36772            session.ErrorOccured -= Session_ErrorOccured;
 36773        }
 774
 775        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
 0776        {
 0777            OnRaiseError(e);
 0778        }
 779
 780        private void Session_Disconnected(object sender, EventArgs e)
 0781        {
 0782            _channel?.Dispose();
 0783        }
 784
 785        private void Channel_Closed(object sender, ChannelEventArgs e)
 1786        {
 787            // TODO: Do we need to call dispose here ??
 1788            Dispose();
 1789        }
 790
 791        private void Channel_DataReceived(object sender, ChannelDataEventArgs e)
 26792        {
 26793            lock (_incoming)
 26794            {
 1094795                foreach (var b in e.Data)
 508796                {
 508797                    _incoming.Enqueue(b);
 508798                }
 26799            }
 800
 26801            if (_dataReceived != null)
 26802            {
 26803                _ = _dataReceived.Set();
 26804            }
 805
 26806            OnDataReceived(e.Data);
 26807        }
 808
 809        private void OnRaiseError(ExceptionEventArgs e)
 0810        {
 0811            ErrorOccurred?.Invoke(this, e);
 0812        }
 813
 814        private void OnDataReceived(byte[] data)
 26815        {
 26816            DataReceived?.Invoke(this, new ShellDataEventArgs(data));
 26817        }
 818    }
 819}

Methods/Properties

.ctor(Renci.SshNet.ISession,System.String,System.UInt32,System.UInt32,System.UInt32,System.UInt32,System.Collections.Generic.IDictionary`2<Renci.SshNet.Common.TerminalModes,System.UInt32>,System.Int32)
get_DataAvailable()
get_BufferSize()
get_CanRead()
get_CanSeek()
get_CanWrite()
Flush()
get_Length()
get_Position()
set_Position(System.Int64)
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
Expect(Renci.SshNet.ExpectAction[])
Expect(System.TimeSpan,Renci.SshNet.ExpectAction[])
Expect(System.String)
Expect(System.String,System.TimeSpan)
Expect(System.Text.RegularExpressions.Regex)
Expect(System.Text.RegularExpressions.Regex,System.TimeSpan)
BeginExpect(Renci.SshNet.ExpectAction[])
BeginExpect(System.AsyncCallback,Renci.SshNet.ExpectAction[])
BeginExpect(System.AsyncCallback,System.Object,Renci.SshNet.ExpectAction[])
BeginExpect(System.TimeSpan,System.AsyncCallback,System.Object,Renci.SshNet.ExpectAction[])
EndExpect(System.IAsyncResult)
ReadLine()
ReadLine(System.TimeSpan)
Read()
Read(System.Byte[],System.Int32,System.Int32)
Write(System.String)
Write(System.Byte[],System.Int32,System.Int32)
WriteLine(System.String)
Dispose(System.Boolean)
UnsubscribeFromSessionEvents(Renci.SshNet.ISession)
Session_ErrorOccured(System.Object,Renci.SshNet.Common.ExceptionEventArgs)
Session_Disconnected(System.Object,System.EventArgs)
Channel_Closed(System.Object,Renci.SshNet.Common.ChannelEventArgs)
Channel_DataReceived(System.Object,Renci.SshNet.Common.ChannelDataEventArgs)
OnRaiseError(Renci.SshNet.Common.ExceptionEventArgs)
OnDataReceived(System.Byte[])