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 4.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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 System.Threading.Tasks;
  8. using NLog;
  9. namespace Shadowsocks.Net.Proxy
  10. {
  11. public class HttpProxy : IProxy
  12. {
  13. private static readonly Logger logger = LogManager.GetCurrentClassLogger();
  14. public EndPoint LocalEndPoint => _remote.LocalEndPoint;
  15. public EndPoint ProxyEndPoint { get; private set; }
  16. public EndPoint DestEndPoint { get; private set; }
  17. private readonly Socket _remote = new Socket(SocketType.Stream, ProtocolType.Tcp);
  18. private const string HTTP_CRLF = "\r\n";
  19. private const string HTTP_CONNECT_TEMPLATE =
  20. "CONNECT {0} HTTP/1.1" + HTTP_CRLF +
  21. "Host: {0}" + HTTP_CRLF +
  22. "Proxy-Connection: keep-alive" + HTTP_CRLF +
  23. "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36" + HTTP_CRLF +
  24. "{1}" + // Proxy-Authorization if any
  25. "" + HTTP_CRLF; // End with an empty line
  26. private const string PROXY_AUTH_TEMPLATE = "Proxy-Authorization: Basic {0}" + HTTP_CRLF;
  27. public void Shutdown(SocketShutdown how)
  28. {
  29. _remote.Shutdown(how);
  30. }
  31. public void Close()
  32. {
  33. _remote.Dispose();
  34. }
  35. private static readonly Regex HttpRespondHeaderRegex = new Regex(@"^(HTTP/1\.\d) (\d{3}) (.+)$", RegexOptions.Compiled);
  36. private int _respondLineCount = 0;
  37. private bool _established = false;
  38. private bool OnLineRead(string line, object state)
  39. {
  40. logger.Trace(line);
  41. if (_respondLineCount == 0)
  42. {
  43. var m = HttpRespondHeaderRegex.Match(line);
  44. if (m.Success)
  45. {
  46. var resultCode = m.Groups[2].Value;
  47. if ("200" != resultCode)
  48. {
  49. return true;
  50. }
  51. _established = true;
  52. }
  53. }
  54. else
  55. {
  56. if (string.IsNullOrEmpty(line))
  57. {
  58. return true;
  59. }
  60. }
  61. _respondLineCount++;
  62. return false;
  63. }
  64. private NetworkCredential auth;
  65. public async Task ConnectProxyAsync(EndPoint remoteEP, NetworkCredential auth = null, CancellationToken token = default)
  66. {
  67. ProxyEndPoint = remoteEP;
  68. this.auth = auth;
  69. await _remote.ConnectAsync(remoteEP);
  70. _remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
  71. }
  72. public async Task ConnectRemoteAsync(EndPoint destEndPoint, CancellationToken token = default)
  73. {
  74. DestEndPoint = destEndPoint;
  75. String authInfo = "";
  76. if (auth != null)
  77. {
  78. string authKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(auth.UserName + ":" + auth.Password));
  79. authInfo = string.Format(PROXY_AUTH_TEMPLATE, authKey);
  80. }
  81. string request = string.Format(HTTP_CONNECT_TEMPLATE, destEndPoint, authInfo);
  82. var b = Encoding.UTF8.GetBytes(request);
  83. await _remote.SendAsync(Encoding.UTF8.GetBytes(request), SocketFlags.None, token);
  84. // start line read
  85. LineReader reader = new LineReader(_remote, OnLineRead, (e, _) => throw e, (_1, _2, _3, _4) => { }, Encoding.UTF8, HTTP_CRLF, 1024, null);
  86. await reader.Finished;
  87. }
  88. public async Task<int> SendAsync(ReadOnlyMemory<byte> buffer, CancellationToken token = default)
  89. {
  90. return await _remote.SendAsync(buffer, SocketFlags.None, token);
  91. }
  92. public async Task<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
  93. {
  94. return await _remote.ReceiveAsync(buffer, SocketFlags.None, token);
  95. }
  96. }
  97. }