优化SHA,未传入key时,仅对数据进行哈希,不做消息认证
智能大石头 authored at 2025-12-25 16:40:59
3.62 KiB
X
using NewLife;
using NewLife.Data;
using NewLife.Http;
using Xunit;

namespace XUnitTest.Http;

/// <summary>HttpRequest 解析与构建测试</summary>
public class HttpRequestTests
{
    [Fact]
    public void Parse_Request_Get_WithHeadersAndBody()
    {
        var body = "name=Stone&age=30".GetBytes();
        var header = """
            GET /api/user?id=123 HTTP/1.1
            Host: example.com
            Connection: keep-alive
            Content-Length: 
            """ + body.Length + """

            Content-Type: application/x-www-form-urlencoded
            Custom: Value-1


            """;
        var raw = header.GetBytes();
        var pk = new ArrayPacket(raw) { Next = (ArrayPacket)body };

        var req = new HttpRequest();
        var ok = req.Parse(pk);
        Assert.True(ok);
        Assert.Equal("1.1", req.Version);
        Assert.Equal("GET", req.Method);
        Assert.Equal("/api/user?id=123", req.RequestUri + "");
        Assert.Equal("example.com", req.Host);
        Assert.True(req.KeepAlive);
        Assert.Equal(body.Length, req.ContentLength);
        Assert.Equal("application/x-www-form-urlencoded", req.ContentType);
        Assert.Equal(body.Length, req.BodyLength);
        Assert.True(req.IsCompleted);
        Assert.Equal("Value-1", req.Headers["Custom"]);
    }

    [Fact]
    public void Parse_Request_Post_NoContentLength()
    {
        var header = """
            POST /submit HTTP/1.0
            Host: test.com
            Connection: keep-alive
            Content-Type: text/plain


            """;
        var body = "Hello".GetBytes();
        var pk = new ArrayPacket(header.GetBytes()) { Next = (ArrayPacket)body };

        var req = new HttpRequest();
        var ok = req.Parse(pk);
        Assert.True(ok);
        Assert.Equal("1.0", req.Version);
        Assert.Equal("POST", req.Method);
        Assert.True(req.KeepAlive); // HTTP/1.0 keep-alive 由头部指定
        Assert.Equal(-1, req.ContentLength); // 未提供 Content-Length
        Assert.Equal(body.Length, req.BodyLength);
        Assert.True(req.IsCompleted); // 未指定长度视为完成
    }

    [Fact]
    public void FastParse_Request_OnlyFirstLine()
    {
        var data = "GET /ping HTTP/1.1\r\n".GetBytes();
        var pk = new ArrayPacket(data);
        var req = new HttpRequest();
        var ok = req.FastParse(pk);
        Assert.True(ok);
        Assert.Equal("GET", req.Method);
        Assert.Equal("/ping", req.RequestUri + "");
        Assert.Equal("1.1", req.Version);
        Assert.Null(req.Headers["Host"]);
    }

    [Fact]
    public void Build_Request_AutoMethodAndHost()
    {
        var req = new HttpRequest
        {
            RequestUri = new Uri("http://example.com:80/api/info?x=1"),
            KeepAlive = true,
            ContentType = "application/json",
            Body = (ArrayPacket)"{\"name\":\"Stone\"}".GetBytes()
        };
        var pk = req.Build();
        var text = pk.ToStr();
        Assert.StartsWith("POST /api/info?x=1 HTTP/1.1\r\n", text); // 有主体自动 POST
        Assert.Contains("Host: example.com\r\n", text);
        Assert.Contains("Content-Length: 16\r\n", text);
        Assert.Contains("Content-Type: application/json\r\n", text);
        Assert.Contains("Connection: keep-alive\r\n", text);
        Assert.EndsWith("\r\n{\"name\":\"Stone\"}", text);
    }

    [Fact]
    public void Parse_InvalidHeader_ReturnsFalse()
    {
        var raw = """
            INVALID_HEADER
            Key:Value


            """.GetBytes();
        var pk = new ArrayPacket(raw);
        var req = new HttpRequest();
        var ok = req.Parse(pk);
        Assert.False(ok);
    }
}