Skip to content

Commit c37b084

Browse files
authored
Merge pull request #309 from Jwenqiang/fix/webhook-panic-crash
fix: prevent process crash when webhook fires for a deleted user
2 parents cf8895d + 8579307 commit c37b084

2 files changed

Lines changed: 32 additions & 12 deletions

File tree

helpers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,10 @@ func callHookWithHmac(myurl string, payload map[string]string, userID string, en
249249
log.Info().Str("url", myurl).Str("userID", userID).Msg("Sending POST to client with retry logic")
250250

251251
client := clientManager.GetHTTPClient(userID)
252+
if client == nil {
253+
log.Warn().Str("url", myurl).Str("userID", userID).Msg("HTTP client is nil for user, skipping webhook")
254+
return
255+
}
252256

253257
// Retry settings
254258
maxRetries := 1
@@ -401,6 +405,10 @@ func callHookFileWithHmac(myurl string, payload map[string]string, userID string
401405
log.Info().Str("file", file).Str("url", myurl).Msg("Sending POST with retry logic")
402406

403407
client := clientManager.GetHTTPClient(userID)
408+
if client == nil {
409+
log.Warn().Str("url", myurl).Str("userID", userID).Msg("HTTP client is nil for user, skipping file webhook")
410+
return fmt.Errorf("http client is nil for user %s", userID)
411+
}
404412

405413
maxRetries := 1
406414
if *webhookRetryEnabled {

wmiau.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"net/url"
1313
"os"
1414
"path/filepath"
15+
"runtime/debug"
1516
"strconv"
1617
"strings"
1718
"time"
@@ -43,6 +44,25 @@ type MyClient struct {
4344
s *server
4445
}
4546

47+
// safeGo runs fn in a new goroutine with a defer recover so a panic inside
48+
// fire-and-forget side-effects (webhook delivery, MQ push) cannot crash
49+
// the whole process. Losing one delivery is preferable to taking wuzapi
50+
// down for every connected user.
51+
func safeGo(name string, fn func()) {
52+
go func() {
53+
defer func() {
54+
if r := recover(); r != nil {
55+
log.Error().
56+
Str("goroutine", name).
57+
Interface("panic", r).
58+
Str("stack", string(debug.Stack())).
59+
Msg("panic recovered in goroutine")
60+
}
61+
}()
62+
fn()
63+
}()
64+
}
65+
4666
// ensureS3ClientForUser loads S3 config from DB and initializes client if not already present (lazy init for reconnect-after-restart)
4767
func ensureS3ClientForUser(userID string) {
4868
GetS3Manager().EnsureClientFromDB(userID)
@@ -92,17 +112,9 @@ func sendToUserWebHookWithHmac(webhookurl string, path string, jsonData []byte,
92112
log.Info().Str("url", webhookurl).Msg("Calling user webhook")
93113

94114
if path == "" {
95-
go callHookWithHmac(webhookurl, data, userID, encryptedHmacKey)
115+
safeGo("callHookWithHmac", func() { callHookWithHmac(webhookurl, data, userID, encryptedHmacKey) })
96116
} else {
97-
// Create a channel to capture the error from the goroutine
98-
errChan := make(chan error, 1)
99-
go func() {
100-
err := callHookFileWithHmac(webhookurl, data, userID, path, encryptedHmacKey)
101-
errChan <- err
102-
}()
103-
104-
// Optionally handle the error from the channel (if needed)
105-
if err := <-errChan; err != nil {
117+
if err := callHookFileWithHmac(webhookurl, data, userID, path, encryptedHmacKey); err != nil {
106118
log.Error().Err(err).Msg("Error calling hook file")
107119
}
108120
}
@@ -213,9 +225,9 @@ func sendEventWithWebHook(mycli *MyClient, postmap map[string]interface{}, path
213225
sendToUserWebHookWithHmac(webhookurl, path, jsonData, mycli.userID, mycli.token, encryptedHmacKey)
214226

215227
// Get global webhook if configured
216-
go sendToGlobalWebHook(jsonData, mycli.token, mycli.userID)
228+
safeGo("sendToGlobalWebHook", func() { sendToGlobalWebHook(jsonData, mycli.token, mycli.userID) })
217229

218-
go sendToGlobalRabbit(jsonData, mycli.token, mycli.userID)
230+
safeGo("sendToGlobalRabbit", func() { sendToGlobalRabbit(jsonData, mycli.token, mycli.userID) })
219231
}
220232

221233
func checkIfSubscribedToEvent(subscribedEvents []string, eventType string, userId string) bool {

0 commit comments

Comments
 (0)