summaryrefslogtreecommitdiffstats
path: root/vendor/go.mau.fi/whatsmeow/request.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/request.go')
-rw-r--r--vendor/go.mau.fi/whatsmeow/request.go79
1 files changed, 71 insertions, 8 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/request.go b/vendor/go.mau.fi/whatsmeow/request.go
index 444ae1ad..e070126b 100644
--- a/vendor/go.mau.fi/whatsmeow/request.go
+++ b/vendor/go.mau.fi/whatsmeow/request.go
@@ -8,7 +8,7 @@ package whatsmeow
import (
"context"
- "encoding/base64"
+ "fmt"
"strconv"
"sync/atomic"
"time"
@@ -27,6 +27,20 @@ func isDisconnectNode(node *waBinary.Node) bool {
return node == xmlStreamEndNode || node.Tag == "stream:error"
}
+// isAuthErrorDisconnect checks if the given disconnect node is an error that shouldn't cause retrying.
+func isAuthErrorDisconnect(node *waBinary.Node) bool {
+ if node.Tag != "stream:error" {
+ return false
+ }
+ code, _ := node.Attrs["code"].(string)
+ conflict, _ := node.GetOptionalChildByTag("conflict")
+ conflictType := conflict.AttrGetter().OptionalString("type")
+ if code == "401" || conflictType == "replaced" || conflictType == "device_removed" {
+ return true
+ }
+ return false
+}
+
func (cli *Client) clearResponseWaiters(node *waBinary.Node) {
cli.responseWaitersLock.Lock()
for _, waiter := range cli.responseWaiters {
@@ -88,10 +102,11 @@ type infoQuery struct {
Content interface{}
Timeout time.Duration
+ NoRetry bool
Context context.Context
}
-func (cli *Client) sendIQAsyncDebug(query infoQuery) (<-chan *waBinary.Node, []byte, error) {
+func (cli *Client) sendIQAsyncAndGetData(query *infoQuery) (<-chan *waBinary.Node, []byte, error) {
if len(query.ID) == 0 {
query.ID = cli.generateRequestID()
}
@@ -107,7 +122,7 @@ func (cli *Client) sendIQAsyncDebug(query infoQuery) (<-chan *waBinary.Node, []b
if !query.Target.IsEmpty() {
attrs["target"] = query.Target
}
- data, err := cli.sendNodeDebug(waBinary.Node{
+ data, err := cli.sendNodeAndGetData(waBinary.Node{
Tag: "iq",
Attrs: attrs,
Content: query.Content,
@@ -120,12 +135,12 @@ func (cli *Client) sendIQAsyncDebug(query infoQuery) (<-chan *waBinary.Node, []b
}
func (cli *Client) sendIQAsync(query infoQuery) (<-chan *waBinary.Node, error) {
- ch, _, err := cli.sendIQAsyncDebug(query)
+ ch, _, err := cli.sendIQAsyncAndGetData(&query)
return ch, err
}
func (cli *Client) sendIQ(query infoQuery) (*waBinary.Node, error) {
- resChan, data, err := cli.sendIQAsyncDebug(query)
+ resChan, data, err := cli.sendIQAsyncAndGetData(&query)
if err != nil {
return nil, err
}
@@ -138,10 +153,13 @@ func (cli *Client) sendIQ(query infoQuery) (*waBinary.Node, error) {
select {
case res := <-resChan:
if isDisconnectNode(res) {
- if cli.DebugDecodeBeforeSend && res.Tag == "stream:error" && res.GetChildByTag("xml-not-well-formed").Tag != "" {
- cli.Log.Debugf("Info query that was interrupted by xml-not-well-formed: %s", base64.URLEncoding.EncodeToString(data))
+ if query.NoRetry {
+ return nil, &DisconnectedError{Action: "info query", Node: res}
+ }
+ res, err = cli.retryFrame("info query", query.ID, data, res, query.Context, query.Timeout)
+ if err != nil {
+ return nil, err
}
- return nil, &DisconnectedError{Action: "info query", Node: res}
}
resType, _ := res.Attrs["type"].(string)
if res.Tag != "iq" || (resType != "result" && resType != "error") {
@@ -156,3 +174,48 @@ func (cli *Client) sendIQ(query infoQuery) (*waBinary.Node, error) {
return nil, ErrIQTimedOut
}
}
+
+func (cli *Client) retryFrame(reqType, id string, data []byte, origResp *waBinary.Node, ctx context.Context, timeout time.Duration) (*waBinary.Node, error) {
+ if isAuthErrorDisconnect(origResp) {
+ cli.Log.Debugf("%s (%s) was interrupted by websocket disconnection (%s), not retrying as it looks like an auth error", id, reqType, origResp.XMLString())
+ return nil, &DisconnectedError{Action: reqType, Node: origResp}
+ }
+
+ cli.Log.Debugf("%s (%s) was interrupted by websocket disconnection (%s), waiting for reconnect to retry...", id, reqType, origResp.XMLString())
+ if !cli.WaitForConnection(5 * time.Second) {
+ cli.Log.Debugf("Websocket didn't reconnect within 5 seconds of failed %s (%s)", reqType, id)
+ return nil, &DisconnectedError{Action: reqType, Node: origResp}
+ }
+
+ cli.socketLock.RLock()
+ sock := cli.socket
+ cli.socketLock.RUnlock()
+ if sock == nil {
+ return nil, ErrNotConnected
+ }
+
+ respChan := cli.waitResponse(id)
+ err := sock.SendFrame(data)
+ if err != nil {
+ cli.cancelResponse(id, respChan)
+ return nil, err
+ }
+ var resp *waBinary.Node
+ if ctx != nil && timeout > 0 {
+ select {
+ case resp = <-respChan:
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-time.After(timeout):
+ // FIXME this error isn't technically correct (but works for now - the ctx and timeout params are only used from sendIQ)
+ return nil, ErrIQTimedOut
+ }
+ } else {
+ resp = <-respChan
+ }
+ if isDisconnectNode(resp) {
+ cli.Log.Debugf("Retrying %s %s was interrupted by websocket disconnection (%v), not retrying anymore", reqType, id, resp.XMLString())
+ return nil, &DisconnectedError{Action: fmt.Sprintf("%s (retry)", reqType), Node: resp}
+ }
+ return resp, nil
+}