add credit based mux window
This commit is contained in:
+45
-14
@@ -24,6 +24,8 @@ type Client struct {
|
||||
mappings map[string]Mapping // normalized pattern -> mapping
|
||||
pool *WorkerPool
|
||||
|
||||
streamWnd int // our advertised per-stream receive window (bytes)
|
||||
|
||||
mu sync.Mutex
|
||||
ctrl *wire.FramedConn
|
||||
}
|
||||
@@ -44,16 +46,32 @@ func New(cfg *Config) *Client {
|
||||
for _, m := range cfg.Mappings {
|
||||
c.mappings[NormalizeAddress(m.Pattern)] = m
|
||||
}
|
||||
c.streamWnd = clampWindow(cfg.StreamWindowBytes)
|
||||
c.pool = newWorkerPool(c, cfg.MaxConn)
|
||||
return c
|
||||
}
|
||||
|
||||
func clampWindow(w int) int {
|
||||
if w <= 0 {
|
||||
return DefaultStreamWindow
|
||||
}
|
||||
if w < MinStreamWindow {
|
||||
return MinStreamWindow
|
||||
}
|
||||
if w > MaxStreamWindow {
|
||||
return MaxStreamWindow
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// dialSession opens a TCP connection, performs the Intent-17 handshake, the
|
||||
// Phase-A rekey, and reads SessionReady, returning an established frame conn.
|
||||
func (c *Client) dialSession(magic byte) (*wire.FramedConn, error) {
|
||||
// Phase-A rekey, and reads SessionReady, returning an established frame conn
|
||||
// and the hub's advertised per-stream receive window. Per-stream flow control
|
||||
// is mandatory: a hub that does not echo the STREAM_FC flag is rejected.
|
||||
func (c *Client) dialSession(magic byte) (fc *wire.FramedConn, peerWnd int, err error) {
|
||||
conn, err := net.DialTimeout("tcp", c.cfg.Server, 10*time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
if tcp, ok := conn.(*net.TCPConn); ok {
|
||||
_ = tcp.SetNoDelay(true)
|
||||
@@ -68,24 +86,26 @@ func (c *Client) dialSession(magic byte) (*wire.FramedConn, error) {
|
||||
// 1. plaintext Minecraft Handshake, Intent 17, address = hex(SHA3-224(PSK)).
|
||||
hs := wire.BuildHandshake(ProtocolVersion, c.pskAddr, c.serverPort, IntentRedapricot)
|
||||
if _, err := conn.Write(hs); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 2. Phase-A ciphers derived from the PSK.
|
||||
fc := wire.NewFramedConn(conn,
|
||||
fc = wire.NewFramedConn(conn,
|
||||
wire.CipherFor(c.pskBytes, wire.DirS2C), // in: server -> client
|
||||
wire.CipherFor(c.pskBytes, wire.DirC2S), // out: client -> server
|
||||
)
|
||||
|
||||
// 3. Rekey frame (Phase A).
|
||||
// 3. Rekey frame (Phase A), including the mandatory feature flags and our
|
||||
// per-stream receive window.
|
||||
rnd := make([]byte, 16)
|
||||
if _, err := crand.Read(rnd); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
ts := time.Now().UnixMilli()
|
||||
rekeyMsg := wire.NewWriter().U8(magic).VarInt(len(rnd)).Bytes(rnd).I64(ts).Out()
|
||||
rekeyMsg := wire.NewWriter().U8(magic).VarInt(len(rnd)).Bytes(rnd).I64(ts).
|
||||
VarInt(FlagStreamFC).VarInt(c.streamWnd).Out()
|
||||
if err := fc.WriteFrame(rekeyMsg); err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 4. Switch to Phase-B ciphers: REKEY = Rand || Timestamp(I64 BE).
|
||||
@@ -99,16 +119,26 @@ func (c *Client) dialSession(magic byte) (*wire.FramedConn, error) {
|
||||
wire.CipherFor(rekey, wire.DirC2S),
|
||||
)
|
||||
|
||||
// 5. SessionReady.
|
||||
// 5. SessionReady: the type byte followed by the hub's accepted flags and
|
||||
// its per-stream receive window. Both are required.
|
||||
payload, err := fc.ReadFrame()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, 0, err
|
||||
}
|
||||
if len(payload) < 1 || payload[0] != CtlSessionReady {
|
||||
return nil, fmt.Errorf("expected SessionReady, got %v", payload)
|
||||
return nil, 0, fmt.Errorf("expected SessionReady, got %v", payload)
|
||||
}
|
||||
r := wire.NewReader(payload[1:])
|
||||
flags, ferr := r.VarInt()
|
||||
hubWnd, werr := r.VarInt()
|
||||
if ferr != nil || werr != nil || flags&FlagStreamFC == 0 || hubWnd <= 0 {
|
||||
return nil, 0, fmt.Errorf("hub did not accept per-stream flow control (unsupported hub version?)")
|
||||
}
|
||||
if hubWnd > MaxStreamWindow {
|
||||
hubWnd = MaxStreamWindow
|
||||
}
|
||||
ok = true
|
||||
return fc, nil
|
||||
return fc, hubWnd, nil
|
||||
}
|
||||
|
||||
// Start establishes the control session and registers all patterns. It returns
|
||||
@@ -119,7 +149,7 @@ func (c *Client) Start(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (c *Client) connectControl(ctx context.Context) error {
|
||||
fc, err := c.dialSession(MagicControl)
|
||||
fc, _, err := c.dialSession(MagicControl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("control connect: %w", err)
|
||||
}
|
||||
@@ -233,6 +263,7 @@ func (c *Client) handleControlRequest(cid []byte, pattern, ip string, port int,
|
||||
st := newStream(wc, sid, cid, mapping, ip, port)
|
||||
wc.registerStream(sid, st)
|
||||
wc.sendSyn(sid, cid)
|
||||
go st.writeLoop()
|
||||
go st.run()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user