[fix]修正UdpServer在接收广播时连续启动接收的错误,在StarAgent中,此时可能收到广播包,SocketFlags是Broadcast,需要清空,否则报错“参考的对象类型不支持尝试的操作”; 无需设置SocketOptionName.PacketInformation,在ReceiveMessageFromAsync时会自动设置,并且支持ipv6;
石头 编写于 2024-10-10 00:36:00 石头 提交于 2024-10-10 00:45:43
X
using System.Net.Http;
using NewLife.Net;

namespace NewLife.Http;

/// <summary>支持优化Dns解析的HttpClient处理器</summary>
public class DnsHttpHandler : DelegatingHandler
{
    #region 属性
    /// <summary>DNS解析器</summary>
    public IDnsResolver Resolver { get; set; } = DnsResolver.Instance;
    #endregion

    /// <summary>实例化一个支持APM的HttpClient处理器</summary>
    /// <param name="innerHandler"></param>
    public DnsHttpHandler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    /// <summary>发送请求</summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var uri = request.RequestUri;
        if (uri == null) return await base.SendAsync(request, cancellationToken);

        // 调用自定义DNS解析器
        var addrs = Resolver?.Resolve(uri.Host);
        if (addrs != null && addrs.Length > 0)
        {
            var addr = addrs[0];

            // 从请求中读取当前使用的IP索引,轮询使用。因为HttpClient调用失败后会重试,这里分配新的IP
#if NET5_0_OR_GREATER
            var key = new HttpRequestOptionsKey<Int32>("dnsIndex");
            if (!request.Options.TryGetValue(key, out var idx)) idx = 0;

            addr = addrs[idx % addrs.Length];
            request.Options.Set(key, ++idx);
#else
            var idx = request.Properties.TryGetValue("dnsIndex", out var obj) ? obj.ToInt() : 0;
            addr = addrs[idx % addrs.Length];
            request.Properties["dnsIndex"] = ++idx;
#endif

            // 先固定Host
            request.Headers.Host ??= uri.Host;

            var builder = new UriBuilder(uri)
            {
                Host = addr + "",
            };

            // 再把Uri换成IP
            request.RequestUri = builder.Uri;
        }

        return await base.SendAsync(request, cancellationToken);
    }
}