Livt packages

Livt.Web

Version 1.0.0

Bounded HTTP routing, request parsing, response encoding, and Ethernet application composition for Livt.

Published files (63)

tests/http/HttpNetworkEndpointTest.lvt

namespace Livt.Web.Tests.Http

using Livt.Net
using Livt.Web.Http

@Test
component HttpNetworkEndpointTest
{
	const RX[658] = [
		0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x08, 0x00, 0x45, 0x00,
		0x00, 0x28, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x26, 0xCD, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00,
		0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
		0x04, 0x00, 0x63, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB, 0xCC,
		0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x08, 0x00, 0x45, 0x00, 0x00, 0x28, 0x00, 0x01,
		0x40, 0x00, 0x40, 0x06, 0x26, 0xCD, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x01, 0x30, 0x39,
		0x00, 0x50, 0x01, 0x02, 0x03, 0x05, 0x00, 0x00, 0x00, 0x02, 0x50, 0x10, 0x04, 0x00, 0x63, 0x40,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10,
		0x20, 0x30, 0x40, 0x50, 0x08, 0x00, 0x45, 0x00, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06,
		0x26, 0xB5, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0x01, 0x02,
		0x03, 0x05, 0x00, 0x00, 0x00, 0x02, 0x50, 0x18, 0x04, 0x00, 0x28, 0x36, 0x00, 0x00, 0x47, 0x45,
		0x54, 0x20, 0x2F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31,
		0x2E, 0x30, 0x0D, 0x0A, 0x0D, 0x0A, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30,
		0x40, 0x50, 0x08, 0x00, 0x45, 0x00, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x26, 0xB4,
		0x0A, 0x00, 0x00, 0x03, 0x0A, 0x00, 0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0x01, 0x02, 0x03, 0x05,
		0x00, 0x00, 0x00, 0x02, 0x50, 0x18, 0x04, 0x00, 0x28, 0x35, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20,
		0x2F, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30,
		0x0D, 0x0A, 0x0D, 0x0A, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50,
		0x08, 0x00, 0x45, 0x00, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x26, 0xB5, 0x0A, 0x00,
		0x00, 0x02, 0x0A, 0x00, 0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0x01, 0x02, 0x03, 0x05, 0x00, 0x00,
		0x00, 0x02, 0x50, 0x18, 0x04, 0x00, 0x28, 0x36, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2F, 0x73,
		0x74, 0x61, 0x74, 0x75, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50,
		0x08, 0x00, 0x45, 0x00, 0x00, 0x2D, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x26, 0xC8, 0x0A, 0x00,
		0x00, 0x02, 0x0A, 0x00, 0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0x01, 0x02, 0x03, 0x05, 0x00, 0x00,
		0x00, 0x02, 0x50, 0x18, 0x04, 0x00, 0x98, 0xCD, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2F, 0x00,
		0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x08, 0x00, 0x45, 0x00,
		0x00, 0x28, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x26, 0xCD, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00,
		0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02,
		0x04, 0x00, 0x67, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB, 0xCC,
		0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04,
		0x00, 0x01, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10,
		0x20, 0x30, 0x40, 0x50, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x40, 0x00, 0x40, 0x01,
		0x26, 0xDB, 0x0A, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x01, 0x08, 0x00, 0x21, 0x68, 0x12, 0x34,
		0x00, 0x01, 0x61, 0x62, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50,
		0x08, 0x00, 0x45, 0x00, 0x00, 0x40, 0x00, 0x01, 0x40, 0x00, 0x40, 0x06, 0x26, 0xB5, 0x0A, 0x00,
		0x00, 0x02, 0x0A, 0x00, 0x00, 0x01, 0x30, 0x39, 0x00, 0x50, 0x01, 0x02, 0x03, 0x05, 0x00, 0x00,
		0x00, 0x02, 0x50, 0x18, 0x04, 0x00, 0xD7, 0x36, 0x00, 0x00, 0x47, 0x45, 0x54, 0x20, 0x2F, 0x73,
		0x74, 0x61, 0x74, 0x75, 0x73, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x0D, 0x0A,
		0x0D, 0x0A]
	const RX_OFFSETS[10] = [0, 60, 120, 198, 276, 340, 400, 460, 520, 580]
	const RX_LENGTHS[10] = [60, 60, 78, 78, 64, 60, 60, 60, 60, 78]
	const TX[386] = [
		0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x08, 0x00, 0x45, 0x00,
		0x00, 0x28, 0x00, 0x02, 0x40, 0x00, 0x40, 0x06, 0x26, 0xCC, 0x0A, 0x00, 0x00, 0x01, 0x0A, 0x00,
		0x00, 0x02, 0x00, 0x50, 0x30, 0x39, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x50, 0x12,
		0x04, 0x00, 0x63, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x20, 0x30,
		0x40, 0x50, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x08, 0x00, 0x45, 0x00, 0x00, 0x84, 0x00, 0x01,
		0x40, 0x00, 0x40, 0x06, 0x26, 0x71, 0x0A, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x50,
		0x30, 0x39, 0x00, 0x00, 0x00, 0x02, 0x01, 0x02, 0x03, 0x1D, 0x50, 0x19, 0x04, 0x00, 0x49, 0xFB,
		0x00, 0x00, 0x48, 0x54, 0x54, 0x50, 0x2F, 0x31, 0x2E, 0x30, 0x20, 0x32, 0x30, 0x30, 0x20, 0x4F,
		0x4B, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x54, 0x79, 0x70, 0x65, 0x3A,
		0x20, 0x61, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x6A, 0x73, 0x6F,
		0x6E, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2D, 0x4C, 0x65, 0x6E, 0x67, 0x74,
		0x68, 0x3A, 0x20, 0x33, 0x0D, 0x0A, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E,
		0x3A, 0x20, 0x63, 0x6C, 0x6F, 0x73, 0x65, 0x0D, 0x0A, 0x0D, 0x0A, 0x5B, 0x37, 0x5D, 0x02, 0x10,
		0x20, 0x30, 0x40, 0x50, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00,
		0x06, 0x04, 0x00, 0x02, 0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x0A, 0x00, 0x00, 0x01, 0x02, 0x10,
		0x20, 0x30, 0x40, 0x50, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50,
		0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x08, 0x00, 0x45, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x40, 0x00,
		0x40, 0x01, 0x26, 0xDB, 0x0A, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x29, 0x68,
		0x12, 0x34, 0x00, 0x01, 0x61, 0x62, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10, 0x20, 0x30, 0x40, 0x50, 0x02, 0xAA, 0xBB, 0xCC,
		0xDD, 0xEE, 0x08, 0x00, 0x45, 0x00, 0x00, 0x28, 0x00, 0x02, 0x40, 0x00, 0x40, 0x06, 0x26, 0xCC,
		0x0A, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, 0x02, 0x00, 0x50, 0x30, 0x39, 0x00, 0x00, 0x00, 0x01,
		0x00, 0x00, 0x00, 0x00, 0x50, 0x12, 0x04, 0x00, 0x67, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00]
	const TX_OFFSETS[5] = [0, 60, 206, 266, 326]
	const TX_LENGTHS[5] = [60, 146, 60, 60, 60]
	source: RamPacketData<256>
	ethernet: EthernetFrameParser<RamPacketData<256>>
	ipv4: Ipv4PacketParser<EthernetFrameParser<RamPacketData<256>>>
	connection: TcpConnectionRecognizer<RamPacketData<256>>
	request: HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>
	path: RoutingBytes
	counter: RoutingCounter
	handler: CounterContent
	matcher: HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>
	route: Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>
	router: HttpRouter<Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>>
	application: HttpApplication<TcpConnectionRecognizer<RamPacketData<256>>, Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>>
	endpoint: HttpNetworkEndpoint<RamPacketData<256>, HttpApplication<TcpConnectionRecognizer<RamPacketData<256>>, Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>>, 256>

	new()
	{
		var localMac: byte[6] = [0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE]
		var localIp: byte[4] = [0x0A, 0x00, 0x00, 0x01]
		var localPort: byte[2] = [0x00, 0x50]
		source = new RamPacketData<256>()
		ethernet = new EthernetFrameParser<RamPacketData<256>>(source)
		ipv4 = new Ipv4PacketParser<EthernetFrameParser<RamPacketData<256>>>(ethernet)
		connection = new TcpConnectionRecognizer<RamPacketData<256>>(ethernet, ipv4)
		request = new HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>(connection)
		path = new RoutingBytes()
		counter = new RoutingCounter()
		handler = new CounterContent(counter)
		matcher = new HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>(request, path)
		route = new Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>(HttpMethod.Get, request, matcher, handler)
		router = new HttpRouter<Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>>(route)
		application = new HttpApplication<TcpConnectionRecognizer<RamPacketData<256>>, Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>>(request, router)
		endpoint = new HttpNetworkEndpoint<RamPacketData<256>, HttpApplication<TcpConnectionRecognizer<RamPacketData<256>>, Route<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, HttpPath<HttpRequestParser<TcpConnectionRecognizer<RamPacketData<256>>>, RoutingBytes>, CounterContent>>, 256>(source, ethernet, ipv4, connection, application, localMac, localIp, localPort)
	}

	fn Reset()
	{
		endpoint.Abort()
		assert endpoint.TryRelease() == PacketDataResult.Success

		if (source.IsReady()) {
			assert source.TryRelease() == PacketDataResult.Success
		}
		else {
			assert source.TryClear() == PacketDataResult.Success
		}

		path.Select(1)
		counter.Set(7)
		handler.ResetCounts()
	}

	fn Load(which: int)
	{
		assert endpoint.TryRelease() == PacketDataResult.Success

		if (source.IsReady()) {
			assert source.TryRelease() == PacketDataResult.Success
		}
		else {
			assert source.TryClear() == PacketDataResult.Success
		}

		for (var i: int = 0; i < RX_LENGTHS[which]; i++) {
			assert source.TryWrite(i, RX[RX_OFFSETS[which] + i]) == PacketDataResult.Success
		}

		assert source.TryPublish() == PacketDataResult.Success
	}

	fn CheckFrame(which: int)
	{
		assert endpoint.GetAvailableLength() == TX_LENGTHS[which]

		for (var i: int = 0; i < TX_LENGTHS[which]; i++) {
			var value: byte = 0xFF
			assert endpoint.TryRead(i, value) == PacketDataResult.Success
			assert value == TX[TX_OFFSETS[which] + i]
		}
	}

	fn Finish()
	{
		assert endpoint.MarkEmitted()
		assert endpoint.Complete()
		assert endpoint.TryRelease() == PacketDataResult.Success
	}

	fn Handshake()
	{
		this.Load(0)
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		assert endpoint.GetResponseKind() == HttpNetworkResponse.TcpSynAck
		this.Finish()
	}

	fn PrepareHttp()
	{
		this.Load(2)
		assert endpoint.HandleFrame() == HttpTransportState.Pending
		assert endpoint.HandleFrame() == HttpTransportState.Pending
		assert endpoint.Poll() == HttpTransportState.Pending
		assert endpoint.Poll() == HttpTransportState.Prepared
		assert handler.GetStarts() == 1
	}

	@Test
	fn SynResponseAndRetention()
	{
		this.Reset()
		this.Load(0)
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		this.CheckFrame(0)
		assert !endpoint.Complete()
		assert endpoint.TryRelease() == PacketDataResult.Busy
		assert endpoint.MarkEmitted()
		assert endpoint.TryRelease() == PacketDataResult.Busy
		assert endpoint.IsReady()
		assert source.TryWrite(0, 0x00) == PacketDataResult.Busy
		assert endpoint.Complete()
		assert endpoint.TryRelease() == PacketDataResult.Success
		assert !connection.IsReady()
		assert handler.GetStarts() == 0
	}

	@Test
	fn CombinedAckRequestResponseAndRetainedSnapshot()
	{
		this.Reset()
		this.Handshake()
		this.PrepareHttp()
		assert endpoint.GetResponseKind() == HttpNetworkResponse.Http
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		this.CheckFrame(1)
		assert endpoint.MarkEmitted()
		assert application.GetState() == HttpApplicationState.Retained
		assert request.IsReady()
		assert endpoint.TryRelease() == PacketDataResult.Busy
		counter.Set(9)
		var value: byte = 0x00
		assert endpoint.TryRead(TX_LENGTHS[1] - 2, value) == PacketDataResult.Success
		assert value == 0x37
		assert endpoint.Complete()
		assert !application.IsReady()
		assert handler.GetEnds() == 1
		assert endpoint.TryRelease() == PacketDataResult.Success
		assert !request.IsReady()
		assert !connection.IsReady()
		assert source.IsReady()
	}

	@Test
	fn StandaloneAckThenHttp()
	{
		this.Reset()
		this.Handshake()
		this.Load(1)
		assert endpoint.HandleFrame() == HttpTransportState.Ignored
		this.PrepareHttp()
		this.CheckFrame(1)
		this.Finish()
	}

	@Test
	fn ArpUsesSharedCaptureWithoutHttp()
	{
		this.Reset()
		this.Load(7)
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		assert endpoint.GetResponseKind() == HttpNetworkResponse.Arp
		this.CheckFrame(2)
		this.Finish()
		assert handler.GetStarts() == 0
	}

	@Test
	fn IcmpUsesSharedCaptureWithoutHttp()
	{
		this.Reset()
		this.Load(8)
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		assert endpoint.GetResponseKind() == HttpNetworkResponse.IcmpEcho
		this.CheckFrame(3)
		this.Finish()
		assert handler.GetStarts() == 0
	}

	@Test
	fn TruncatedCaptureDoesNotReachApplication()
	{
		this.Reset()
		this.Handshake()
		this.Load(4)
		assert endpoint.HandleFrame() == HttpTransportState.Failed
		assert handler.GetStarts() == 0
		assert !endpoint.IsReady()
		assert endpoint.TryRelease() == PacketDataResult.Success
	}

	@Test
	fn IncompleteHttpPayloadDoesNotRunHandler()
	{
		this.Reset()
		this.Handshake()
		this.Load(5)
		assert endpoint.HandleFrame() == HttpTransportState.Failed
		assert application.GetRequestResult() == HttpRequestResult.Incomplete
		assert handler.GetStarts() == 0
		assert endpoint.TryRelease() == PacketDataResult.Success
	}

	@Test
	fn OtherPeerCannotUseHandshake()
	{
		this.Reset()
		this.Handshake()
		this.Load(3)
		assert endpoint.HandleFrame() == HttpTransportState.Ignored
		assert handler.GetStarts() == 0
		this.PrepareHttp()
		this.Finish()
	}

	@Test
	fn CancelPendingBeforeSourceReuse()
	{
		this.Reset()
		this.Handshake()
		this.Load(2)
		assert endpoint.HandleFrame() == HttpTransportState.Pending
		assert endpoint.TryRelease() == PacketDataResult.Busy
		endpoint.Abort()
		assert handler.GetEnds() == 1
		assert endpoint.TryRelease() == PacketDataResult.Success
		assert application.GetState() == HttpApplicationState.Idle
		assert !connection.IsReady()
		this.Load(2)
		assert endpoint.HandleFrame() == HttpTransportState.Ignored
	}

	@Test
	fn ChecksumPreparationFailurePublishesNoFrame()
	{
		this.Reset()
		this.Handshake()
		this.Load(2)
		assert endpoint.HandleFrame() == HttpTransportState.Pending
		assert endpoint.Poll() == HttpTransportState.Pending
		handler.FailRead()
		assert endpoint.Poll() == HttpTransportState.Failed
		assert !endpoint.IsReady()
		assert endpoint.GetAvailableLength() == 0
		assert handler.GetEnds() == 1
		assert endpoint.TryRelease() == PacketDataResult.Success
	}

	@Test
	fn EmissionFailureStopsResponse()
	{
		this.Reset()
		this.Handshake()
		this.PrepareHttp()
		handler.FailRead()
		var value: byte = 0xFF
		assert endpoint.TryRead(TX_LENGTHS[1] - 1, value) == PacketDataResult.Busy
		assert value == 0x00
		assert !endpoint.IsReady()
		assert endpoint.GetState() == HttpTransportState.Failed
		assert handler.GetEnds() == 1
		assert endpoint.TryRelease() == PacketDataResult.Success
	}

	@Test
	fn AbortRetainedResponseResetsSession()
	{
		this.Reset()
		this.Handshake()
		this.PrepareHttp()
		assert endpoint.MarkEmitted()
		endpoint.Abort()
		endpoint.Abort()
		assert endpoint.TryRelease() == PacketDataResult.Success
		assert handler.GetEnds() == 1
		assert source.IsReady()
		this.Load(2)
		assert endpoint.HandleFrame() == HttpTransportState.Ignored
	}

	@Test
	fn UnpublishedCaptureDoesNotStartWork()
	{
		this.Reset()
		assert endpoint.HandleFrame() == HttpTransportState.NotReady
		assert handler.GetStarts() == 0
		this.Load(0)
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		this.Finish()
	}

	@Test
	fn SequenceAcknowledgmentWraps()
	{
		this.Reset()
		this.Load(6)
		assert endpoint.HandleFrame() == HttpTransportState.Prepared
		this.CheckFrame(4)
		this.Finish()
	}

	@Test
	fn DocumentsCurrentUncheckedTcpChecksumPolicy()
	{
		this.Reset()
		this.Handshake()
		this.Load(9)
		// Known Net policy gap: structural parsing does not enforce receive checksum.
		assert endpoint.HandleFrame() == HttpTransportState.Pending
		assert handler.GetStarts() == 1
		endpoint.Abort()
		assert endpoint.TryRelease() == PacketDataResult.Success
	}
}