mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-07-30 17:30:40 +00:00
156 lines
5.4 KiB
C#
156 lines
5.4 KiB
C#
using Esiur.Data;
|
|
using Esiur.Net.Packets;
|
|
using Esiur.Net.Packets.Http;
|
|
using Esiur.Net.Packets.WebSocket;
|
|
using Esiur.Resource;
|
|
using System.Text;
|
|
|
|
namespace Esiur.Tests.Unit;
|
|
|
|
public class PacketCodecTests
|
|
{
|
|
[Fact]
|
|
public void EpParser_ValidatesSliceBounds()
|
|
{
|
|
var packet = new EpAuthPacket(new Warehouse());
|
|
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => packet.Parse(Array.Empty<byte>(), 1, 0));
|
|
Assert.Throws<ArgumentOutOfRangeException>(() => packet.Parse(Array.Empty<byte>(), 0, 1));
|
|
}
|
|
|
|
[Fact]
|
|
public void WebSocket_ComposesAndParsesUnmaskedFrame()
|
|
{
|
|
var packet = new WebsocketPacket
|
|
{
|
|
FIN = true,
|
|
Opcode = WebsocketPacket.WSOpcode.BinaryFrame,
|
|
Message = new byte[] { 1, 2, 3 }
|
|
};
|
|
|
|
Assert.True(packet.Compose());
|
|
Assert.Equal(new byte[] { 0x82, 0x03, 1, 2, 3 }, packet.Data);
|
|
|
|
var parsed = new WebsocketPacket();
|
|
Assert.Equal(packet.Data.Length, parsed.Parse(packet.Data, 0, (uint)packet.Data.Length));
|
|
Assert.Equal(packet.Message, parsed.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void WebSocket_MasksOutboundPayloadAndRestoresItWhenParsed()
|
|
{
|
|
var message = Encoding.UTF8.GetBytes("hello");
|
|
var packet = new WebsocketPacket
|
|
{
|
|
FIN = true,
|
|
Mask = true,
|
|
MaskKey = new byte[] { 1, 2, 3, 4 },
|
|
Opcode = WebsocketPacket.WSOpcode.TextFrame,
|
|
Message = message
|
|
};
|
|
|
|
packet.Compose();
|
|
|
|
Assert.Equal(0x81, packet.Data[0]);
|
|
Assert.Equal(0x85, packet.Data[1]);
|
|
Assert.Equal(packet.MaskKey, packet.Data.Skip(2).Take(4));
|
|
Assert.NotEqual(message, packet.Data.Skip(6).ToArray());
|
|
|
|
var parsed = new WebsocketPacket();
|
|
Assert.Equal(packet.Data.Length, parsed.Parse(packet.Data, 0, (uint)packet.Data.Length));
|
|
Assert.Equal(message, parsed.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void WebSocket_RejectsOversizedDeclarationBeforePayloadArrives()
|
|
{
|
|
var packet = new WebsocketPacket { MaximumPayloadLength = 1_024 };
|
|
var header = new byte[] { 0x82, 126, 0x04, 0x01 };
|
|
|
|
Assert.Throws<ParserLimitException>(() => packet.Parse(header, 0, (uint)header.Length));
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpRequest_ParsesOffsetBodyQueryCookiesAndForm()
|
|
{
|
|
const string request =
|
|
"POST /submit?a=1 HTTP/1.1\r\n" +
|
|
"Host: example.test\r\n" +
|
|
"Cookie: sid=abc; flag\r\n" +
|
|
"Content-Type: application/x-www-form-urlencoded\r\n" +
|
|
"Content-Length: 7\r\n\r\n" +
|
|
"x=hello";
|
|
var packetBytes = Encoding.ASCII.GetBytes(request);
|
|
var data = new byte[packetBytes.Length + 2];
|
|
Buffer.BlockCopy(packetBytes, 0, data, 2, packetBytes.Length);
|
|
|
|
var packet = new HttpRequestPacket();
|
|
var consumed = packet.Parse(data, 2, (uint)data.Length);
|
|
|
|
Assert.Equal(packetBytes.Length, consumed);
|
|
Assert.Equal(Esiur.Net.Packets.Http.HttpMethod.POST, packet.Method);
|
|
Assert.Equal("/submit", packet.Filename);
|
|
Assert.Equal("1", packet.Query["A"]);
|
|
Assert.Equal("abc", packet.Cookies["SID"]);
|
|
Assert.Equal(string.Empty, packet.Cookies["flag"]);
|
|
Assert.Equal("hello", packet.PostForms["x"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpRequest_RejectsOversizedContentBeforeBuffering()
|
|
{
|
|
var data = Encoding.ASCII.GetBytes(
|
|
"POST / HTTP/1.1\r\nContent-Length: 5\r\n\r\n");
|
|
var packet = new HttpRequestPacket { MaximumContentLength = 4 };
|
|
|
|
Assert.Throws<ParserLimitException>(() => packet.Parse(data, 0, (uint)data.Length));
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpResponse_ParsesBodyFromHeaderBoundaryAndUsesNegativeMissingCount()
|
|
{
|
|
var complete = Encoding.ASCII.GetBytes(
|
|
"HTTP/1.1 200 OK\r\nContent-Length: 5\r\nSet-Cookie: sid=abc; Path=/; HttpOnly\r\n\r\nhello");
|
|
var prefixed = new byte[complete.Length + 2];
|
|
Buffer.BlockCopy(complete, 0, prefixed, 2, complete.Length);
|
|
|
|
var packet = new HttpResponsePacket();
|
|
Assert.Equal(complete.Length, packet.Parse(prefixed, 2, (uint)prefixed.Length));
|
|
Assert.Equal("hello", Encoding.ASCII.GetString(packet.Message));
|
|
Assert.Single(packet.Cookies);
|
|
Assert.True(packet.Cookies[0].HttpOnly);
|
|
|
|
var incomplete = Encoding.ASCII.GetBytes(
|
|
"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhe");
|
|
Assert.Equal(-3, new HttpResponsePacket().Parse(incomplete, 0, (uint)incomplete.Length));
|
|
}
|
|
|
|
[Fact]
|
|
public void HttpResponse_ComposeCalculatesLengthAndAppendsBody()
|
|
{
|
|
var packet = new HttpResponsePacket
|
|
{
|
|
Text = "OK",
|
|
Message = Encoding.ASCII.GetBytes("hello")
|
|
};
|
|
|
|
packet.Compose(HttpComposeOption.AllCalculateLength);
|
|
var response = Encoding.ASCII.GetString(packet.Data);
|
|
|
|
Assert.Contains("Content-Length: 5\r\n", response, StringComparison.OrdinalIgnoreCase);
|
|
Assert.EndsWith("\r\n\r\nhello", response, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void StringKeyList_LookupsRemainCaseInsensitiveWithoutDuplicates()
|
|
{
|
|
var values = new StringKeyList();
|
|
values.Add("Content-Type", "text/plain");
|
|
values["CONTENT-TYPE"] = "application/json";
|
|
|
|
Assert.Equal(1, values.Count);
|
|
Assert.Equal("application/json", values["content-type"]);
|
|
Assert.True(values.ContainsKey("Content-Type"));
|
|
}
|
|
}
|