carry transport proto end-to-end through OPEN/OPEN_ACK

OPEN and OPEN_ACK now carry proto:1 alongside upstream_id:1. The
session is tagged with its transport proto and reliability semantics
switch on the specific proto:

- TCP (proto=1): full L2 reliability (seq, ack, retransmit, in-order)
- UDP (proto=2, reserved): best-effort (no retransmit, no ordering,
  no pure ACKs — seq/ack_seq fields present but ignored)

This is architecturally correct: instead of a generic 'reliable:bool'
flag, each proto gets the semantics it needs. Today only TCP exists so
every session is reliable, but the extension point is clean for when
stateless protos are added.

Updated: PROTOCOL.md, Rust frame.rs/session.rs/main.rs,
C# Frame.cs/SessionManager.cs.
This commit is contained in:
2026-08-13 09:15:15 +00:00
parent eb8994d1e1
commit 7fa50dd4e4
6 changed files with 129 additions and 68 deletions
+6 -6
View File
@@ -45,8 +45,8 @@ abstract record Frame
{
internal record Discover : Frame;
internal record Manifest(string Hostname, UpstreamEntry[] Entries) : Frame;
internal record Open(byte UpstreamId) : Frame;
internal record OpenAck(uint SessionId, byte UpstreamId) : Frame;
internal record Open(byte UpstreamId, byte Proto) : Frame;
internal record OpenAck(uint SessionId, byte UpstreamId, byte Proto) : Frame;
internal record OpenNak(byte UpstreamId, byte Reason) : Frame;
internal record Data(uint SessionId, uint Seq, uint AckSeq, byte[] Payload) : Frame;
internal record Close(uint SessionId, byte? Reason) : Frame;
@@ -106,9 +106,9 @@ static class FrameCodec
Frame.Manifest manifest =>
Build(Proto.TypeManifest, 0, BuildManifestPayload(manifest.Hostname, manifest.Entries)),
Frame.Open open =>
Build(Proto.TypeOpen, 0, [open.UpstreamId]),
Build(Proto.TypeOpen, 0, [open.UpstreamId, open.Proto]),
Frame.OpenAck ack =>
Build(Proto.TypeOpenAck, ack.SessionId, [ack.UpstreamId]),
Build(Proto.TypeOpenAck, ack.SessionId, [ack.UpstreamId, ack.Proto]),
Frame.OpenNak nak =>
Build(Proto.TypeOpenNak, 0, [nak.UpstreamId, nak.Reason]),
Frame.Data data =>
@@ -177,8 +177,8 @@ static class FrameCodec
return type switch
{
Proto.TypeManifest => ParseManifest(payload2),
Proto.TypeOpenAck when payload2.Length == 1 =>
new Frame.OpenAck(sessionId, payload2[0]),
Proto.TypeOpenAck when payload2.Length == 2 =>
new Frame.OpenAck(sessionId, payload2[0], payload2[1]),
Proto.TypeOpenNak when payload2.Length == 2 =>
new Frame.OpenNak(payload2[0], payload2[1]),
Proto.TypeClose when payload2.Length is 0 or 1 =>