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

namespace NewLife.Http;

/// <summary>Http响应</summary>
public class HttpResponse : HttpBase
{
    #region 属性
    /// <summary>状态码</summary>
    public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK;

    /// <summary>状态描述</summary>
    public String? StatusDescription { get; set; }
    #endregion

    /// <summary>分析第一行</summary>
    /// <param name="firstLine"></param>
    protected override Boolean OnParse(String firstLine)
    {
        if (firstLine.IsNullOrEmpty()) return false;

        // HTTP/1.1 502 Bad Gateway
        if (!firstLine.StartsWith("HTTP/")) return false;

        var ss = firstLine.Split(' ');
        //if (ss.Length < 3) throw new Exception("非法响应头 {0}".F(firstLine));
        if (ss.Length < 3) return false;

        Version = ss[0].TrimStart("HTTP/");

        // 分析响应码
        var code = ss[1].ToInt();
        if (code > 0) StatusCode = (HttpStatusCode)code;

        StatusDescription = ss.Skip(2).Join(" ");

        return true;
    }

    /// <summary>创建请求响应包</summary>
    /// <returns></returns>
    public override Packet Build()
    {
        // 如果响应异常,则使用响应描述作为内容
        if (StatusCode > HttpStatusCode.OK && Body == null && !StatusDescription.IsNullOrEmpty())
        {
            Body = StatusDescription.GetBytes();
        }

        return base.Build();
    }

    /// <summary>创建头部</summary>
    /// <param name="length"></param>
    /// <returns></returns>
    protected override String BuildHeader(Int32 length)
    {
        // 构建头部
        var sb = Pool.StringBuilder.Get();
        sb.AppendFormat("HTTP/{2} {0} {1}\r\n", (Int32)StatusCode, StatusCode, Version);

        //// cors
        //sb.AppendFormat("Access-Control-Allow-Origin:{0}\r\n", "*");
        //sb.AppendFormat("Access-Control-Allow-Methods:{0}\r\n", "POST, GET");
        //sb.AppendFormat("Access-Control-Allow-Headers:{0}\r\n", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

        // 内容长度
        if (length > 0)
            Headers["Content-Length"] = length + "";
        else if (!Headers.ContainsKey("Transfer-Encoding"))
            Headers["Content-Length"] = "0";

        if (!ContentType.IsNullOrEmpty()) Headers["Content-Type"] = ContentType;

        foreach (var item in Headers)
        {
            sb.AppendFormat("{0}: {1}\r\n", item.Key, item.Value);
        }

        sb.Append("\r\n");

        return sb.Put(true);
    }

    /// <summary>验证,如果失败则抛出异常</summary>
    public void Valid()
    {
        if (StatusCode != HttpStatusCode.OK) throw new Exception(StatusDescription ?? (StatusCode + ""));
    }

    /// <summary>设置结果,影响Body和ContentType</summary>
    /// <param name="result"></param>
    /// <param name="contentType"></param>
    public void SetResult(Object result, String? contentType = null)
    {
        if (result == null) return;

        if (result is Exception ex)
        {
            if (ex is ApiException aex)
                StatusCode = (HttpStatusCode)aex.Code;
            else
                StatusCode = HttpStatusCode.InternalServerError;

            StatusDescription = ex.Message;
        }
        else if (result is Packet pk)
        {
            if (contentType.IsNullOrEmpty()) contentType = "application/octet-stream";
            Body = pk;
        }
        else if (result is Byte[] buffer)
        {
            if (contentType.IsNullOrEmpty()) contentType = "application/octet-stream";
            Body = buffer;
        }
        else if (result is Stream stream)
        {
            if (contentType.IsNullOrEmpty()) contentType = "application/octet-stream";
            Body = stream.ReadBytes(-1);
        }
        else if (result is String str)
        {
            if (contentType.IsNullOrEmpty()) contentType = "text/html";
            Body = str.GetBytes();
        }
        else
        {
            if (contentType.IsNullOrEmpty()) contentType = "application/json";
            Body = result.ToJson().GetBytes();
        }

        if (ContentType.IsNullOrEmpty()) ContentType = contentType;
    }

    /// <summary>已重载。</summary>
    /// <returns></returns>
    public override String ToString() => $"HTTP/{Version} {(Int32)StatusCode} {StatusDescription ?? (StatusCode + "")}";
}