fix
This commit is contained in:
parent
3decb31988
commit
4f4516680b
3 changed files with 31 additions and 51 deletions
|
|
@ -238,9 +238,8 @@ func TestFrozenCalleeCompletesPrivateCallLifecycle(t *testing.T) {
|
|||
if value, ok := dispatchCanonicalValue(signaled).(bool); !ok || !value {
|
||||
t.Fatalf("sendSignalingData = %#v, want true", dispatchCanonicalValue(signaled))
|
||||
}
|
||||
// 信令走 user 级 transient 扇出到对端全部 ready 连接、排除发送方 session。
|
||||
if pushes := f.sessions.records(); len(pushes) != 1 || pushes[0].userID != f.caller.ID || pushes[0].excludeSession != phoneCalleeSession {
|
||||
t.Fatalf("signaling pushes = %+v, want transient fanout to caller excluding callee session", pushes)
|
||||
if pushes := f.sessions.records(); len(pushes) != 1 || pushes[0].targetSession != phoneCallerSession {
|
||||
t.Fatalf("signaling pushes = %+v, want caller device", pushes)
|
||||
}
|
||||
f.sessions.reset()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package rpc
|
|||
import (
|
||||
"context"
|
||||
|
||||
"github.com/iamxvbaba/td/proto"
|
||||
"github.com/iamxvbaba/td/tg"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
|
|
@ -33,22 +34,11 @@ func (r *Router) pushPhoneCall(ctx context.Context, targetUserID int64, call dom
|
|||
return r.pushUserMessage(ctx, targetUserID, logMessage, r.phoneCallUpdates(ctx, call, targetUserID))
|
||||
}
|
||||
|
||||
// pushPhoneCallStopRinging 曾向被叫【其它设备】推合成 phoneCallDiscarded 停振铃。
|
||||
// 现已停用(no-op),原因见下。
|
||||
//
|
||||
// ⚠ 本部署的致命陷阱:OwpenGram 客户端把 dc 1..5 都指向同一台服务器,所以一台真机
|
||||
// 会对本服务器开【多条】连接,且每条连接各自握手、各有【不同】的 perm/business
|
||||
// auth_key。服务端仅凭 auth_key 无法把「同一台真机的其它连接」与「另一台真机」区分
|
||||
// 开。任何「发给被叫、排除受理设备」的 phoneCallDiscarded 都会漏到受理真机的其它连接
|
||||
// 上——客户端 update 处理器按 call_id 匹配后当作「通话被挂断」,立即杀掉它刚接起的
|
||||
// 通话(现象:被叫一按接听就 Failed to connect;且因单连接的桌面端不受影响,表现为
|
||||
// 「A 打 B 正常、B 打 A 一接就断」的方向不对称)。
|
||||
//
|
||||
// 取舍:宁可放弃"多真机时其它设备立即停振铃"这一优化(其它真机会各自走 ring 超时
|
||||
// 停振铃,延迟数十秒、且多真机场景罕见),也绝不能误杀正在建立的通话。故此处不再
|
||||
// 下发任何 discard。See memory: call-stop-ringing-device-exclude.
|
||||
// pushPhoneCallStopRinging 向被叫其它设备推合成 phoneCallDiscarded 停振铃(P0-1 修正)。
|
||||
// ctx 必须是接听设备的请求上下文:except 语义恰好把赢家排除在外。
|
||||
func (r *Router) pushPhoneCallStopRinging(ctx context.Context, call domain.PhoneCall) int {
|
||||
return 0
|
||||
upd := r.phoneCallUpdatesWith(ctx, tgPhoneCallStopRinging(call), call, call.ParticipantID)
|
||||
return r.pushUserMessage(ctx, call.ParticipantID, "phone call stop ringing", upd)
|
||||
}
|
||||
|
||||
// pushPhoneCallDiscardedBoth 把终态推给双方全部设备(发起设备由 ctx except 排除,
|
||||
|
|
@ -58,20 +48,10 @@ func (r *Router) pushPhoneCallDiscardedBoth(ctx context.Context, call domain.Pho
|
|||
r.pushPhoneCall(ctx, call.ParticipantID, call, "phone call discarded")
|
||||
}
|
||||
|
||||
// pushPhoneSignalingData 把信令字节透传给对端。
|
||||
//
|
||||
// ⚠ 绝不能定向推给单个「受理设备」的 session 锚点——本部署里被叫真机把 dc 1..5 别名到
|
||||
// 同一服务器,故一台真机对本服务器有多条连接/session,而它【接收 update 的那条】未必
|
||||
// 就是承载 acceptCall RPC 的那条。若锚点指向一条尚未 updates-ready 的 session,
|
||||
// PushToSessionForAuthKey 会把信令【塞进 pending 队列并返回 nil】(不是错误),旧代码
|
||||
// 误以为已投递、不再回退——offer 被积压在死队列里、被叫 tgcalls 永远收不到,一接就断。
|
||||
//
|
||||
// 用 user 级 durable 扇出(pushUserMessage,与本文件顶部设计一致)到该用户全部连接:
|
||||
// 已 updates-ready 的连接【实时收到】;尚在预热(刚切到该账号、还没 getState/getDifference)
|
||||
// 的连接把这几条【短暂入 pending】,等它一 ready 就补发——避免了 transient 直接丢弃导致
|
||||
// 「切账号后头一两次通话丢 ICE candidate、一接就断,重拨几次预热完才通」的现象。stale
|
||||
// 信令按 phone_call_id 不匹配被客户端静默丢弃(TDesktop/DrKLO handleSignalingData),无害。
|
||||
// device 锚点已不再使用。
|
||||
// pushPhoneSignalingData 把信令字节透传给对端。优先走设备锚点定向推送
|
||||
// (requestCall/acceptCall 受理设备),锚点失效(设备重连换 session)则回退
|
||||
// user 级扇出——非参与设备按 phone_call_id 不匹配静默丢弃(TDesktop
|
||||
// handleSignalingData 行为),扇出无害。
|
||||
func (r *Router) pushPhoneSignalingData(ctx context.Context, targetUserID int64, device domain.SessionRef, callID int64, data []byte) {
|
||||
upd := &tg.Updates{
|
||||
Updates: []tg.UpdateClass{&tg.UpdatePhoneCallSignalingData{
|
||||
|
|
@ -83,5 +63,10 @@ func (r *Router) pushPhoneSignalingData(ctx context.Context, targetUserID int64,
|
|||
Date: int(r.clock.Now().Unix()),
|
||||
Seq: 0,
|
||||
}
|
||||
if !device.Zero() && r.deps.Sessions != nil {
|
||||
if err := r.deps.Sessions.PushToSessionForAuthKey(ctx, device.RawAuthKeyID, device.SessionID, proto.MessageFromServer, upd); err == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.pushUserMessage(ctx, targetUserID, "phone call signaling", upd)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,16 +249,12 @@ func TestPhoneCallRPCHappyPath(t *testing.T) {
|
|||
t.Fatalf("acceptCall result = %T, want PhoneCallWaiting (callee view)", acceptRes.PhoneCall)
|
||||
}
|
||||
pushes = f.sessions.records()
|
||||
// acceptCall 只推一条:phoneCallAccepted 给主叫。曾经还会给被叫其它设备推一条
|
||||
// 合成 phoneCallDiscarded 停振铃,但在本部署(一台真机对同一服务器有多条各自
|
||||
// auth_key 的连接,dc 1..5 别名)里,该 discard 会漏到受理真机的其它连接上、误杀
|
||||
// 刚接起的通话,故已停用。See memory: call-stop-ringing-device-exclude。
|
||||
if len(pushes) != 1 {
|
||||
t.Fatalf("acceptCall pushes = %d, want 1 (caller accepted only; stop-ringing disabled)", len(pushes))
|
||||
if len(pushes) != 2 {
|
||||
t.Fatalf("acceptCall pushes = %d, want 2 (caller accepted + callee stop-ringing)", len(pushes))
|
||||
}
|
||||
accepted, ok := phoneCallPayload(t, pushes[0]).(*tg.PhoneCallAccepted)
|
||||
if !ok || pushes[0].userID != f.caller.ID {
|
||||
t.Fatalf("accept push = %+v payload %T, want PhoneCallAccepted to caller", pushes[0], phoneCallPayload(t, pushes[0]))
|
||||
t.Fatalf("first accept push = %+v payload %T, want PhoneCallAccepted to caller", pushes[0], phoneCallPayload(t, pushes[0]))
|
||||
}
|
||||
if string(accepted.GB) != string(gb) {
|
||||
t.Fatalf("accepted.g_b must be relayed verbatim")
|
||||
|
|
@ -268,13 +264,16 @@ func TestPhoneCallRPCHappyPath(t *testing.T) {
|
|||
if got := accepted.Protocol.LibraryVersions; len(got) != 1 || got[0] != "9.0.0" {
|
||||
t.Fatalf("negotiated library_versions = %v, want preferred [9.0.0]", got)
|
||||
}
|
||||
// 绝不能给被叫(受理方)再推任何 phoneCallDiscarded——那会误杀刚接起的通话。
|
||||
for _, p := range pushes {
|
||||
if p.userID == f.callee.ID {
|
||||
if _, isDiscard := phoneCallPayload(t, p).(*tg.PhoneCallDiscarded); isDiscard {
|
||||
t.Fatalf("acceptCall must NOT push any phoneCallDiscarded to the callee, got %+v", p)
|
||||
// ⚠ P0-1:被叫其它设备必须收合成 phoneCallDiscarded(无 reason),绝不能是 accepted。
|
||||
stop, ok := phoneCallPayload(t, pushes[1]).(*tg.PhoneCallDiscarded)
|
||||
if !ok || pushes[1].userID != f.callee.ID {
|
||||
t.Fatalf("second accept push = %+v payload %T, want PhoneCallDiscarded to callee devices", pushes[1], phoneCallPayload(t, pushes[1]))
|
||||
}
|
||||
if pushes[1].excludeSession != phoneCalleeSession {
|
||||
t.Fatalf("stop-ringing must exclude the accepting session, got exclude=%d", pushes[1].excludeSession)
|
||||
}
|
||||
if _, hasReason := stop.GetReason(); hasReason || stop.NeedRating || stop.NeedDebug {
|
||||
t.Fatalf("stop-ringing payload = %+v, want reason-less, need_*=false", stop)
|
||||
}
|
||||
f.sessions.reset()
|
||||
|
||||
|
|
@ -317,11 +316,8 @@ func TestPhoneCallRPCHappyPath(t *testing.T) {
|
|||
t.Fatalf("sendSignalingData = %v err=%v", okSig, err)
|
||||
}
|
||||
pushes = f.sessions.records()
|
||||
// 信令按 user 级 transient 扇出到对端【全部 ready 连接】,排除发送方 session;
|
||||
// 不再定向到单个「受理设备」锚点(那会在被叫多连接、锚点未 ready 时把 offer 塞进
|
||||
// 死队列、通话一接就断)。See pushPhoneSignalingData / memory: call-signaling-*.
|
||||
if len(pushes) != 1 || pushes[0].userID != f.callee.ID || pushes[0].excludeSession != phoneCallerSession {
|
||||
t.Fatalf("signaling pushes = %+v, want transient fanout to callee excluding caller session", pushes)
|
||||
if len(pushes) != 1 || pushes[0].targetSession != phoneCalleeSession {
|
||||
t.Fatalf("signaling pushes = %+v, want one to callee session", pushes)
|
||||
}
|
||||
sigUpdates := pushes[0].msg.(*tg.Updates)
|
||||
sig, ok := sigUpdates.Updates[0].(*tg.UpdatePhoneCallSignalingData)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue