HttpClientHandler.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.Security;
  8. using System.Net.Sockets;
  9. using System.Security.Cryptography.X509Certificates;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. namespace FastGithub.Http
  13. {
  14. /// <summary>
  15. /// HttpClientHandler
  16. /// </summary>
  17. class HttpClientHandler : DelegatingHandler
  18. {
  19. private readonly IDomainResolver domainResolver;
  20. /// <summary>
  21. /// HttpClientHandler
  22. /// </summary>
  23. /// <param name="domainResolver"></param>
  24. public HttpClientHandler(IDomainResolver domainResolver)
  25. {
  26. this.domainResolver = domainResolver;
  27. this.InnerHandler = CreateSocketsHttpHandler();
  28. }
  29. /// <summary>
  30. /// 创建转发代理的httpHandler
  31. /// </summary>
  32. /// <returns></returns>
  33. private static SocketsHttpHandler CreateSocketsHttpHandler()
  34. {
  35. return new SocketsHttpHandler
  36. {
  37. Proxy = null,
  38. UseProxy = false,
  39. AllowAutoRedirect = false,
  40. AutomaticDecompression = DecompressionMethods.None,
  41. ConnectCallback = async (context, cancellationToken) =>
  42. {
  43. var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
  44. await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
  45. var stream = new NetworkStream(socket, ownsSocket: true);
  46. var requestContext = context.InitialRequestMessage.GetRequestContext();
  47. if (requestContext.IsHttps == false)
  48. {
  49. return stream;
  50. }
  51. var sslStream = new SslStream(stream, leaveInnerStreamOpen: false);
  52. await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
  53. {
  54. TargetHost = requestContext.TlsSniPattern.Value,
  55. RemoteCertificateValidationCallback = ValidateServerCertificate
  56. }, cancellationToken);
  57. return sslStream;
  58. bool ValidateServerCertificate(object sender, X509Certificate? cert, X509Chain? chain, SslPolicyErrors errors)
  59. {
  60. if (errors == SslPolicyErrors.RemoteCertificateNameMismatch)
  61. {
  62. if (requestContext.TlsIgnoreNameMismatch == true)
  63. {
  64. return true;
  65. }
  66. var host = requestContext.Host;
  67. var dnsNames = ReadDnsNames(cert);
  68. return dnsNames.Any(dns => IsMatch(dns, host));
  69. }
  70. return errors == SslPolicyErrors.None;
  71. }
  72. }
  73. };
  74. }
  75. /// <summary>
  76. /// 读取使用的DNS名称
  77. /// </summary>
  78. /// <param name="cert"></param>
  79. /// <returns></returns>
  80. private static IEnumerable<string> ReadDnsNames(X509Certificate? cert)
  81. {
  82. if (cert == null)
  83. {
  84. yield break;
  85. }
  86. var parser = new Org.BouncyCastle.X509.X509CertificateParser();
  87. var x509Cert = parser.ReadCertificate(cert.GetRawCertData());
  88. var subjects = x509Cert.GetSubjectAlternativeNames();
  89. foreach (var subject in subjects)
  90. {
  91. if (subject is IList list)
  92. {
  93. if (list.Count >= 2 && list[0] is int nameType && nameType == 2)
  94. {
  95. var dnsName = list[1]?.ToString();
  96. if (dnsName != null)
  97. {
  98. yield return dnsName;
  99. }
  100. }
  101. }
  102. }
  103. }
  104. /// <summary>
  105. /// 比较域名
  106. /// </summary>
  107. /// <param name="dnsName"></param>
  108. /// <param name="host"></param>
  109. /// <returns></returns>
  110. private static bool IsMatch(string dnsName, string? host)
  111. {
  112. if (host == null)
  113. {
  114. return false;
  115. }
  116. if (dnsName == host)
  117. {
  118. return true;
  119. }
  120. if (dnsName[0] == '*')
  121. {
  122. return host.EndsWith(dnsName[1..]);
  123. }
  124. return false;
  125. }
  126. /// <summary>
  127. /// 替换域名为ip
  128. /// </summary>
  129. /// <param name="request"></param>
  130. /// <param name="cancellationToken"></param>
  131. /// <returns></returns>
  132. protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  133. {
  134. var uri = request.RequestUri;
  135. if (uri != null && uri.HostNameType == UriHostNameType.Dns)
  136. {
  137. var address = await this.domainResolver.ResolveAsync(uri.Host, cancellationToken);
  138. var builder = new UriBuilder(uri)
  139. {
  140. Scheme = Uri.UriSchemeHttp,
  141. Host = address.ToString(),
  142. };
  143. request.RequestUri = builder.Uri;
  144. request.Headers.Host = uri.Host;
  145. var context = request.GetRequestContext();
  146. context.TlsSniPattern = context.TlsSniPattern.WithDomain(uri.Host).WithIPAddress(address).WithRandom();
  147. }
  148. return await base.SendAsync(request, cancellationToken);
  149. }
  150. }
  151. }