HttpConnectorTest_Connect_TimeoutReadingHttpContent.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Threading;
  9. using Microsoft.VisualStudio.TestTools.UnitTesting;
  10. using Moq;
  11. using Renci.SshNet.Common;
  12. using Renci.SshNet.Tests.Common;
  13. namespace Renci.SshNet.Tests.Classes.Connection
  14. {
  15. [TestClass]
  16. public class HttpConnectorTest_Connect_TimeoutReadingHttpContent : HttpConnectorTestBase
  17. {
  18. private ConnectionInfo _connectionInfo;
  19. private SshOperationTimeoutException _actualException;
  20. private Socket _clientSocket;
  21. private AsyncSocketListener _proxyServer;
  22. private List<byte> _bytesReceivedByProxy;
  23. private string _expectedHttpRequest;
  24. private Stopwatch _stopWatch;
  25. private AsyncSocketListener _server;
  26. private bool _disconnected;
  27. protected override void SetupData()
  28. {
  29. base.SetupData();
  30. var random = new Random();
  31. _connectionInfo = new ConnectionInfo(IPAddress.Loopback.ToString(),
  32. 1026,
  33. "user",
  34. ProxyTypes.Http,
  35. IPAddress.Loopback.ToString(),
  36. 8122,
  37. "proxyUser",
  38. "proxyPwd",
  39. new KeyboardInteractiveAuthenticationMethod("user"))
  40. {
  41. Timeout = TimeSpan.FromMilliseconds(random.Next(50, 200))
  42. };
  43. _expectedHttpRequest = string.Format("CONNECT {0}:{1} HTTP/1.0{2}" +
  44. "Proxy-Authorization: Basic cHJveHlVc2VyOnByb3h5UHdk{2}{2}",
  45. _connectionInfo.Host,
  46. _connectionInfo.Port.ToString(CultureInfo.InvariantCulture),
  47. "\r\n");
  48. _bytesReceivedByProxy = new List<byte>();
  49. _stopWatch = new Stopwatch();
  50. _actualException = null;
  51. _clientSocket = SocketFactory.Create(SocketType.Stream, ProtocolType.Tcp);
  52. _proxyServer = new AsyncSocketListener(new IPEndPoint(IPAddress.Loopback, _connectionInfo.ProxyPort));
  53. _proxyServer.Disconnected += (socket) => _disconnected = true;
  54. _proxyServer.BytesReceived += (bytesReceived, socket) =>
  55. {
  56. _bytesReceivedByProxy.AddRange(bytesReceived);
  57. // Force a timeout by sending less content than indicated by Content-Length header
  58. if (_bytesReceivedByProxy.Count == _expectedHttpRequest.Length)
  59. {
  60. _ = socket.Send(Encoding.ASCII.GetBytes("HTTP/1.0 200 OK\r\n"));
  61. _ = socket.Send(Encoding.ASCII.GetBytes("Content-Length: 10\r\n"));
  62. _ = socket.Send(Encoding.ASCII.GetBytes("Content-Type: application/octet-stream\r\n"));
  63. _ = socket.Send(Encoding.ASCII.GetBytes("\r\n"));
  64. _ = socket.Send(Encoding.ASCII.GetBytes("TOO_FEW"));
  65. }
  66. };
  67. _proxyServer.Start();
  68. _server = new AsyncSocketListener(new IPEndPoint(IPAddress.Loopback, _connectionInfo.Port));
  69. _server.Start();
  70. }
  71. protected override void SetupMocks()
  72. {
  73. _ = SocketFactoryMock.Setup(p => p.Create(SocketType.Stream, ProtocolType.Tcp))
  74. .Returns(_clientSocket);
  75. }
  76. protected override void TearDown()
  77. {
  78. base.TearDown();
  79. _server?.Dispose();
  80. _proxyServer?.Dispose();
  81. }
  82. protected override void Act()
  83. {
  84. _stopWatch.Start();
  85. try
  86. {
  87. _ = Connector.Connect(_connectionInfo);
  88. Assert.Fail();
  89. }
  90. catch (SshOperationTimeoutException ex)
  91. {
  92. _actualException = ex;
  93. }
  94. finally
  95. {
  96. _stopWatch.Stop();
  97. }
  98. // Give some time to process all messages
  99. Thread.Sleep(200);
  100. }
  101. [TestMethod]
  102. public void ConnectShouldHaveThrownSshOperationTimeoutException()
  103. {
  104. Assert.IsNull(_actualException.InnerException);
  105. Assert.AreEqual(string.Format(CultureInfo.InvariantCulture, "Socket read operation has timed out after {0:F0} milliseconds.", _connectionInfo.Timeout.TotalMilliseconds), _actualException.Message);
  106. }
  107. [TestMethod]
  108. public void ProxyShouldHaveReceivedExpectedHttpRequest()
  109. {
  110. Assert.AreEqual(_expectedHttpRequest, Encoding.ASCII.GetString(_bytesReceivedByProxy.ToArray()));
  111. }
  112. [TestMethod]
  113. public void ConnectShouldHaveRespectedTimeout()
  114. {
  115. var errorText = string.Format("Elapsed: {0}, Timeout: {1}",
  116. _stopWatch.ElapsedMilliseconds,
  117. _connectionInfo.Timeout.TotalMilliseconds);
  118. // Compare elapsed time with configured timeout, allowing for a margin of error
  119. Assert.IsTrue(_stopWatch.ElapsedMilliseconds >= _connectionInfo.Timeout.TotalMilliseconds - 10, errorText);
  120. Assert.IsTrue(_stopWatch.ElapsedMilliseconds < _connectionInfo.Timeout.TotalMilliseconds + 100, errorText);
  121. }
  122. [TestMethod]
  123. public void ClientSocketShouldNotBeConnected()
  124. {
  125. Assert.IsTrue(_disconnected);
  126. Assert.IsFalse(_clientSocket.Connected);
  127. }
  128. [TestMethod]
  129. public void ClientSocketShouldHaveBeenDisposed()
  130. {
  131. try
  132. {
  133. _ = _clientSocket.Receive(new byte[0]);
  134. Assert.Fail();
  135. }
  136. catch (ObjectDisposedException)
  137. {
  138. }
  139. }
  140. [TestMethod]
  141. public void CreateOnSocketFactoryShouldHaveBeenInvokedOnce()
  142. {
  143. SocketFactoryMock.Verify(p => p.Create(SocketType.Stream, ProtocolType.Tcp),
  144. Times.Once());
  145. }
  146. }
  147. }