fix(mtproto): sync accept TDLib reconnect service parity
This commit is contained in:
parent
c854fc7b94
commit
c13fbf8885
9 changed files with 176 additions and 41 deletions
|
|
@ -15,7 +15,7 @@ func TestEncryptedConnectionSwitchesAuthKeyEvenWhenSessionIDIsReused(t *testing.
|
||||||
_, authB, cipherB := dialHandshake(t, addr, dc, pub)
|
_, authB, cipherB := dialHandshake(t, addr, dc, pub)
|
||||||
msgID := proto.NewMessageIDGen(time.Now)
|
msgID := proto.NewMessageIDGen(time.Now)
|
||||||
|
|
||||||
sendEncrypted(t, connA, cipherA, authA, msgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
|
sendEncryptedWithSeq(t, connA, cipherA, authA, msgID.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 1})
|
||||||
for range 3 { // new_session_created + pong + msgs_ack; leave no A-key frame on the socket.
|
for range 3 { // new_session_created + pong + msgs_ack; leave no A-key frame on the socket.
|
||||||
readServerMessage(t, connA, cipherA, authA.AuthKey)
|
readServerMessage(t, connA, cipherA, authA.AuthKey)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1481,15 +1481,15 @@ func validateClientEnvelope(now time.Time, msgID int64, seqNo int32, typeID uint
|
||||||
if msgTime.After(now.Add(30 * time.Second)) {
|
if msgTime.After(now.Add(30 * time.Second)) {
|
||||||
return badMsgIDTooHigh
|
return badMsgIDTooHigh
|
||||||
}
|
}
|
||||||
if clientMessageAllowsEitherSeqParity(typeID) {
|
switch clientMessageContentPolicyFor(typeID) {
|
||||||
return 0
|
case clientMessageContentRequired:
|
||||||
}
|
|
||||||
if clientMessageNeedsAck(typeID) {
|
|
||||||
if seqNo%2 == 0 {
|
if seqNo%2 == 0 {
|
||||||
return badMsgSeqNotOdd
|
return badMsgSeqNotOdd
|
||||||
}
|
}
|
||||||
} else if seqNo%2 != 0 {
|
case clientMessageContentForbidden:
|
||||||
return badMsgSeqNotEven
|
if seqNo%2 != 0 {
|
||||||
|
return badMsgSeqNotEven
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
@ -1498,48 +1498,68 @@ func validateClientContainerEnvelope(msgID int64, seqNo int32, typeID uint32) in
|
||||||
if !validClientMessageIDBits(msgID) {
|
if !validClientMessageIDBits(msgID) {
|
||||||
return badMsgIDInvalidBits
|
return badMsgIDInvalidBits
|
||||||
}
|
}
|
||||||
if clientMessageAllowsEitherSeqParity(typeID) {
|
switch clientMessageContentPolicyFor(typeID) {
|
||||||
return 0
|
case clientMessageContentRequired:
|
||||||
}
|
|
||||||
if clientMessageNeedsAck(typeID) {
|
|
||||||
if seqNo%2 == 0 {
|
if seqNo%2 == 0 {
|
||||||
return badMsgSeqNotOdd
|
return badMsgSeqNotOdd
|
||||||
}
|
}
|
||||||
} else if seqNo%2 != 0 {
|
case clientMessageContentForbidden:
|
||||||
return badMsgSeqNotEven
|
if seqNo%2 != 0 {
|
||||||
|
return badMsgSeqNotEven
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func clientMessageAllowsEitherSeqParity(typeID uint32) bool {
|
type clientMessageContentPolicy uint8
|
||||||
switch typeID {
|
|
||||||
case mt.PingDelayDisconnectRequestTypeID,
|
|
||||||
// get_future_salts 的 seqno 奇偶在客户端间不一致:部分客户端按内容消息发奇数,
|
|
||||||
// gotd 按服务消息发偶数。两者都合法(官方服务器都接受),故不在此卡奇偶,避免
|
|
||||||
// 误判 bad_msg 触发客户端重连风暴。ack/content 行为仍由 clientMessageNeedsAck 决定。
|
|
||||||
mt.GetFutureSaltsRequestTypeID:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func clientMessageNeedsAck(typeID uint32) bool {
|
const (
|
||||||
|
clientMessageContentRequired clientMessageContentPolicy = iota + 1
|
||||||
|
clientMessageContentForbidden
|
||||||
|
clientMessageContentOptional
|
||||||
|
)
|
||||||
|
|
||||||
|
// clientMessageContentPolicyFor classifies the client envelope, not merely the
|
||||||
|
// constructor's usual sending convention. MTProto requires API RPCs to be
|
||||||
|
// content-related and requires containers/acknowledgements to be irrelevant,
|
||||||
|
// but clients may mark the other service constructors as either. TDLib uses
|
||||||
|
// even sequence numbers for its reconnect state/resend/cancel service batch,
|
||||||
|
// while gotd and DrKLO use odd sequence numbers for some of the same requests.
|
||||||
|
func clientMessageContentPolicyFor(typeID uint32) clientMessageContentPolicy {
|
||||||
switch typeID {
|
switch typeID {
|
||||||
case proto.MessageContainerTypeID,
|
case proto.MessageContainerTypeID,
|
||||||
mt.MsgsAckTypeID,
|
mt.MsgsAckTypeID,
|
||||||
|
mt.MsgCopyTypeID:
|
||||||
|
return clientMessageContentForbidden
|
||||||
|
case mt.PingRequestTypeID,
|
||||||
mt.PingDelayDisconnectRequestTypeID,
|
mt.PingDelayDisconnectRequestTypeID,
|
||||||
mt.DestroySessionRequestTypeID,
|
mt.GetFutureSaltsRequestTypeID,
|
||||||
mt.HTTPWaitRequestTypeID,
|
mt.MsgsStateReqTypeID,
|
||||||
mt.BadMsgNotificationTypeID,
|
mt.MsgResendReqTypeID,
|
||||||
mt.BadServerSaltTypeID,
|
|
||||||
mt.MsgsAllInfoTypeID,
|
mt.MsgsAllInfoTypeID,
|
||||||
mt.MsgsStateInfoTypeID,
|
mt.MsgsStateInfoTypeID,
|
||||||
|
mt.DestroySessionRequestTypeID,
|
||||||
|
mt.HTTPWaitRequestTypeID,
|
||||||
|
mt.RPCDropAnswerRequestTypeID,
|
||||||
|
mt.BadMsgNotificationTypeID,
|
||||||
|
mt.BadServerSaltTypeID,
|
||||||
mt.MsgDetailedInfoTypeID,
|
mt.MsgDetailedInfoTypeID,
|
||||||
mt.MsgNewDetailedInfoTypeID:
|
mt.MsgNewDetailedInfoTypeID,
|
||||||
return false
|
destroyAuthKeyRequestTypeID:
|
||||||
|
return clientMessageContentOptional
|
||||||
default:
|
default:
|
||||||
|
return clientMessageContentRequired
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientMessageIsContentRelated(typeID uint32, seqNo int32) bool {
|
||||||
|
switch clientMessageContentPolicyFor(typeID) {
|
||||||
|
case clientMessageContentRequired:
|
||||||
return true
|
return true
|
||||||
|
case clientMessageContentOptional:
|
||||||
|
return seqNo%2 != 0
|
||||||
|
default:
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,72 @@ func (h *durableDestroyLayerRPC) deletion() ([8]byte, int64) {
|
||||||
return h.authKeyID, h.sessionID
|
return h.authKeyID, h.sessionID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClientMessageContentPolicy(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
typeID uint32
|
||||||
|
want clientMessageContentPolicy
|
||||||
|
}{
|
||||||
|
{name: "api_rpc", typeID: tg.HelpGetConfigRequestTypeID, want: clientMessageContentRequired},
|
||||||
|
{name: "container", typeID: proto.MessageContainerTypeID, want: clientMessageContentForbidden},
|
||||||
|
{name: "msgs_ack", typeID: mt.MsgsAckTypeID, want: clientMessageContentForbidden},
|
||||||
|
{name: "msg_copy", typeID: mt.MsgCopyTypeID, want: clientMessageContentForbidden},
|
||||||
|
{name: "bad_msg_notification", typeID: mt.BadMsgNotificationTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "bad_server_salt", typeID: mt.BadServerSaltTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "msg_detailed_info", typeID: mt.MsgDetailedInfoTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "msg_new_detailed_info", typeID: mt.MsgNewDetailedInfoTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "ping", typeID: mt.PingRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "ping_delay_disconnect", typeID: mt.PingDelayDisconnectRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "get_future_salts", typeID: mt.GetFutureSaltsRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "msgs_state_req", typeID: mt.MsgsStateReqTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "msg_resend_req", typeID: mt.MsgResendReqTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "msgs_all_info", typeID: mt.MsgsAllInfoTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "msgs_state_info", typeID: mt.MsgsStateInfoTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "destroy_session", typeID: mt.DestroySessionRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "http_wait", typeID: mt.HTTPWaitRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "rpc_drop_answer", typeID: mt.RPCDropAnswerRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
{name: "destroy_auth_key", typeID: destroyAuthKeyRequestTypeID, want: clientMessageContentOptional},
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
msgID := proto.NewMessageIDGen(func() time.Time { return now }).New(proto.MessageFromClient)
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
if got := clientMessageContentPolicyFor(test.typeID); got != test.want {
|
||||||
|
t.Fatalf("content policy = %d, want %d", got, test.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
evenCode := validateClientContainerEnvelope(msgID, 8, test.typeID)
|
||||||
|
oddCode := validateClientContainerEnvelope(msgID, 9, test.typeID)
|
||||||
|
directEvenCode := validateClientEnvelope(now, msgID, 8, test.typeID)
|
||||||
|
directOddCode := validateClientEnvelope(now, msgID, 9, test.typeID)
|
||||||
|
if directEvenCode != evenCode || directOddCode != oddCode {
|
||||||
|
t.Fatalf(
|
||||||
|
"top-level/container parity mismatch = top(%d,%d) container(%d,%d)",
|
||||||
|
directEvenCode, directOddCode, evenCode, oddCode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
switch test.want {
|
||||||
|
case clientMessageContentRequired:
|
||||||
|
if evenCode != badMsgSeqNotOdd || oddCode != 0 {
|
||||||
|
t.Fatalf("required content parity codes = even:%d odd:%d", evenCode, oddCode)
|
||||||
|
}
|
||||||
|
case clientMessageContentForbidden:
|
||||||
|
if evenCode != 0 || oddCode != badMsgSeqNotEven {
|
||||||
|
t.Fatalf("forbidden content parity codes = even:%d odd:%d", evenCode, oddCode)
|
||||||
|
}
|
||||||
|
case clientMessageContentOptional:
|
||||||
|
if evenCode != 0 || oddCode != 0 {
|
||||||
|
t.Fatalf("optional content parity codes = even:%d odd:%d", evenCode, oddCode)
|
||||||
|
}
|
||||||
|
if clientMessageIsContentRelated(test.typeID, 8) || !clientMessageIsContentRelated(test.typeID, 9) {
|
||||||
|
t.Fatal("optional service content bit was not derived from seq_no parity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestEncryptedPingPong 验证 M2/M4:握手后 client 加密 ping,
|
// TestEncryptedPingPong 验证 M2/M4:握手后 client 加密 ping,
|
||||||
// server 回 new_session_created + pong + msgs_ack。
|
// server 回 new_session_created + pong + msgs_ack。
|
||||||
func TestEncryptedPingPong(t *testing.T) {
|
func TestEncryptedPingPong(t *testing.T) {
|
||||||
|
|
@ -79,7 +145,7 @@ func TestEncryptedPingPong(t *testing.T) {
|
||||||
clientMsgID := proto.NewMessageIDGen(time.Now)
|
clientMsgID := proto.NewMessageIDGen(time.Now)
|
||||||
const pingID int64 = 0x1234beef
|
const pingID int64 = 0x1234beef
|
||||||
pingMsgID := clientMsgID.New(proto.MessageFromClient)
|
pingMsgID := clientMsgID.New(proto.MessageFromClient)
|
||||||
sendEncrypted(t, conn, cipher, auth, pingMsgID, &mt.PingRequest{PingID: pingID})
|
sendEncryptedWithSeq(t, conn, cipher, auth, pingMsgID, 1, &mt.PingRequest{PingID: pingID})
|
||||||
|
|
||||||
replies := collectReplies(t, conn, cipher, auth.AuthKey, mt.PongTypeID)
|
replies := collectReplies(t, conn, cipher, auth.AuthKey, mt.PongTypeID)
|
||||||
mustHave(t, replies, mt.NewSessionCreatedTypeID, "new_session_created")
|
mustHave(t, replies, mt.NewSessionCreatedTypeID, "new_session_created")
|
||||||
|
|
@ -230,6 +296,55 @@ func TestMsgsStateReq(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestTDLibReconnectRecoveryContainerAcceptsEvenServiceMessages reproduces the
|
||||||
|
// first container TDLib emits after reopening an authenticated session with
|
||||||
|
// unknown queries. All service entries and the outer container use the current
|
||||||
|
// even sequence number. Rejecting msgs_state_req as a content-only constructor
|
||||||
|
// turns the valid inner message into bad_msg_notification(code=64), after which
|
||||||
|
// TDLib closes the session and retries the same container forever.
|
||||||
|
func TestTDLibReconnectRecoveryContainerAcceptsEvenServiceMessages(t *testing.T) {
|
||||||
|
const dc = 2
|
||||||
|
addr, pub, _ := startTestServer(t, Options{DC: dc})
|
||||||
|
conn, auth, cipher := dialHandshake(t, addr, dc, pub)
|
||||||
|
|
||||||
|
ids := proto.NewMessageIDGen(time.Now)
|
||||||
|
pendingMsgID := ids.New(proto.MessageFromClient)
|
||||||
|
ackMsgID := ids.New(proto.MessageFromClient)
|
||||||
|
stateMsgID := ids.New(proto.MessageFromClient)
|
||||||
|
pingMsgID := ids.New(proto.MessageFromClient)
|
||||||
|
outerMsgID := ids.New(proto.MessageFromClient)
|
||||||
|
const (
|
||||||
|
pendingSeqNo int32 = 9
|
||||||
|
serviceSeqNo int32 = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
pendingBody := mustEncodeTL(t, &mt.PingRequest{PingID: 0xc01d})
|
||||||
|
ackBody := mustEncodeTL(t, &mt.MsgsAck{MsgIDs: []int64{stateMsgID - 4}})
|
||||||
|
stateBody := mustEncodeTL(t, &mt.MsgsStateReq{MsgIDs: []int64{pendingMsgID, stateMsgID - 4}})
|
||||||
|
pingBody := mustEncodeTL(t, &mt.PingDelayDisconnectRequest{PingID: 0x5eed, DisconnectDelay: 60})
|
||||||
|
container := &proto.MessageContainer{Messages: []proto.Message{
|
||||||
|
{ID: pendingMsgID, SeqNo: int(pendingSeqNo), Bytes: len(pendingBody), Body: pendingBody},
|
||||||
|
{ID: ackMsgID, SeqNo: int(serviceSeqNo), Bytes: len(ackBody), Body: ackBody},
|
||||||
|
{ID: stateMsgID, SeqNo: int(serviceSeqNo), Bytes: len(stateBody), Body: stateBody},
|
||||||
|
{ID: pingMsgID, SeqNo: int(serviceSeqNo), Bytes: len(pingBody), Body: pingBody},
|
||||||
|
}}
|
||||||
|
|
||||||
|
sendEncryptedWithSeq(t, conn, cipher, auth, outerMsgID, serviceSeqNo, container)
|
||||||
|
frames := collectReplyFrames(t, conn, cipher, auth.AuthKey, map[uint32]int{
|
||||||
|
mt.MsgsStateInfoTypeID: 1,
|
||||||
|
mt.PongTypeID: 2,
|
||||||
|
})
|
||||||
|
for _, frame := range frames {
|
||||||
|
if frame.TypeID == mt.BadMsgNotificationTypeID {
|
||||||
|
var bad mt.BadMsgNotification
|
||||||
|
if err := bad.Decode(frame.Plain); err != nil {
|
||||||
|
t.Fatalf("decode bad_msg_notification: %v", err)
|
||||||
|
}
|
||||||
|
t.Fatalf("TDLib reconnect recovery container was rejected: %+v", bad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestMsgResendReq 验证 MTProto msg_resend_req 由连接层按状态查询兜底响应,
|
// TestMsgResendReq 验证 MTProto msg_resend_req 由连接层按状态查询兜底响应,
|
||||||
// 不会落入业务 RPC fallback。
|
// 不会落入业务 RPC fallback。
|
||||||
func TestMsgResendReq(t *testing.T) {
|
func TestMsgResendReq(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -1044,7 +1044,7 @@ func TestReconnectFakeReqPQThenEncryptedFrame(t *testing.T) {
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
msgGen := tgproto.NewMessageIDGen(time.Now)
|
msgGen := tgproto.NewMessageIDGen(time.Now)
|
||||||
sendEncrypted(t, conn, cipher, auth, msgGen.New(tgproto.MessageFromClient), &mt.PingRequest{PingID: 7})
|
sendEncryptedWithSeq(t, conn, cipher, auth, msgGen.New(tgproto.MessageFromClient), 1, &mt.PingRequest{PingID: 7})
|
||||||
|
|
||||||
var resPQFrame bin.Buffer
|
var resPQFrame bin.Buffer
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,7 @@ func encodeClientMessageForTest(t *testing.T, msg bin.Encoder) ([]byte, int32) {
|
||||||
if container, ok := msg.(*proto.MessageContainer); ok {
|
if container, ok := msg.(*proto.MessageContainer); ok {
|
||||||
return raw, clientContainerSeqNoForTest(container)
|
return raw, clientContainerSeqNoForTest(container)
|
||||||
}
|
}
|
||||||
if clientMessageNeedsAck(typeID) {
|
if clientMessageContentPolicyFor(typeID) == clientMessageContentRequired {
|
||||||
return raw, 1
|
return raw, 1
|
||||||
}
|
}
|
||||||
return raw, 0
|
return raw, 0
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@ func (s *Server) walkInbound(
|
||||||
return &dispatchBadMsgError{msgID: msgID, seqNo: seqNo, code: code}
|
return &dispatchBadMsgError{msgID: msgID, seqNo: seqNo, code: code}
|
||||||
}
|
}
|
||||||
|
|
||||||
content := clientMessageNeedsAck(typeID)
|
content := clientMessageIsContentRelated(typeID, seqNo)
|
||||||
if record, seen := overlay.seenRecord(msgID); seen {
|
if record, seen := overlay.seenRecord(msgID); seen {
|
||||||
if record.seqNo != seqNo || record.content != content {
|
if record.seqNo != seqNo || record.content != content {
|
||||||
return &dispatchBadMsgError{msgID: msgID, seqNo: seqNo, code: badMsgContainer}
|
return &dispatchBadMsgError{msgID: msgID, seqNo: seqNo, code: badMsgContainer}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func TestRegisterSeedsNegotiatedLayerBeforeFirstRPC(t *testing.T) {
|
||||||
conn, auth, cipher := dialHandshake(t, addr, 2, pub)
|
conn, auth, cipher := dialHandshake(t, addr, 2, pub)
|
||||||
|
|
||||||
clientMsgID := proto.NewMessageIDGen(time.Now)
|
clientMsgID := proto.NewMessageIDGen(time.Now)
|
||||||
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 7})
|
sendEncryptedWithSeq(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 7})
|
||||||
|
|
||||||
// 等 pong 回来,确保携带注册动作的那一帧已处理完成。
|
// 等 pong 回来,确保携带注册动作的那一帧已处理完成。
|
||||||
gotPong := false
|
gotPong := false
|
||||||
|
|
|
||||||
|
|
@ -544,7 +544,7 @@ func TestOutboundActorSerializesConcurrentSends(t *testing.T) {
|
||||||
conn, auth, cipher := dialHandshake(t, addr, dc, pub)
|
conn, auth, cipher := dialHandshake(t, addr, dc, pub)
|
||||||
|
|
||||||
clientMsgID := proto.NewMessageIDGen(time.Now)
|
clientMsgID := proto.NewMessageIDGen(time.Now)
|
||||||
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
|
sendEncryptedWithSeq(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 1})
|
||||||
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
|
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
|
||||||
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tlprofile.ProfileCanonical)
|
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tlprofile.ProfileCanonical)
|
||||||
srv.Conns().SetReceivesUpdates(auth.SessionID, true)
|
srv.Conns().SetReceivesUpdates(auth.SessionID, true)
|
||||||
|
|
@ -1034,7 +1034,7 @@ func TestOutboundResendAndAckState(t *testing.T) {
|
||||||
conn, auth, cipher := dialHandshake(t, addr, dc, pub)
|
conn, auth, cipher := dialHandshake(t, addr, dc, pub)
|
||||||
|
|
||||||
clientMsgID := proto.NewMessageIDGen(time.Now)
|
clientMsgID := proto.NewMessageIDGen(time.Now)
|
||||||
sendEncrypted(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
|
sendEncryptedWithSeq(t, conn, cipher, auth, clientMsgID.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 1})
|
||||||
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
|
collectReplies(t, conn, cipher, auth.AuthKey, mt.MsgsAckTypeID)
|
||||||
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tlprofile.ProfileCanonical)
|
freezeActiveTestSessionProfile(t, srv.Conns(), auth.AuthKey.ID, auth.SessionID, tlprofile.ProfileCanonical)
|
||||||
srv.Conns().SetReceivesUpdates(auth.SessionID, true)
|
srv.Conns().SetReceivesUpdates(auth.SessionID, true)
|
||||||
|
|
|
||||||
|
|
@ -1205,9 +1205,9 @@ func TestSessionManagerPush(t *testing.T) {
|
||||||
|
|
||||||
// 各发一个 ping 建立 session,触发注册(并清掉 new_session_created/pong/ack)。
|
// 各发一个 ping 建立 session,触发注册(并清掉 new_session_created/pong/ack)。
|
||||||
msgGen := proto.NewMessageIDGen(time.Now)
|
msgGen := proto.NewMessageIDGen(time.Now)
|
||||||
sendEncrypted(t, conn1, cipher1, auth1, msgGen.New(proto.MessageFromClient), &mt.PingRequest{PingID: 1})
|
sendEncryptedWithSeq(t, conn1, cipher1, auth1, msgGen.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 1})
|
||||||
collectReplies(t, conn1, cipher1, auth1.AuthKey, mt.PongTypeID)
|
collectReplies(t, conn1, cipher1, auth1.AuthKey, mt.PongTypeID)
|
||||||
sendEncrypted(t, conn2, cipher2, auth2, msgGen.New(proto.MessageFromClient), &mt.PingRequest{PingID: 2})
|
sendEncryptedWithSeq(t, conn2, cipher2, auth2, msgGen.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 2})
|
||||||
collectReplies(t, conn2, cipher2, auth2.AuthKey, mt.PongTypeID)
|
collectReplies(t, conn2, cipher2, auth2.AuthKey, mt.PongTypeID)
|
||||||
|
|
||||||
if got := srv.Conns().Online(); got != 2 {
|
if got := srv.Conns().Online(); got != 2 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue