Initial open source release

This commit is contained in:
A 2026-06-04 01:37:39 +08:00
commit 74992e893f
377 changed files with 118084 additions and 0 deletions

View file

@ -0,0 +1,114 @@
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 入队时才起 worker(ensureInboundRPCWorkers),
// 避免握手后静默 / 纯推送目标连接白白钉住 rpcMaxInflight 个 goroutine。
rpcRootCtx context.Context
rpcMaxInflight int
rpcWorkersOnce sync.Once
// sentContentMessages 只由 outbound actor 访问,用于生成 MTProto seq_no。
sentContentMessages int32
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) }