owpengram-server/internal/mtprotoedge/conn.go
A 091d8f084b mtproto: add compat transport quick ack support
(cherry picked from commit d051bc37bd14076fdd0a83ad41cd507929b20ece)
2026-06-26 13:27:11 +08:00

117 lines
4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package mtprotoedge
import (
"context"
"sync"
"sync/atomic"
"time"
"github.com/gotd/td/bin"
"github.com/gotd/td/crypto"
"github.com/gotd/td/proto"
"github.com/gotd/td/transport"
)
// Conn 是一个已识别 session 的客户端连接,持有向其加密发送消息所需的全部上下文。
// 由 SessionManager 管理,供请求响应与主动 push 共用。
//
// Send 并发安全:所有出站消息先进 per-Conn outbound actor由它串行分配 msg_id/seq_no、
// 加密并写 transport避免高并发 RPC 响应与 push 交错造成 MTProto 顺序错误。
type outboundWriter interface {
Send(context.Context, *bin.Buffer) error
}
type Conn struct {
transport transport.Conn
writer outboundWriter
cipher crypto.Cipher
msgID *proto.MessageIDGen
writeTimeout time.Duration
metrics Metrics
authKeyID [8]byte
sessionID int64
salt int64
key crypto.AuthKey
outbound chan outboundOp
outboundControl chan outboundOp
outboundStop chan struct{}
outboundDone chan struct{}
outboundClose sync.Once
rpcQueue chan inboundRPC
rpcStop chan struct{}
rpcCancel context.CancelFunc
rpcClose sync.Once
rpcWG sync.WaitGroup
rpcTimeout time.Duration
// inflightRPCBytes 跟踪已入队未完成的 inbound RPC body 总字节,配合 maxInflightRPCBytes
// 给 RPC 队列设字节预算(不止限条数),防对抗客户端发大请求撑内存。
inflightRPCBytes atomic.Int64
// RPC worker 懒启动:首个 RPC 入队时才起 workerensureInboundRPCWorkers
// 避免握手后静默 / 纯推送目标连接白白钉住 rpcMaxInflight 个 goroutine。
rpcRootCtx context.Context
rpcMaxInflight int
rpcWorkersOnce sync.Once
// sentContentMessages 只由 outbound actor 访问,用于生成 MTProto seq_no。
sentContentMessages int32
// outboundPlain/outboundWire 只由 outbound actor 访问,用于复用出站加密缓冲。
outboundPlain bin.Buffer
outboundWire bin.Buffer
identityMu sync.RWMutex
businessAuthKeyID [8]byte
businessAuthKeyResolved bool
userID atomic.Int64
userIDResolved atomic.Bool
receivesUpdates atomic.Bool
}
// AuthKeyID 返回连接的 auth_key_id。
func (c *Conn) AuthKeyID() [8]byte { return c.authKeyID }
// BusinessAuthKeyID 返回业务视角的 auth_key_id。
//
// temp auth_key 绑定后解析为 perm auth_key第二个返回值表示本连接是否已完成解析
// 即便解析结果等于原始 auth_key_id 也会返回 true以避免每个 RPC 重复查绑定表。
func (c *Conn) BusinessAuthKeyID() ([8]byte, bool) {
c.identityMu.RLock()
defer c.identityMu.RUnlock()
return c.businessAuthKeyID, c.businessAuthKeyResolved
}
// SetBusinessAuthKeyID 缓存业务视角 auth_key_id。
func (c *Conn) SetBusinessAuthKeyID(id [8]byte) {
c.identityMu.Lock()
changed := !c.businessAuthKeyResolved || c.businessAuthKeyID != id
c.businessAuthKeyID = id
c.businessAuthKeyResolved = true
c.identityMu.Unlock()
if changed {
c.userID.Store(0)
c.userIDResolved.Store(false)
}
}
// SessionID 返回连接的 session_id。
func (c *Conn) SessionID() int64 { return c.sessionID }
// UserID 返回绑定的用户 id未登录为 0。
func (c *Conn) UserID() int64 { return c.userID.Load() }
// UserIDResolved 返回 user_id 授权状态是否已为当前连接解析过。
//
// resolved=true 且 userID=0 表示该 auth_key 当前未登录;这样登录前的多次 RPC
// 不会反复查询授权表,后续登录成功会由 BindUser 覆盖为真实用户。
func (c *Conn) UserIDResolved() (userID int64, resolved bool) {
return c.userID.Load(), c.userIDResolved.Load()
}
// ReceivesUpdates 报告该连接是否接收主动推送的 updates。
func (c *Conn) ReceivesUpdates() bool { return c.receivesUpdates.Load() }
// SetReceivesUpdates 设置该连接是否接收主动推送的 updates。
// 登录后的主连接在 updates.getState/getDifference 建立同步基线后置为 true。
func (c *Conn) SetReceivesUpdates(v bool) { c.receivesUpdates.Store(v) }