HttpProxyStub.cs 5.5 KB

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