2022-01-30 23:27:37 +00:00
|
|
|
// Copyright (c) 2021 Tulir Asokan
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
package whatsmeow
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"math/rand"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
waBinary "go.mau.fi/whatsmeow/binary"
|
|
|
|
"go.mau.fi/whatsmeow/types"
|
2022-06-11 21:07:42 +00:00
|
|
|
"go.mau.fi/whatsmeow/types/events"
|
2022-01-30 23:27:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// KeepAliveResponseDeadline specifies the duration to wait for a response to websocket keepalive pings.
|
|
|
|
KeepAliveResponseDeadline = 10 * time.Second
|
|
|
|
// KeepAliveIntervalMin specifies the minimum interval for websocket keepalive pings.
|
|
|
|
KeepAliveIntervalMin = 20 * time.Second
|
|
|
|
// KeepAliveIntervalMax specifies the maximum interval for websocket keepalive pings.
|
|
|
|
KeepAliveIntervalMax = 30 * time.Second
|
2023-08-05 18:43:19 +00:00
|
|
|
|
|
|
|
// KeepAliveMaxFailTime specifies the maximum time to wait before forcing a reconnect if keepalives fail repeatedly.
|
|
|
|
KeepAliveMaxFailTime = 3 * time.Minute
|
2022-01-30 23:27:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func (cli *Client) keepAliveLoop(ctx context.Context) {
|
2023-08-05 18:43:19 +00:00
|
|
|
lastSuccess := time.Now()
|
2022-06-11 21:07:42 +00:00
|
|
|
var errorCount int
|
2022-01-30 23:27:37 +00:00
|
|
|
for {
|
|
|
|
interval := rand.Int63n(KeepAliveIntervalMax.Milliseconds()-KeepAliveIntervalMin.Milliseconds()) + KeepAliveIntervalMin.Milliseconds()
|
|
|
|
select {
|
|
|
|
case <-time.After(time.Duration(interval) * time.Millisecond):
|
2022-06-11 21:07:42 +00:00
|
|
|
isSuccess, shouldContinue := cli.sendKeepAlive(ctx)
|
|
|
|
if !shouldContinue {
|
2022-01-30 23:27:37 +00:00
|
|
|
return
|
2022-06-11 21:07:42 +00:00
|
|
|
} else if !isSuccess {
|
|
|
|
errorCount++
|
|
|
|
go cli.dispatchEvent(&events.KeepAliveTimeout{
|
|
|
|
ErrorCount: errorCount,
|
|
|
|
LastSuccess: lastSuccess,
|
|
|
|
})
|
2023-08-05 18:43:19 +00:00
|
|
|
if cli.EnableAutoReconnect && time.Since(lastSuccess) > KeepAliveMaxFailTime {
|
|
|
|
cli.Log.Debugf("Forcing reconnect due to keepalive failure")
|
|
|
|
cli.Disconnect()
|
|
|
|
go cli.autoReconnect()
|
|
|
|
}
|
2022-06-11 21:07:42 +00:00
|
|
|
} else {
|
|
|
|
if errorCount > 0 {
|
|
|
|
errorCount = 0
|
|
|
|
go cli.dispatchEvent(&events.KeepAliveRestored{})
|
|
|
|
}
|
|
|
|
lastSuccess = time.Now()
|
2022-01-30 23:27:37 +00:00
|
|
|
}
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-11 21:07:42 +00:00
|
|
|
func (cli *Client) sendKeepAlive(ctx context.Context) (isSuccess, shouldContinue bool) {
|
2022-01-30 23:27:37 +00:00
|
|
|
respCh, err := cli.sendIQAsync(infoQuery{
|
|
|
|
Namespace: "w:p",
|
|
|
|
Type: "get",
|
|
|
|
To: types.ServerJID,
|
|
|
|
Content: []waBinary.Node{{Tag: "ping"}},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
cli.Log.Warnf("Failed to send keepalive: %v", err)
|
2022-06-11 21:07:42 +00:00
|
|
|
return false, true
|
2022-01-30 23:27:37 +00:00
|
|
|
}
|
|
|
|
select {
|
|
|
|
case <-respCh:
|
|
|
|
// All good
|
2022-06-11 21:07:42 +00:00
|
|
|
return true, true
|
2022-01-30 23:27:37 +00:00
|
|
|
case <-time.After(KeepAliveResponseDeadline):
|
|
|
|
cli.Log.Warnf("Keepalive timed out")
|
2022-06-11 21:07:42 +00:00
|
|
|
return false, true
|
2022-01-30 23:27:37 +00:00
|
|
|
case <-ctx.Done():
|
2022-06-11 21:07:42 +00:00
|
|
|
return false, false
|
2022-01-30 23:27:37 +00:00
|
|
|
}
|
|
|
|
}
|