Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 6 additions & 15 deletions app/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ func SendAndroidNotification(ctx context.Context, cfg *config.ConfYaml, opts CLI
return err
}

if _, err := notify.PushToAndroid(ctx, req, cfg); err != nil {
return err
}

return nil
_, err := notify.PushToAndroid(ctx, req, cfg)
return err
}

// SendHuaweiNotification sends a Huawei notification via CLI.
Expand Down Expand Up @@ -73,11 +70,8 @@ func SendHuaweiNotification(ctx context.Context, cfg *config.ConfYaml, opts CLIS
return err
}

if _, err := notify.PushToHuawei(ctx, req, cfg); err != nil {
return err
}

return nil
_, err := notify.PushToHuawei(ctx, req, cfg)
return err
}

// SendIOSNotification sends an iOS notification via CLI.
Expand Down Expand Up @@ -110,11 +104,8 @@ func SendIOSNotification(ctx context.Context, cfg *config.ConfYaml, opts CLISend
return err
}

if _, err := notify.PushToIOS(ctx, req, cfg); err != nil {
return err
}

return nil
_, err := notify.PushToIOS(ctx, req, cfg)
return err
}

// SendNotification sends a notification based on platform type.
Expand Down
80 changes: 44 additions & 36 deletions logx/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,54 +205,62 @@ type InputLog struct {

// LogPush record user push request and server response.
func LogPush(input *InputLog) LogPushEntry {
var platColor, resetColor, output string
log := GetLogPushEntry(input)

if isTerm {
platColor = colorForPlatForm(input.Platform)
resetColor = reset
// Only build the output string when the destination logger will actually
// emit it; the marshal/format runs once per token on the push hot path.
switch input.Status {
case core.SucceededPush:
if LogAccess.IsLevelEnabled(logrus.InfoLevel) {
LogAccess.Info(formatPushLog(input, log))
}
case core.FailedPush:
if LogError.IsLevelEnabled(logrus.ErrorLevel) {
LogError.Error(formatPushLog(input, log))
}
}

log := GetLogPushEntry(input)
return log
}

// formatPushLog renders a push log entry as JSON or colored text.
func formatPushLog(input *InputLog, log LogPushEntry) string {
if input.Format == "json" {
logJSON, _ := json.Marshal(log)
return string(logJSON)
}

output = string(logJSON)
} else {
var typeColor string
switch input.Status {
case core.SucceededPush:
if isTerm {
typeColor = green
}

output = fmt.Sprintf("|%s %s %s| %s%s%s [%s] %s",
typeColor, log.Type, resetColor,
platColor, log.Platform, resetColor,
log.Token,
log.Message,
)
case core.FailedPush:
if isTerm {
typeColor = red
}

output = fmt.Sprintf("|%s %s %s| %s%s%s [%s] | %s | Error Message: %s",
typeColor, log.Type, resetColor,
platColor, log.Platform, resetColor,
log.Token,
log.Message,
log.Error,
)
}
var platColor, resetColor, typeColor string
if isTerm {
platColor = colorForPlatForm(input.Platform)
resetColor = reset
}

switch input.Status {
case core.SucceededPush:
LogAccess.Info(output)
if isTerm {
typeColor = green
}

return fmt.Sprintf("|%s %s %s| %s%s%s [%s] %s",
typeColor, log.Type, resetColor,
platColor, log.Platform, resetColor,
log.Token,
log.Message,
)
case core.FailedPush:
LogError.Error(output)
if isTerm {
typeColor = red
}

return fmt.Sprintf("|%s %s %s| %s%s%s [%s] | %s | Error Message: %s",
typeColor, log.Type, resetColor,
platColor, log.Platform, resetColor,
log.Token,
log.Message,
log.Error,
)
}

return log
return ""
}
7 changes: 3 additions & 4 deletions notify/feedback.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ import (

// extractHeaders converts a slice of strings to a map of strings.
func extractHeaders(headers []string) map[string]string {
result := make(map[string]string)
result := make(map[string]string, len(headers))
for _, header := range headers {
parts := strings.Split(header, ":")
if len(parts) == 2 {
result[parts[0]] = parts[1]
if key, value, ok := strings.Cut(header, ":"); ok {
result[key] = value
}
}
return result
Expand Down
3 changes: 1 addition & 2 deletions notify/global.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,5 @@ var (
)

const (
HIGH = "high"
NORMAL = "nornal"
HIGH = "high"
)
14 changes: 10 additions & 4 deletions notify/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,17 @@ func (p *PushNotification) Payload() []byte {
// IsTopic check if message format is topic for FCM
// ref: https://firebase.google.com/docs/cloud-messaging/send-message#topic-http-post-request
func (p *PushNotification) IsTopic() bool {
if p.Platform == core.PlatFormHuawei || p.Platform == core.PlatFormAndroid {
return p.Topic != "" || p.Condition != ""
}
return (p.Platform == core.PlatFormHuawei || p.Platform == core.PlatFormAndroid) &&
(p.Topic != "" || p.Condition != "")
}

return false
// effectiveMaxRetry clamps the configured retry count by the per-request Retry
// override when the request asks for fewer retries.
func effectiveMaxRetry(reqRetry, cfgMax int) int {
if reqRetry > 0 && reqRetry < cfgMax {
return reqRetry
}
return cfgMax
}

// CheckMessage for check request message
Expand Down
40 changes: 26 additions & 14 deletions notify/notification_apns.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"net/http"
"path/filepath"
"sync"
"sync/atomic"
"time"

"github.com/appleboy/gorush/config"
Expand Down Expand Up @@ -467,19 +468,18 @@ func PushToIOS(
) (resp *ResponsePush, err error) {
logx.LogAccess.Debug("Start push notification for iOS")

var (
retryCount = 0
maxRetry = cfg.Ios.MaxRetry
)

if req.Retry > 0 && req.Retry < maxRetry {
maxRetry = req.Retry
}
retryCount := 0
maxRetry := effectiveMaxRetry(req.Retry, cfg.Ios.MaxRetry)

resp = &ResponsePush{}

Retry:
var newTokens []string
var (
newTokens []string
successCount atomic.Int64
errorCount atomic.Int64
mu sync.Mutex // guards newTokens and resp.Logs against the per-token goroutines
)

notification := GetIOSNotification(req)
client := getApnsClient(cfg, req)
Expand All @@ -503,19 +503,22 @@ Retry:

// apns server error
errLog := logPush(cfg, core.FailedPush, token, req, err)
resp.Logs = append(resp.Logs, errLog)

status.StatStorage.AddIosError(1)
errorCount.Add(1)
// We should retry only "retryable" statuses. More info about response:
// See https://apple.co/3AdNane (Handling Notification Responses from APNs)
if res != nil && res.StatusCode >= http.StatusInternalServerError {
retryable := res != nil && res.StatusCode >= http.StatusInternalServerError

mu.Lock()
resp.Logs = append(resp.Logs, errLog)
if retryable {
newTokens = append(newTokens, token)
}
mu.Unlock()
}

if res != nil && res.Sent() {
logPush(cfg, core.SucceededPush, token, req, nil)
status.StatStorage.AddIosSuccess(1)
successCount.Add(1)
}

// free push slot
Expand All @@ -526,6 +529,15 @@ Retry:

wg.Wait()

// Flush the per-token counts in a single update each, instead of one
// stat-storage round-trip per device token.
if n := successCount.Load(); n > 0 {
status.StatStorage.AddIosSuccess(n)
}
if n := errorCount.Load(); n > 0 {
status.StatStorage.AddIosError(n)
}

if len(newTokens) > 0 && retryCount < maxRetry {
retryCount++

Expand Down
11 changes: 4 additions & 7 deletions notify/notification_fcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,9 @@ func convertDataToStringMap(data map[string]any) map[string]string {
}
result := make(map[string]string, len(data))
for k, v := range data {
switch v.(type) {
switch val := v.(type) {
case string:
result[k] = fmt.Sprintf("%s", v)
result[k] = val
default:
if b, err := json.Marshal(v); err == nil {
result[k] = string(b)
Expand Down Expand Up @@ -184,7 +184,7 @@ func GetAndroidNotification(req *PushNotification) []*messaging.Message {
setupFCMSound(req)

data := convertDataToStringMap(req.Data)
var messages []*messaging.Message
messages := make([]*messaging.Message, 0, len(req.Tokens)+1)

if req.IsTopic() {
msg := buildFCMMessage(req, data)
Expand Down Expand Up @@ -277,10 +277,7 @@ func PushToAndroid(
return nil, err
}

maxRetry := cfg.Android.MaxRetry
if req.Retry > 0 && req.Retry < maxRetry {
maxRetry = req.Retry
}
maxRetry := effectiveMaxRetry(req.Retry, cfg.Android.MaxRetry)

resp = &ResponsePush{}
retryCount := 0
Expand Down
6 changes: 1 addition & 5 deletions notify/notification_hms.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,9 @@ func PushToHuawei(
var (
client *client.HMSClient
retryCount = 0
maxRetry = cfg.Huawei.MaxRetry
maxRetry = effectiveMaxRetry(req.Retry, cfg.Huawei.MaxRetry)
)

if req.Retry > 0 && req.Retry < maxRetry {
maxRetry = req.Retry
}

// check message
err = CheckMessage(req)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion router/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func markFailedNotification(
reason string,
) []logx.LogPushEntry {
logx.LogError.Error(reason)
logs := make([]logx.LogPushEntry, 0)
logs := make([]logx.LogPushEntry, 0, len(notification.Tokens))
for _, token := range notification.Tokens {
logs = append(logs, logx.GetLogPushEntry(&logx.InputLog{
ID: notification.ID,
Expand Down
33 changes: 11 additions & 22 deletions router/server_normal.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,25 +86,12 @@ func RunHTTPServer(
return startServer(ctx, server, cfg)
}

func listenAndServe(ctx context.Context, s *http.Server, cfg *config.ConfYaml) error {
var g errgroup.Group
g.Go(func() error {
<-ctx.Done()
timeout := time.Duration(cfg.Core.ShutdownTimeout) * time.Second
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return s.Shutdown(ctx)
})
g.Go(func() error {
if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
})
return g.Wait()
}

func listenAndServeTLS(ctx context.Context, s *http.Server, cfg *config.ConfYaml) error {
func serveUntilShutdown(
ctx context.Context,
s *http.Server,
cfg *config.ConfYaml,
serve func() error,
) error {
var g errgroup.Group
g.Go(func() error {
<-ctx.Done()
Expand All @@ -114,7 +101,7 @@ func listenAndServeTLS(ctx context.Context, s *http.Server, cfg *config.ConfYaml
return s.Shutdown(ctx)
})
g.Go(func() error {
if err := s.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
if err := serve(); err != nil && err != http.ErrServerClosed {
return err
}
return nil
Expand All @@ -124,8 +111,10 @@ func listenAndServeTLS(ctx context.Context, s *http.Server, cfg *config.ConfYaml

func startServer(ctx context.Context, s *http.Server, cfg *config.ConfYaml) error {
if s.TLSConfig == nil {
return listenAndServe(ctx, s, cfg)
return serveUntilShutdown(ctx, s, cfg, s.ListenAndServe)
}

return listenAndServeTLS(ctx, s, cfg)
return serveUntilShutdown(ctx, s, cfg, func() error {
return s.ListenAndServeTLS("", "")
})
}
5 changes: 0 additions & 5 deletions rpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

"firebase.google.com/go/v4/messaging"
"github.com/appleboy/gorush/config"
"github.com/appleboy/gorush/core"
"github.com/appleboy/gorush/logx"
"github.com/appleboy/gorush/notify"
"github.com/appleboy/gorush/rpc/proto"
Expand Down Expand Up @@ -94,10 +93,6 @@ func (s *Server) Send(
notification.Badge = &badge
}

if in.Topic != "" && in.Platform == core.PlatFormAndroid {
notification.Topic = in.Topic
}

if in.Alert != nil {
notification.Alert = notify.Alert{
Title: in.Alert.Title,
Expand Down
Loading