New
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"dragontcp/internal/cover"
|
||||
"dragontcp/internal/protocol"
|
||||
)
|
||||
|
||||
@@ -57,6 +58,24 @@ type Options struct {
|
||||
pollDelay time.Duration
|
||||
txnTimeout time.Duration
|
||||
tcpBuffer int
|
||||
headerMask byte
|
||||
coverProfile cover.Profile
|
||||
}
|
||||
|
||||
// WithHeaderMask returns a copy using one fixed frame-magic profile. The mask
|
||||
// is selected during startup discovery and remains unchanged for normal data.
|
||||
func (o Options) WithHeaderMask(mask byte) Options {
|
||||
o.headerMask = mask
|
||||
o.coverProfile = cover.Profile{}
|
||||
return o
|
||||
}
|
||||
|
||||
// WithCoverProfile returns a copy using a fixed startup-selected preface,
|
||||
// padding length, and frame mask.
|
||||
func (o Options) WithCoverProfile(profile cover.Profile) Options {
|
||||
o.coverProfile = profile
|
||||
o.headerMask = profile.HeaderMask
|
||||
return o
|
||||
}
|
||||
|
||||
func wireToken(token string) string {
|
||||
@@ -219,17 +238,21 @@ type txnLane struct {
|
||||
tcpBuffer int
|
||||
reconnectEvery int
|
||||
timeout time.Duration
|
||||
headerMask byte
|
||||
coverProfile cover.Profile
|
||||
conn net.Conn
|
||||
count int
|
||||
closed bool
|
||||
}
|
||||
|
||||
func newTxnLane(serverAddr string, tcpBuffer, reconnectEvery int, timeout time.Duration) *txnLane {
|
||||
func newTxnLane(serverAddr string, tcpBuffer, reconnectEvery int, timeout time.Duration, headerMask byte, coverProfile cover.Profile) *txnLane {
|
||||
return &txnLane{
|
||||
serverAddr: serverAddr,
|
||||
tcpBuffer: tcpBuffer,
|
||||
reconnectEvery: reconnectEvery,
|
||||
timeout: timeout,
|
||||
headerMask: headerMask,
|
||||
coverProfile: coverProfile,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,6 +285,10 @@ func (l *txnLane) ensureConn() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cover.WritePreface(conn, l.coverProfile); err != nil {
|
||||
_ = conn.Close()
|
||||
return err
|
||||
}
|
||||
protocol.TuneTCP(conn)
|
||||
protocol.TuneTCPBuffer(conn, l.tcpBuffer)
|
||||
l.conn = conn
|
||||
@@ -285,12 +312,12 @@ func (l *txnLane) Do(payload []byte) ([]byte, error) {
|
||||
_ = l.conn.SetDeadline(time.Now().Add(timeout))
|
||||
requestID := requestCounter.Add(1)
|
||||
|
||||
if err := protocol.WriteRequestFrame(l.conn, requestID, payload); err != nil {
|
||||
if err := protocol.WriteRequestFrameProfile(l.conn, requestID, payload, l.headerMask); err != nil {
|
||||
l.closeLocked()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responseID, response, err := protocol.ReadResponseFrame(l.conn)
|
||||
responseID, response, err := protocol.ReadResponseFrameProfile(l.conn, l.headerMask)
|
||||
if err != nil {
|
||||
l.closeLocked()
|
||||
return nil, err
|
||||
@@ -324,6 +351,28 @@ func doControl(lane *txnLane, payload []byte) ([]byte, error) {
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
// ProbeProfile performs one small authenticated transaction using the selected
|
||||
// frame-magic mask. It does not create a target session.
|
||||
func ProbeProfile(serverAddr, token string, opts Options) bool {
|
||||
timeout := opts.txnTimeout
|
||||
if timeout <= 0 || timeout > 2*time.Second {
|
||||
timeout = 2 * time.Second
|
||||
}
|
||||
lane := newTxnLane(serverAddr, opts.tcpBuffer, 1, timeout, opts.headerMask, opts.coverProfile)
|
||||
defer lane.Close()
|
||||
resp, err := lane.Do([]byte("CPROBE " + wireToken(token)))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if string(resp) == "PROBEOK" {
|
||||
return true
|
||||
}
|
||||
// Servers predating profile discovery do not know CPROBE, but receiving a
|
||||
// correctly framed error still proves that the legacy mask-zero header
|
||||
// survived. The subsequent end-to-end probe remains authoritative.
|
||||
return opts.headerMask == 0 && strings.HasPrefix(string(resp), "ERR expected TUNNEL")
|
||||
}
|
||||
|
||||
type chunkResult struct {
|
||||
seq uint64
|
||||
data []byte
|
||||
@@ -425,7 +474,7 @@ func Open(serverAddr, token, targetHost string, targetPort int, opts Options) (n
|
||||
c.upSizer = newAdaptiveSizer("upload", opts)
|
||||
c.downSizer = newAdaptiveSizer("download", opts)
|
||||
|
||||
c.pushLane = newTxnLane(serverAddr, opts.tcpBuffer, opts.reconnectEvery, opts.txnTimeout)
|
||||
c.pushLane = newTxnLane(serverAddr, opts.tcpBuffer, opts.reconnectEvery, opts.txnTimeout, opts.headerMask, opts.coverProfile)
|
||||
|
||||
openPayload := []byte(fmt.Sprintf(
|
||||
"COPEN %s %s %s %d",
|
||||
@@ -464,7 +513,7 @@ func Open(serverAddr, token, targetHost string, targetPort int, opts Options) (n
|
||||
|
||||
c.pullLanes = make([]*txnLane, opts.pollers)
|
||||
for i := 0; i < opts.pollers; i++ {
|
||||
lane := newTxnLane(serverAddr, opts.tcpBuffer, opts.reconnectEvery, opts.txnTimeout)
|
||||
lane := newTxnLane(serverAddr, opts.tcpBuffer, opts.reconnectEvery, opts.txnTimeout, opts.headerMask, opts.coverProfile)
|
||||
c.pullLanes[i] = lane
|
||||
c.workers.Add(1)
|
||||
go c.pullWorker(lane)
|
||||
@@ -762,7 +811,7 @@ func (c *chunkConn) Close() error {
|
||||
c.once.Do(func() {
|
||||
c.cancel()
|
||||
|
||||
lane := newTxnLane(c.serverAddr, c.opts.tcpBuffer, 1, c.opts.txnTimeout)
|
||||
lane := newTxnLane(c.serverAddr, c.opts.tcpBuffer, 1, c.opts.txnTimeout, c.opts.headerMask, c.opts.coverProfile)
|
||||
_, _ = doControl(lane, []byte(fmt.Sprintf("CCLOSE %s %s", wireToken(c.token), c.sid)))
|
||||
lane.Close()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user