HttpProxyMiddleware.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. using FastGithub.Configuration;
  2. using FastGithub.DomainResolve;
  3. using Microsoft.AspNetCore.Connections.Features;
  4. using Microsoft.AspNetCore.Http;
  5. using Microsoft.AspNetCore.Http.Features;
  6. using System.IO.Pipelines;
  7. using System.Net;
  8. using System.Net.Http;
  9. using System.Net.Sockets;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using Yarp.ReverseProxy.Forwarder;
  13. namespace FastGithub.HttpServer
  14. {
  15. /// <summary>
  16. /// http代理中间件
  17. /// </summary>
  18. sealed class HttpProxyMiddleware
  19. {
  20. private readonly FastGithubConfig fastGithubConfig;
  21. private readonly IDomainResolver domainResolver;
  22. private readonly IHttpForwarder httpForwarder;
  23. private readonly HttpMessageInvoker httpClient;
  24. /// <summary>
  25. /// http代理中间件
  26. /// </summary>
  27. /// <param name="fastGithubConfig"></param>
  28. /// <param name="domainResolver"></param>
  29. /// <param name="httpForwarder"></param>
  30. public HttpProxyMiddleware(
  31. FastGithubConfig fastGithubConfig,
  32. IDomainResolver domainResolver,
  33. IHttpForwarder httpForwarder)
  34. {
  35. this.fastGithubConfig = fastGithubConfig;
  36. this.domainResolver = domainResolver;
  37. this.httpForwarder = httpForwarder;
  38. this.httpClient = new HttpMessageInvoker(CreateHttpHandler(), disposeHandler: false);
  39. }
  40. /// <summary>
  41. /// 处理请求
  42. /// </summary>
  43. /// <param name="context"></param>
  44. /// <returns></returns>
  45. public async Task InvokeAsync(HttpContext context)
  46. {
  47. if (context.Request.Method == HttpMethods.Get && context.Request.Path == "/proxy.pac")
  48. {
  49. var buidler = new StringBuilder();
  50. buidler.AppendLine("function FindProxyForURL(url, host){");
  51. buidler.AppendLine($" var proxy = 'PROXY {context.Request.Host}';");
  52. foreach (var domain in this.fastGithubConfig.GetDomainPatterns())
  53. {
  54. buidler.AppendLine($" if (shExpMatch(host, '{domain}')) return proxy;");
  55. }
  56. buidler.AppendLine(" return 'DIRECT';");
  57. buidler.AppendLine("}");
  58. var pacString = buidler.ToString();
  59. context.Response.ContentType = "application/x-ns-proxy-autoconfig";
  60. await context.Response.WriteAsync(pacString);
  61. }
  62. else if (context.Request.Method != HttpMethods.Connect)
  63. {
  64. var destinationPrefix = $"{context.Request.Scheme}://{context.Request.Host}";
  65. await this.httpForwarder.SendAsync(context, destinationPrefix, this.httpClient);
  66. }
  67. else
  68. {
  69. var endpoint = await this.GetTargetEndPointAsync(context.Request);
  70. using var targetSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
  71. await targetSocket.ConnectAsync(endpoint);
  72. context.Response.StatusCode = StatusCodes.Status200OK;
  73. context.Features.Get<IHttpResponseFeature>().ReasonPhrase = "Connection Established";
  74. await context.Response.CompleteAsync();
  75. var transport = context.Features.Get<IConnectionTransportFeature>()?.Transport;
  76. if (transport != null)
  77. {
  78. var targetStream = new NetworkStream(targetSocket, ownsSocket: false);
  79. var task1 = targetStream.CopyToAsync(transport.Output);
  80. var task2 = transport.Input.CopyToAsync(targetStream);
  81. await Task.WhenAny(task1, task2);
  82. }
  83. }
  84. }
  85. /// <summary>
  86. /// 获取目标终节点
  87. /// </summary>
  88. /// <param name="request"></param>
  89. /// <returns></returns>
  90. private async Task<EndPoint> GetTargetEndPointAsync(HttpRequest request)
  91. {
  92. const int HTTPS_PORT = 443;
  93. var targetHost = request.Host.Host;
  94. var targetPort = request.Host.Port ?? HTTPS_PORT;
  95. if (IPAddress.TryParse(targetHost, out var address) == true)
  96. {
  97. return new IPEndPoint(address, targetPort);
  98. }
  99. if (this.fastGithubConfig.TryGetDomainConfig(targetHost, out _) == false)
  100. {
  101. return new DnsEndPoint(targetHost, targetPort);
  102. }
  103. // https,走反向代理中间人
  104. if (targetPort == HTTPS_PORT)
  105. {
  106. return new IPEndPoint(IPAddress.Loopback, HttpsReverseProxyPort.Value);
  107. }
  108. // dns优选
  109. address = await this.domainResolver.ResolveAsync(new DnsEndPoint(targetHost, targetPort));
  110. return new IPEndPoint(address, targetPort);
  111. }
  112. /// <summary>
  113. /// 创建httpHandler
  114. /// </summary>
  115. /// <returns></returns>
  116. private static SocketsHttpHandler CreateHttpHandler()
  117. {
  118. return new()
  119. {
  120. Proxy = null,
  121. UseProxy = false,
  122. UseCookies = false,
  123. AllowAutoRedirect = false,
  124. AutomaticDecompression = DecompressionMethods.None
  125. };
  126. }
  127. }
  128. }