-
-
Notifications
You must be signed in to change notification settings - Fork 10
Merge upstream #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Merge upstream #45
Changes from all commits
6eefbff
4fa3462
72f22e6
4650ea9
d1cc3c0
33cfac5
0fadda7
1f6240e
de26b4a
bdd4e83
0ca8346
28bfe53
57796d3
30593f2
9ec8f76
b25a56d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -66,9 +66,10 @@ type Client struct { | |||||||||||||||||||
| recvLog waLog.Logger | ||||||||||||||||||||
| sendLog waLog.Logger | ||||||||||||||||||||
|
|
||||||||||||||||||||
| socket *socket.NoiseSocket | ||||||||||||||||||||
| socketLock sync.RWMutex | ||||||||||||||||||||
| socketWait chan struct{} | ||||||||||||||||||||
| socket *socket.NoiseSocket | ||||||||||||||||||||
| socketLock sync.RWMutex | ||||||||||||||||||||
| socketWait chan struct{} | ||||||||||||||||||||
| handlerQueueWait chan struct{} | ||||||||||||||||||||
|
|
||||||||||||||||||||
| isLoggedIn atomic.Bool | ||||||||||||||||||||
| paired atomic.Bool | ||||||||||||||||||||
|
|
@@ -117,7 +118,6 @@ type Client struct { | |||||||||||||||||||
| responseWaitersLock sync.Mutex | ||||||||||||||||||||
|
|
||||||||||||||||||||
| nodeHandlers map[string]nodeHandler | ||||||||||||||||||||
| handlerQueue chan *waBinary.Node | ||||||||||||||||||||
| eventHandlers []wrappedEventHandler | ||||||||||||||||||||
| eventHandlersLock sync.RWMutex | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -265,7 +265,6 @@ func NewClient(deviceStore *store.Device, log waLog.Logger) *Client { | |||||||||||||||||||
| responseWaiters: make(map[string]chan<- *waBinary.Node), | ||||||||||||||||||||
| eventHandlers: make([]wrappedEventHandler, 0, 1), | ||||||||||||||||||||
| messageRetries: make(map[string]int), | ||||||||||||||||||||
| handlerQueue: make(chan *waBinary.Node, handlerQueueSize), | ||||||||||||||||||||
| appStateProc: appstate.NewProcessor(deviceStore, log.Sub("AppState")), | ||||||||||||||||||||
| socketWait: make(chan struct{}), | ||||||||||||||||||||
| expectedDisconnect: exsync.NewEvent(), | ||||||||||||||||||||
|
|
@@ -568,16 +567,19 @@ func (cli *Client) unlockedConnect(ctx context.Context) error { | |||||||||||||||||||
| fs.URL = cli.MessengerConfig.WebsocketURL | ||||||||||||||||||||
| fs.HTTPHeaders.Set("Origin", cli.MessengerConfig.BaseURL) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| var queue chan *waBinary.Node | ||||||||||||||||||||
| maps.Copy(fs.HTTPHeaders, cli.WebSocketHeaders) | ||||||||||||||||||||
| if err := fs.Connect(ctx); err != nil { | ||||||||||||||||||||
| fs.Close(0) | ||||||||||||||||||||
| return err | ||||||||||||||||||||
| } else if err = cli.doHandshake(ctx, fs, *keys.NewKeyPair()); err != nil { | ||||||||||||||||||||
| } else if queue, err = cli.doHandshake(fs, *keys.NewKeyPair()); err != nil { | ||||||||||||||||||||
| fs.Close(0) | ||||||||||||||||||||
| return fmt.Errorf("noise handshake failed: %w", err) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| closeWait := make(chan struct{}) | ||||||||||||||||||||
| cli.handlerQueueWait = closeWait | ||||||||||||||||||||
| go cli.keepAliveLoop(ctx, fs.Context()) | ||||||||||||||||||||
| go cli.handlerQueueLoop(ctx, fs.Context()) | ||||||||||||||||||||
| go cli.handlerQueueLoop(ctx, fs.Context(), queue, closeWait) | ||||||||||||||||||||
| return nil | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -625,6 +627,7 @@ func (cli *Client) autoReconnect(ctx context.Context) { | |||||||||||||||||||
| if !cli.EnableAutoReconnect || cli.Store.ID == nil { | ||||||||||||||||||||
| return | ||||||||||||||||||||
| } | ||||||||||||||||||||
| // TODO wait for handler queue to close here? | ||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the server closes the socket immediately after sending a Useful? React with 👍 / 👎. |
||||||||||||||||||||
| for { | ||||||||||||||||||||
| autoReconnectDelay := time.Duration(cli.AutoReconnectErrors) * 2 * time.Second | ||||||||||||||||||||
| cli.Log.Debugf("Automatically reconnecting after %v", autoReconnectDelay) | ||||||||||||||||||||
|
|
@@ -705,6 +708,14 @@ func (cli *Client) unlockedDisconnect() { | |||||||||||||||||||
| cli.socket = nil | ||||||||||||||||||||
| cli.clearResponseWaiters(xmlStreamEndNode) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| if cli.handlerQueueWait != nil { | ||||||||||||||||||||
| select { | ||||||||||||||||||||
| case <-cli.handlerQueueWait: | ||||||||||||||||||||
| cli.handlerQueueWait = nil | ||||||||||||||||||||
| case <-time.After(5 * time.Second): | ||||||||||||||||||||
| cli.Log.Warnf("Handler queue wait channel not closed after 5 seconds") | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Logout sends a request to unlink the device, then disconnects from the websocket and deletes the local device store. | ||||||||||||||||||||
|
|
@@ -832,7 +843,13 @@ func (cli *Client) RemoveEventHandlers() { | |||||||||||||||||||
| cli.eventHandlersLock.Unlock() | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func (cli *Client) handleFrame(ctx context.Context, data []byte) { | ||||||||||||||||||||
| func (cli *Client) makeFrameHandler(queue chan *waBinary.Node) func(ctx context.Context, data []byte) { | ||||||||||||||||||||
| return func(ctx context.Context, data []byte) { | ||||||||||||||||||||
| cli.handleFrame(ctx, data, queue) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func (cli *Client) handleFrame(ctx context.Context, data []byte, queue chan *waBinary.Node) { | ||||||||||||||||||||
| decompressed, err := waBinary.Unpack(data) | ||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||
| cli.Log.Warnf("Failed to decompress frame: %v", err) | ||||||||||||||||||||
|
|
@@ -855,13 +872,13 @@ func (cli *Client) handleFrame(ctx context.Context, data []byte) { | |||||||||||||||||||
| // handled | ||||||||||||||||||||
| } else if _, ok := cli.nodeHandlers[node.Tag]; ok { | ||||||||||||||||||||
| select { | ||||||||||||||||||||
| case cli.handlerQueue <- node: | ||||||||||||||||||||
| case queue <- node: | ||||||||||||||||||||
| case <-ctx.Done(): | ||||||||||||||||||||
| default: | ||||||||||||||||||||
| cli.Log.Warnf("Handler queue is full, message ordering is no longer guaranteed") | ||||||||||||||||||||
| go func() { | ||||||||||||||||||||
| select { | ||||||||||||||||||||
| case cli.handlerQueue <- node: | ||||||||||||||||||||
| case queue <- node: | ||||||||||||||||||||
| case <-ctx.Done(): | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }() | ||||||||||||||||||||
|
|
@@ -871,14 +888,35 @@ func (cli *Client) handleFrame(ctx context.Context, data []byte) { | |||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| func (cli *Client) handlerQueueLoop(evtCtx, connCtx context.Context) { | ||||||||||||||||||||
| func (cli *Client) handlerQueueLoop(evtCtx, connCtx context.Context, queue chan *waBinary.Node, closeWait chan struct{}) { | ||||||||||||||||||||
| ticker := time.NewTicker(30 * time.Second) | ||||||||||||||||||||
| ticker.Stop() | ||||||||||||||||||||
| cli.Log.Debugf("Starting handler queue loop") | ||||||||||||||||||||
| defer func() { | ||||||||||||||||||||
| Loop: | ||||||||||||||||||||
| for { | ||||||||||||||||||||
| select { | ||||||||||||||||||||
| case node := <-queue: | ||||||||||||||||||||
| // Make sure stream errors are handled even after disconnection so the appropriate auto-reconnect is done. | ||||||||||||||||||||
| // Everything else | ||||||||||||||||||||
| if node.Tag == "stream:error" { | ||||||||||||||||||||
| cli.Log.Debugf("Handling stream:error node in handler queue loop after context cancellation") | ||||||||||||||||||||
| cli.handleStreamError(evtCtx, node) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| default: | ||||||||||||||||||||
| break Loop | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
| close(closeWait) | ||||||||||||||||||||
| }() | ||||||||||||||||||||
| Loop: | ||||||||||||||||||||
| for { | ||||||||||||||||||||
| select { | ||||||||||||||||||||
| case node := <-cli.handlerQueue: | ||||||||||||||||||||
| case node := <-queue: | ||||||||||||||||||||
| if connCtx.Err() != nil { | ||||||||||||||||||||
| cli.Log.Debugf("Closing handler queue loop before node handling") | ||||||||||||||||||||
| return | ||||||||||||||||||||
| } | ||||||||||||||||||||
| doneChan := make(chan struct{}) | ||||||||||||||||||||
| start := time.Now() | ||||||||||||||||||||
| go func() { | ||||||||||||||||||||
|
|
@@ -890,11 +928,15 @@ Loop: | |||||||||||||||||||
| } | ||||||||||||||||||||
| }() | ||||||||||||||||||||
| ticker.Reset(30 * time.Second) | ||||||||||||||||||||
| for i := 0; i < 10; i++ { | ||||||||||||||||||||
| for range 10 { | ||||||||||||||||||||
| select { | ||||||||||||||||||||
| case <-doneChan: | ||||||||||||||||||||
| ticker.Stop() | ||||||||||||||||||||
| continue Loop | ||||||||||||||||||||
| case <-connCtx.Done(): | ||||||||||||||||||||
| ticker.Stop() | ||||||||||||||||||||
| cli.Log.Warnf("Closing handler queue loop in the middle of handling %s", node) | ||||||||||||||||||||
| return | ||||||||||||||||||||
|
Comment on lines
+936
to
+939
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Keep the completion signal open until the active handler exits. At line 936, cancellation returns before Wait for Proposed fix case <-connCtx.Done():
ticker.Stop()
cli.Log.Warnf("Closing handler queue loop in the middle of handling %s", node)
+ <-doneChan
return📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| case <-ticker.C: | ||||||||||||||||||||
| cli.Log.Warnf("Node handling is taking long for %s (started %s ago)", node, time.Since(start)) | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
@@ -981,7 +1023,11 @@ func (cli *Client) ParseWebMessage(chatJID types.JID, webMsg *waWeb.WebMessageIn | |||||||||||||||||||
| if webMsg.GetOriginalSelfAuthorUserJIDString() != "" { | ||||||||||||||||||||
| info.Sender, err = types.ParseJID(webMsg.GetOriginalSelfAuthorUserJIDString()) | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| info.Sender = cli.getOwnID().ToNonAD() | ||||||||||||||||||||
| if info.Chat.Server == types.HiddenUserServer { | ||||||||||||||||||||
| info.Sender = cli.getOwnLID().ToNonAD() | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| info.Sender = cli.getOwnID().ToNonAD() | ||||||||||||||||||||
| } | ||||||||||||||||||||
| if info.Sender.IsEmpty() { | ||||||||||||||||||||
| return nil, ErrNotLoggedIn | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: polymorfa/hypermeow
Length of output: 1448
Restrict the token before executing repository code.
This job runs build, test, and pre-commit commands after checkout. Add
contents: readpermissions and setpersist-credentials: falseforactions/checkout@v7.Proposed fix
🧰 Tools
🪛 zizmor (1.29.0)
[warning] 18-18: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[warning] 9-42: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Source: Linters/SAST tools