2
0

HttpProxyStub.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. namespace Renci.SshNet.Tests.Common
  7. {
  8. public class HttpProxyStub : IDisposable
  9. {
  10. private readonly IPEndPoint _endPoint;
  11. private AsyncSocketListener _listener;
  12. private HttpRequestParser _httpRequestParser;
  13. private readonly IList<byte[]> _responses;
  14. public HttpProxyStub(IPEndPoint endPoint)
  15. {
  16. _endPoint = endPoint;
  17. _responses = new List<byte[]>();
  18. }
  19. public HttpRequest HttpRequest
  20. {
  21. get
  22. {
  23. if (_httpRequestParser == null)
  24. {
  25. throw new InvalidOperationException("The proxy is not started.");
  26. }
  27. return _httpRequestParser.HttpRequest;
  28. }
  29. }
  30. public IList<byte[]> Responses
  31. {
  32. get { return _responses; }
  33. }
  34. public void Start()
  35. {
  36. _httpRequestParser = new HttpRequestParser();
  37. _listener = new AsyncSocketListener(_endPoint);
  38. _listener.BytesReceived += OnBytesReceived;
  39. _listener.Start();
  40. }
  41. public void Stop()
  42. {
  43. _listener?.Stop();
  44. }
  45. public void Dispose()
  46. {
  47. Stop();
  48. GC.SuppressFinalize(this);
  49. }
  50. private void OnBytesReceived(byte[] bytesReceived, Socket socket)
  51. {
  52. _httpRequestParser.ProcessData(bytesReceived);
  53. if (_httpRequestParser.CurrentState == HttpRequestParser.State.Content)
  54. {
  55. foreach (var response in Responses)
  56. {
  57. _ = socket.Send(response);
  58. }
  59. socket.Shutdown(SocketShutdown.Send);
  60. }
  61. }
  62. private class HttpRequestParser
  63. {
  64. private readonly List<byte> _buffer;
  65. private readonly HttpRequest _httpRequest;
  66. public enum State
  67. {
  68. RequestLine,
  69. Headers,
  70. Content
  71. }
  72. public HttpRequestParser()
  73. {
  74. CurrentState = State.RequestLine;
  75. _buffer = new List<byte>();
  76. _httpRequest = new HttpRequest();
  77. }
  78. public HttpRequest HttpRequest
  79. {
  80. get { return _httpRequest; }
  81. }
  82. public State CurrentState { get; private set; }
  83. public void ProcessData(byte[] data)
  84. {
  85. var position = 0;
  86. while (position != data.Length)
  87. {
  88. if (CurrentState == State.RequestLine)
  89. {
  90. var requestLine = ReadLine(data, ref position);
  91. if (requestLine != null)
  92. {
  93. _httpRequest.RequestLine = requestLine;
  94. CurrentState = State.Headers;
  95. }
  96. }
  97. if (CurrentState == State.Headers)
  98. {
  99. var line = ReadLine(data, ref position);
  100. if (line != null)
  101. {
  102. if (line.Length == 0)
  103. {
  104. CurrentState = State.Content;
  105. }
  106. else
  107. {
  108. _httpRequest.Headers.Add(line);
  109. }
  110. }
  111. }
  112. if (CurrentState == State.Content)
  113. {
  114. if (position < data.Length)
  115. {
  116. var currentContent = _httpRequest.MessageBody;
  117. var newBufferSize = currentContent.Length + (data.Length - position);
  118. var copyBuffer = new byte[newBufferSize];
  119. Array.Copy(currentContent, copyBuffer, currentContent.Length);
  120. Array.Copy(data, position, copyBuffer, currentContent.Length, data.Length - position);
  121. _httpRequest.MessageBody = copyBuffer;
  122. break;
  123. }
  124. }
  125. }
  126. }
  127. private string ReadLine(byte[] data, ref int position)
  128. {
  129. for (; position < data.Length; position++)
  130. {
  131. var b = data[position];
  132. if (b == '\n')
  133. {
  134. var buffer = _buffer.ToArray();
  135. var bytesInLine = buffer.Length;
  136. // when the previous byte was a CR, then do not include it in line
  137. if (buffer.Length > 0 && buffer[buffer.Length - 1] == '\r')
  138. {
  139. bytesInLine -= 1;
  140. }
  141. // clear the buffer
  142. _buffer.Clear();
  143. // move position up one position as we've processed the current byte
  144. position++;
  145. return Encoding.ASCII.GetString(buffer, 0, bytesInLine);
  146. }
  147. _buffer.Add(b);
  148. }
  149. return null;
  150. }
  151. }
  152. }
  153. }