You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

HttpProxy.cs 6.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. using System;
  2. using System.Net;
  3. using System.Net.Sockets;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using System.Threading;
  7. using Shadowsocks.Controller;
  8. using Shadowsocks.Util.Sockets;
  9. namespace Shadowsocks.Proxy
  10. {
  11. public class HttpProxy : IProxy
  12. {
  13. private class FakeAsyncResult : IAsyncResult
  14. {
  15. public readonly HttpState innerState;
  16. private readonly IAsyncResult r;
  17. public FakeAsyncResult(IAsyncResult orig, HttpState state)
  18. {
  19. r = orig;
  20. innerState = state;
  21. }
  22. public bool IsCompleted => r.IsCompleted;
  23. public WaitHandle AsyncWaitHandle => r.AsyncWaitHandle;
  24. public object AsyncState => innerState.AsyncState;
  25. public bool CompletedSynchronously => r.CompletedSynchronously;
  26. }
  27. private class HttpState
  28. {
  29. public AsyncCallback Callback { get; set; }
  30. public object AsyncState { get; set; }
  31. public int BytesToRead;
  32. public Exception ex { get; set; }
  33. }
  34. public EndPoint LocalEndPoint => _remote.LocalEndPoint;
  35. public EndPoint ProxyEndPoint { get; private set; }
  36. public EndPoint DestEndPoint { get; private set; }
  37. private readonly WrappedSocket _remote = new WrappedSocket();
  38. public void BeginConnectProxy(EndPoint remoteEP, AsyncCallback callback, object state)
  39. {
  40. ProxyEndPoint = remoteEP;
  41. _remote.BeginConnect(remoteEP, callback, state);
  42. }
  43. public void EndConnectProxy(IAsyncResult asyncResult)
  44. {
  45. _remote.EndConnect(asyncResult);
  46. _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
  47. }
  48. private const string HTTP_CRLF = "\r\n";
  49. private const string HTTP_CONNECT_TEMPLATE =
  50. "CONNECT {0} HTTP/1.1" + HTTP_CRLF +
  51. "Host: {0}" + HTTP_CRLF +
  52. "Proxy-Connection: keep-alive" + HTTP_CRLF +
  53. "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" + HTTP_CRLF +
  54. "" + HTTP_CRLF; // End with an empty line
  55. public void BeginConnectDest(EndPoint destEndPoint, AsyncCallback callback, object state)
  56. {
  57. DestEndPoint = destEndPoint;
  58. string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint);
  59. var b = Encoding.UTF8.GetBytes(request);
  60. var st = new HttpState();
  61. st.Callback = callback;
  62. st.AsyncState = state;
  63. _remote.BeginSend(b, 0, b.Length, 0, HttpRequestSendCallback, st);
  64. }
  65. public void EndConnectDest(IAsyncResult asyncResult)
  66. {
  67. var state = ((FakeAsyncResult)asyncResult).innerState;
  68. if (state.ex != null)
  69. {
  70. throw state.ex;
  71. }
  72. }
  73. public void BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
  74. object state)
  75. {
  76. _remote.BeginSend(buffer, offset, size, socketFlags, callback, state);
  77. }
  78. public int EndSend(IAsyncResult asyncResult)
  79. {
  80. return _remote.EndSend(asyncResult);
  81. }
  82. public void BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback,
  83. object state)
  84. {
  85. _remote.BeginReceive(buffer, offset, size, socketFlags, callback, state);
  86. }
  87. public int EndReceive(IAsyncResult asyncResult)
  88. {
  89. return _remote.EndReceive(asyncResult);
  90. }
  91. public void Shutdown(SocketShutdown how)
  92. {
  93. _remote.Shutdown(how);
  94. }
  95. public void Close()
  96. {
  97. _remote.Dispose();
  98. }
  99. private void HttpRequestSendCallback(IAsyncResult ar)
  100. {
  101. var state = (HttpState) ar.AsyncState;
  102. try
  103. {
  104. _remote.EndSend(ar);
  105. // start line read
  106. new LineReader(_remote, OnLineRead, OnException, OnFinish, Encoding.UTF8, HTTP_CRLF, 1024, new FakeAsyncResult(ar, state));
  107. }
  108. catch (Exception ex)
  109. {
  110. state.ex = ex;
  111. state.Callback?.Invoke(new FakeAsyncResult(ar, state));
  112. }
  113. }
  114. private void OnFinish(byte[] lastBytes, int index, int length, object state)
  115. {
  116. var st = (FakeAsyncResult)state;
  117. if (st.innerState.ex == null)
  118. {
  119. if (!_established)
  120. {
  121. st.innerState.ex = new Exception(I18N.GetString("Proxy request failed"));
  122. }
  123. // TODO: save last bytes
  124. }
  125. st.innerState.Callback?.Invoke(st);
  126. }
  127. private void OnException(Exception ex, object state)
  128. {
  129. var st = (FakeAsyncResult) state;
  130. st.innerState.ex = ex;
  131. }
  132. private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$");
  133. private int _respondLineCount = 0;
  134. private bool _established = false;
  135. private bool OnLineRead(string line, object state)
  136. {
  137. Logging.Debug(line);
  138. if (_respondLineCount == 0)
  139. {
  140. var m = HttpRespondHeaderRegex.Match(line);
  141. if (m.Success)
  142. {
  143. var resultCode = m.Groups[2].Value;
  144. if ("200" != resultCode)
  145. {
  146. return true;
  147. }
  148. _established = true;
  149. }
  150. }
  151. else
  152. {
  153. if (line.IsNullOrEmpty())
  154. {
  155. return true;
  156. }
  157. }
  158. _respondLineCount++;
  159. return false;
  160. }
  161. }
  162. }