Mult Protocol

This commit is contained in:
2026-08-16 15:22:03 -03:00
parent 96ea761b72
commit 1fb431ccba
17 changed files with 1873 additions and 204 deletions
+44 -78
View File
@@ -571,41 +571,40 @@ func openChunkTunnel(serverAddr, token, targetHost string, targetPort int, opts
// 0 = persistent (CLI explicit)
// 1 = auto: persistent when the path probe succeeds, otherwise one request/connection
// N>=2 = force connection rotation after N logical requests
if reconnect == 1 {
if profile.persistent {
reconnect = 0
fmt.Printf("path probe: reconnect mode auto -> persistent\n")
} else {
fmt.Printf("path probe: reconnect mode auto -> every request\n")
}
// Resolved silently: this runs once per proxied flow, so it must never log.
if reconnect == 1 && profile.persistent {
reconnect = 0
}
sid, err := randomSessionID()
if err != nil {
return nil, err
}
control := newRequestLane(serverAddr, opts.tcpBuffer, reconnect, opts.txnTimeout)
// OPEN rides the upload lane instead of a throwaway connection. A dedicated
// control connection cost one extra dial per proxied flow, which shows up on
// the server as connection churn on top of the steady-state count.
uploadLane := newRequestLane(serverAddr, opts.tcpBuffer, reconnect, opts.txnTimeout)
payload, err := encodeOpen(token, targetHost, targetPort)
if err != nil {
control.Close()
uploadLane.Close()
return nil, err
}
status, body, err := control.single(wire.ModeOpen, sid, 0, payload)
status, body, err := uploadLane.single(wire.ModeOpen, sid, 0, payload)
if err != nil {
control.Close()
uploadLane.Close()
return nil, err
}
if status == wire.StatusError {
control.Close()
uploadLane.Close()
return nil, fmt.Errorf("%s", string(body))
}
if status != wire.StatusOK || len(body) != 4 {
control.Close()
uploadLane.Close()
return nil, fmt.Errorf("bad OPEN response")
}
serverMax := int(binary.BigEndian.Uint32(body))
control.Close()
if serverMax < opts.minSize {
uploadLane.Close()
return nil, fmt.Errorf("server maximum chunk %d is below client minimum %d", serverMax, opts.minSize)
}
if opts.maxSize > serverMax {
@@ -624,13 +623,12 @@ func openChunkTunnel(serverAddr, token, targetHost string, targetPort int, opts
sid: sid,
opts: opts,
serverMax: serverMax,
uploadLane: newRequestLane(serverAddr, opts.tcpBuffer, reconnect, opts.txnTimeout),
uploadLane: uploadLane,
downloadLane: newRequestLane(serverAddr, opts.tcpBuffer, reconnect, opts.txnTimeout),
// Start at the user-configured ceiling. On transport failures the
// pipeline is halved; successful data responses grow it back by one,
// always staying inside minPipeline..maxPipeline. When the two bounds
// are equal the depth is pinned and never adapts, which is what paths
// that only work at one specific batch size need.
// Start at the configured ceiling. On transport failure the batch is
// halved but never below minPipeline; successful data grows it back by
// one. When min == max the depth is pinned and never adapts, which is
// what paths that only work at one specific batch size need.
pipeline: opts.maxPipeline,
minPipeline: opts.minPipeline,
maxPipeline: opts.maxPipeline,
@@ -640,53 +638,6 @@ func openChunkTunnel(serverAddr, token, targetHost string, targetPort int, opts
return c, nil
}
// pinnedBatch reports whether the batch depth is fixed. A pinned depth never
// grows or shrinks: some paths only deliver correctly at one specific number of
// records per request, so the adaptive controller must stay out of the way.
func (c *chunkConn) pinnedBatch() bool { return c.minPipeline >= c.maxPipeline }
// batchCount is how many records the next download request will ask for.
func (c *chunkConn) batchCount(chunk int) int {
count := c.pipeline
if count < c.minPipeline {
count = c.minPipeline
}
if count > c.maxPipeline {
count = c.maxPipeline
}
// Bound each batch to roughly 1 MiB of useful data, but never below the
// configured floor: a pinned depth is a path requirement, not a hint.
if maxCount := (1024 * 1024) / maxInt(chunk, 1); maxCount < count {
count = maxInt(maxCount, c.minPipeline)
}
return count
}
// growPipeline widens the batch by one after a successful data response.
func (c *chunkConn) growPipeline() {
if c.pinnedBatch() {
return
}
if c.pipeline < c.maxPipeline {
c.pipeline++
}
}
// shrinkPipeline halves the batch after a transport failure. It reports the old
// and new depth, and whether anything actually changed; when it returns false
// the caller should shrink the record size instead.
func (c *chunkConn) shrinkPipeline() (int, int, bool) {
if c.pinnedBatch() || c.pipeline <= c.minPipeline {
return c.pipeline, c.pipeline, false
}
old := c.pipeline
c.pipeline /= 2
if c.pipeline < c.minPipeline {
c.pipeline = c.minPipeline
}
return old, c.pipeline, old != c.pipeline
}
func (c *chunkConn) fillReadBuffer() error {
if c.eof {
return io.EOF
@@ -694,7 +645,18 @@ func (c *chunkConn) fillReadBuffer() error {
minFailures := 0
for len(c.readBuf) == 0 && !c.eof {
chunk := c.downSizer.Current()
count := c.batchCount(chunk)
count := c.pipeline
if count < c.minPipeline {
count = c.minPipeline
}
if count > c.maxPipeline {
count = c.maxPipeline
}
// Bound each batch to roughly 1 MiB of useful data, but never below the
// configured floor: a pinned depth is a path requirement, not a hint.
if maxCount := (1024 * 1024) / maxInt(chunk, 1); maxCount < count {
count = maxInt(maxCount, c.minPipeline)
}
data, status, err := c.downloadLane.download(c.sid, c.downloadOffset, c.consumedOffset, chunk, count)
for _, part := range data {
@@ -703,16 +665,20 @@ func (c *chunkConn) fillReadBuffer() error {
}
if len(data) > 0 {
c.downSizer.Success(chunk)
c.growPipeline()
if c.pipeline < c.maxPipeline {
c.pipeline++
}
minFailures = 0
}
if err != nil {
// Shrink the batch first, then the record size. When the batch is
// pinned (min == max) the depth is left alone entirely and only the
// record size adapts.
if old, next, shrank := c.shrinkPipeline(); shrank {
if c.opts.adaptLog {
fmt.Printf("adaptive download batch: %d -> %d after transport failure\n", old, next)
if c.pipeline > c.minPipeline {
old := c.pipeline
c.pipeline /= 2
if c.pipeline < c.minPipeline {
c.pipeline = c.minPipeline
}
if c.opts.adaptLog && old != c.pipeline {
fmt.Printf("adaptive download pipeline: %d -> %d after transport failure\n", old, c.pipeline)
}
} else {
old, next := c.downSizer.Failure(chunk)
@@ -808,9 +774,9 @@ func (c *chunkConn) Write(p []byte) (int, error) {
func (c *chunkConn) Close() error {
c.closeOnce.Do(func() {
lane := newRequestLane(c.uploadLane.serverAddr, c.opts.tcpBuffer, 1, c.opts.txnTimeout)
_, _, _ = lane.single(wire.ModeClose, c.sid, 0, nil)
lane.Close()
// Reuse the upload lane rather than dialling a connection just to say
// goodbye; that was a second wasted dial per flow.
_, _, _ = c.uploadLane.single(wire.ModeClose, c.sid, 0, nil)
c.uploadLane.Close()
c.downloadLane.Close()
})