2021-07-23 17:56:33 +00:00
|
|
|
package notify
|
2017-07-17 03:22:48 +00:00
|
|
|
|
|
|
|
import (
|
2017-10-25 08:49:23 +00:00
|
|
|
"crypto/ecdsa"
|
|
|
|
"crypto/tls"
|
2018-02-18 09:12:51 +00:00
|
|
|
"encoding/base64"
|
2017-07-17 03:22:48 +00:00
|
|
|
"errors"
|
2020-05-14 13:38:58 +00:00
|
|
|
"net"
|
2019-12-07 23:30:24 +00:00
|
|
|
"net/http"
|
2017-07-17 03:22:48 +00:00
|
|
|
"path/filepath"
|
2020-04-23 13:02:53 +00:00
|
|
|
"sync"
|
2017-07-17 03:22:48 +00:00
|
|
|
"time"
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
"github.com/appleboy/gorush/config"
|
2021-07-13 08:32:39 +00:00
|
|
|
"github.com/appleboy/gorush/core"
|
|
|
|
"github.com/appleboy/gorush/logx"
|
2021-07-13 15:58:47 +00:00
|
|
|
"github.com/appleboy/gorush/status"
|
2021-07-13 08:32:39 +00:00
|
|
|
|
2018-11-20 09:01:35 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
2017-10-25 08:49:23 +00:00
|
|
|
"github.com/sideshow/apns2"
|
2017-07-17 03:22:48 +00:00
|
|
|
"github.com/sideshow/apns2/certificate"
|
|
|
|
"github.com/sideshow/apns2/payload"
|
2017-10-25 08:49:23 +00:00
|
|
|
"github.com/sideshow/apns2/token"
|
2019-09-06 07:48:42 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-12-07 23:30:24 +00:00
|
|
|
"golang.org/x/net/http2"
|
2017-07-17 03:22:48 +00:00
|
|
|
)
|
|
|
|
|
2020-05-14 13:38:58 +00:00
|
|
|
var (
|
|
|
|
idleConnTimeout = 90 * time.Second
|
|
|
|
tlsDialTimeout = 20 * time.Second
|
|
|
|
tcpKeepAlive = 60 * time.Second
|
|
|
|
)
|
|
|
|
|
2021-07-16 08:30:01 +00:00
|
|
|
var doOnce sync.Once
|
|
|
|
|
2020-05-14 13:38:58 +00:00
|
|
|
// DialTLS is the default dial function for creating TLS connections for
|
|
|
|
// non-proxied HTTPS requests.
|
|
|
|
var DialTLS = func(cfg *tls.Config) func(network, addr string) (net.Conn, error) {
|
|
|
|
return func(network, addr string) (net.Conn, error) {
|
|
|
|
dialer := &net.Dialer{
|
|
|
|
Timeout: tlsDialTimeout,
|
|
|
|
KeepAlive: tcpKeepAlive,
|
|
|
|
}
|
|
|
|
return tls.DialWithDialer(dialer, network, addr, cfg)
|
|
|
|
}
|
|
|
|
}
|
2019-12-07 23:30:24 +00:00
|
|
|
|
2018-08-28 03:02:13 +00:00
|
|
|
// Sound sets the aps sound on the payload.
|
|
|
|
type Sound struct {
|
|
|
|
Critical int `json:"critical,omitempty"`
|
|
|
|
Name string `json:"name,omitempty"`
|
|
|
|
Volume float32 `json:"volume,omitempty"`
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
// InitAPNSClient use for initialize APNs Client.
|
2021-07-16 04:10:34 +00:00
|
|
|
func InitAPNSClient(cfg config.ConfYaml) error {
|
|
|
|
if cfg.Ios.Enabled {
|
2017-07-17 03:22:48 +00:00
|
|
|
var err error
|
2017-10-25 08:49:23 +00:00
|
|
|
var authKey *ecdsa.PrivateKey
|
|
|
|
var certificateKey tls.Certificate
|
2018-02-18 09:12:51 +00:00
|
|
|
var ext string
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Ios.KeyPath != "" {
|
|
|
|
ext = filepath.Ext(cfg.Ios.KeyPath)
|
2018-02-18 09:12:51 +00:00
|
|
|
|
|
|
|
switch ext {
|
|
|
|
case ".p12":
|
2021-07-16 04:10:34 +00:00
|
|
|
certificateKey, err = certificate.FromP12File(cfg.Ios.KeyPath, cfg.Ios.Password)
|
2018-02-18 09:12:51 +00:00
|
|
|
case ".pem":
|
2021-07-16 04:10:34 +00:00
|
|
|
certificateKey, err = certificate.FromPemFile(cfg.Ios.KeyPath, cfg.Ios.Password)
|
2018-02-18 09:12:51 +00:00
|
|
|
case ".p8":
|
2021-07-16 04:10:34 +00:00
|
|
|
authKey, err = token.AuthKeyFromFile(cfg.Ios.KeyPath)
|
2018-02-18 09:12:51 +00:00
|
|
|
default:
|
|
|
|
err = errors.New("wrong certificate key extension")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2021-07-13 08:32:39 +00:00
|
|
|
logx.LogError.Error("Cert Error:", err.Error())
|
2018-02-18 09:12:51 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
2021-07-16 04:10:34 +00:00
|
|
|
} else if cfg.Ios.KeyBase64 != "" {
|
|
|
|
ext = "." + cfg.Ios.KeyType
|
|
|
|
key, err := base64.StdEncoding.DecodeString(cfg.Ios.KeyBase64)
|
2018-02-18 09:12:51 +00:00
|
|
|
if err != nil {
|
2021-07-13 08:32:39 +00:00
|
|
|
logx.LogError.Error("base64 decode error:", err.Error())
|
2018-02-18 09:12:51 +00:00
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
switch ext {
|
|
|
|
case ".p12":
|
2021-07-16 04:10:34 +00:00
|
|
|
certificateKey, err = certificate.FromP12Bytes(key, cfg.Ios.Password)
|
2018-02-18 09:12:51 +00:00
|
|
|
case ".pem":
|
2021-07-16 04:10:34 +00:00
|
|
|
certificateKey, err = certificate.FromPemBytes(key, cfg.Ios.Password)
|
2018-02-18 09:12:51 +00:00
|
|
|
case ".p8":
|
|
|
|
authKey, err = token.AuthKeyFromBytes(key)
|
|
|
|
default:
|
|
|
|
err = errors.New("wrong certificate key type")
|
|
|
|
}
|
2017-07-17 03:22:48 +00:00
|
|
|
|
2018-02-18 09:12:51 +00:00
|
|
|
if err != nil {
|
2021-07-13 08:32:39 +00:00
|
|
|
logx.LogError.Error("Cert Error:", err.Error())
|
2017-07-17 03:22:48 +00:00
|
|
|
|
2018-02-18 09:12:51 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
2020-05-18 14:32:50 +00:00
|
|
|
if ext == ".p8" {
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Ios.KeyID == "" || cfg.Ios.TeamID == "" {
|
2020-05-18 14:32:50 +00:00
|
|
|
msg := "You should provide ios.KeyID and ios.TeamID for P8 token"
|
2021-07-13 08:32:39 +00:00
|
|
|
logx.LogError.Error(msg)
|
2020-05-18 14:32:50 +00:00
|
|
|
return errors.New(msg)
|
|
|
|
}
|
2017-10-25 08:49:23 +00:00
|
|
|
token := &token.Token{
|
|
|
|
AuthKey: authKey,
|
|
|
|
// KeyID from developer account (Certificates, Identifiers & Profiles -> Keys)
|
2021-07-16 04:10:34 +00:00
|
|
|
KeyID: cfg.Ios.KeyID,
|
2017-10-25 08:49:23 +00:00
|
|
|
// TeamID from developer account (View Account -> Membership)
|
2021-07-16 04:10:34 +00:00
|
|
|
TeamID: cfg.Ios.TeamID,
|
2017-10-25 08:49:23 +00:00
|
|
|
}
|
2019-12-07 23:30:24 +00:00
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
ApnsClient, err = newApnsTokenClient(cfg, token)
|
2017-07-17 03:22:48 +00:00
|
|
|
} else {
|
2021-07-16 04:10:34 +00:00
|
|
|
ApnsClient, err = newApnsClient(cfg, certificateKey)
|
2019-12-07 23:30:24 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 13:26:00 +00:00
|
|
|
if h2Transport, ok := ApnsClient.HTTPClient.Transport.(*http2.Transport); ok {
|
|
|
|
configureHTTP2ConnHealthCheck(h2Transport)
|
|
|
|
}
|
|
|
|
|
2019-12-07 23:30:24 +00:00
|
|
|
if err != nil {
|
2021-07-13 08:32:39 +00:00
|
|
|
logx.LogError.Error("Transport Error:", err.Error())
|
2019-12-07 23:30:24 +00:00
|
|
|
|
|
|
|
return err
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
2021-07-16 04:10:34 +00:00
|
|
|
|
2021-07-16 08:30:01 +00:00
|
|
|
doOnce.Do(func() {
|
|
|
|
MaxConcurrentIOSPushes = make(chan struct{}, cfg.Ios.MaxConcurrentPushes)
|
|
|
|
})
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2019-12-07 23:30:24 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
func newApnsClient(cfg config.ConfYaml, certificate tls.Certificate) (*apns2.Client, error) {
|
2019-12-07 23:30:24 +00:00
|
|
|
var client *apns2.Client
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Ios.Production {
|
2019-12-07 23:30:24 +00:00
|
|
|
client = apns2.NewClient(certificate).Production()
|
|
|
|
} else {
|
|
|
|
client = apns2.NewClient(certificate).Development()
|
|
|
|
}
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Core.HTTPProxy == "" {
|
2019-12-07 23:30:24 +00:00
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
tlsConfig := &tls.Config{
|
|
|
|
Certificates: []tls.Certificate{certificate},
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(certificate.Certificate) > 0 {
|
|
|
|
tlsConfig.BuildNameToCertificate()
|
|
|
|
}
|
|
|
|
|
|
|
|
transport := &http.Transport{
|
|
|
|
TLSClientConfig: tlsConfig,
|
2020-05-14 13:38:58 +00:00
|
|
|
DialTLS: DialTLS(tlsConfig),
|
2019-12-07 23:30:24 +00:00
|
|
|
Proxy: http.DefaultTransport.(*http.Transport).Proxy,
|
|
|
|
IdleConnTimeout: idleConnTimeout,
|
|
|
|
}
|
|
|
|
|
2021-05-14 23:05:54 +00:00
|
|
|
h2Transport, err := http2.ConfigureTransports(transport)
|
|
|
|
if err != nil {
|
2021-05-04 13:26:00 +00:00
|
|
|
return nil, err
|
2019-12-07 23:30:24 +00:00
|
|
|
}
|
2021-05-14 23:05:54 +00:00
|
|
|
|
|
|
|
configureHTTP2ConnHealthCheck(h2Transport)
|
|
|
|
|
2019-12-07 23:30:24 +00:00
|
|
|
client.HTTPClient.Transport = transport
|
|
|
|
|
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
func newApnsTokenClient(cfg config.ConfYaml, token *token.Token) (*apns2.Client, error) {
|
2019-12-07 23:30:24 +00:00
|
|
|
var client *apns2.Client
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Ios.Production {
|
2019-12-07 23:30:24 +00:00
|
|
|
client = apns2.NewTokenClient(token).Production()
|
|
|
|
} else {
|
|
|
|
client = apns2.NewTokenClient(token).Development()
|
|
|
|
}
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Core.HTTPProxy == "" {
|
2019-12-07 23:30:24 +00:00
|
|
|
return client, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
transport := &http.Transport{
|
2020-05-14 13:38:58 +00:00
|
|
|
DialTLS: DialTLS(nil),
|
2019-12-07 23:30:24 +00:00
|
|
|
Proxy: http.DefaultTransport.(*http.Transport).Proxy,
|
|
|
|
IdleConnTimeout: idleConnTimeout,
|
|
|
|
}
|
|
|
|
|
2021-05-14 23:05:54 +00:00
|
|
|
h2Transport, err := http2.ConfigureTransports(transport)
|
|
|
|
if err != nil {
|
2021-05-04 13:26:00 +00:00
|
|
|
return nil, err
|
2019-12-07 23:30:24 +00:00
|
|
|
}
|
|
|
|
|
2021-05-14 23:05:54 +00:00
|
|
|
configureHTTP2ConnHealthCheck(h2Transport)
|
|
|
|
|
2019-12-07 23:30:24 +00:00
|
|
|
client.HTTPClient.Transport = transport
|
|
|
|
|
|
|
|
return client, nil
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
2021-05-04 13:26:00 +00:00
|
|
|
func configureHTTP2ConnHealthCheck(h2Transport *http2.Transport) {
|
|
|
|
h2Transport.ReadIdleTimeout = 1 * time.Second
|
|
|
|
h2Transport.PingTimeout = 1 * time.Second
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
func iosAlertDictionary(payload *payload.Payload, req PushNotification) *payload.Payload {
|
|
|
|
// Alert dictionary
|
|
|
|
|
|
|
|
if len(req.Title) > 0 {
|
|
|
|
payload.AlertTitle(req.Title)
|
|
|
|
}
|
|
|
|
|
2020-12-16 22:48:00 +00:00
|
|
|
if len(req.Message) > 0 && len(req.Title) > 0 {
|
|
|
|
payload.AlertBody(req.Message)
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
if len(req.Alert.Title) > 0 {
|
|
|
|
payload.AlertTitle(req.Alert.Title)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apple Watch & Safari display this string as part of the notification interface.
|
|
|
|
if len(req.Alert.Subtitle) > 0 {
|
|
|
|
payload.AlertSubtitle(req.Alert.Subtitle)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.TitleLocKey) > 0 {
|
|
|
|
payload.AlertTitleLocKey(req.Alert.TitleLocKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.LocArgs) > 0 {
|
|
|
|
payload.AlertLocArgs(req.Alert.LocArgs)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.TitleLocArgs) > 0 {
|
|
|
|
payload.AlertTitleLocArgs(req.Alert.TitleLocArgs)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.Body) > 0 {
|
|
|
|
payload.AlertBody(req.Alert.Body)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.LaunchImage) > 0 {
|
|
|
|
payload.AlertLaunchImage(req.Alert.LaunchImage)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.LocKey) > 0 {
|
|
|
|
payload.AlertLocKey(req.Alert.LocKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.Action) > 0 {
|
|
|
|
payload.AlertAction(req.Alert.Action)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.Alert.ActionLocKey) > 0 {
|
|
|
|
payload.AlertActionLocKey(req.Alert.ActionLocKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
// General
|
|
|
|
if len(req.Category) > 0 {
|
|
|
|
payload.Category(req.Category)
|
|
|
|
}
|
|
|
|
|
2018-11-20 03:02:26 +00:00
|
|
|
if len(req.Alert.SummaryArg) > 0 {
|
|
|
|
payload.AlertSummaryArg(req.Alert.SummaryArg)
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.Alert.SummaryArgCount > 0 {
|
|
|
|
payload.AlertSummaryArgCount(req.Alert.SummaryArgCount)
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
return payload
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetIOSNotification use for define iOS notification.
|
|
|
|
// The iOS Notification Payload
|
|
|
|
// ref: https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html#//apple_ref/doc/uid/TP40008194-CH17-SW1
|
2017-10-25 08:49:23 +00:00
|
|
|
func GetIOSNotification(req PushNotification) *apns2.Notification {
|
|
|
|
notification := &apns2.Notification{
|
2017-11-15 16:12:09 +00:00
|
|
|
ApnsID: req.ApnsID,
|
|
|
|
Topic: req.Topic,
|
|
|
|
CollapseID: req.CollapseID,
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
2019-11-02 06:27:22 +00:00
|
|
|
if req.Expiration != nil {
|
|
|
|
notification.Expiration = time.Unix(*req.Expiration, 0)
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
2020-02-07 03:50:20 +00:00
|
|
|
if len(req.Priority) > 0 {
|
|
|
|
if req.Priority == "normal" {
|
|
|
|
notification.Priority = apns2.PriorityLow
|
|
|
|
} else if req.Priority == "high" {
|
|
|
|
notification.Priority = apns2.PriorityHigh
|
|
|
|
}
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 15:36:38 +00:00
|
|
|
if len(req.PushType) > 0 {
|
|
|
|
notification.PushType = apns2.EPushType(req.PushType)
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
payload := payload.NewPayload()
|
|
|
|
|
2021-01-23 01:48:58 +00:00
|
|
|
// add alert object if message length > 0 and title is empty
|
|
|
|
if len(req.Message) > 0 && req.Title == "" {
|
2017-07-17 03:22:48 +00:00
|
|
|
payload.Alert(req.Message)
|
|
|
|
}
|
|
|
|
|
|
|
|
// zero value for clear the badge on the app icon.
|
|
|
|
if req.Badge != nil && *req.Badge >= 0 {
|
|
|
|
payload.Badge(*req.Badge)
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.MutableContent {
|
|
|
|
payload.MutableContent()
|
|
|
|
}
|
|
|
|
|
2018-11-20 09:01:35 +00:00
|
|
|
switch req.Sound.(type) {
|
|
|
|
// from http request binding
|
|
|
|
case map[string]interface{}:
|
|
|
|
result := &Sound{}
|
|
|
|
_ = mapstructure.Decode(req.Sound, &result)
|
|
|
|
payload.Sound(result)
|
2019-03-13 16:01:15 +00:00
|
|
|
// from http request binding for non critical alerts
|
|
|
|
case string:
|
|
|
|
payload.Sound(&req.Sound)
|
2018-11-20 09:01:35 +00:00
|
|
|
case Sound:
|
2018-08-28 03:02:13 +00:00
|
|
|
payload.Sound(&req.Sound)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.SoundName) > 0 {
|
|
|
|
payload.SoundName(req.SoundName)
|
|
|
|
}
|
|
|
|
|
|
|
|
if req.SoundVolume > 0 {
|
|
|
|
payload.SoundVolume(req.SoundVolume)
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if req.ContentAvailable {
|
|
|
|
payload.ContentAvailable()
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(req.URLArgs) > 0 {
|
|
|
|
payload.URLArgs(req.URLArgs)
|
|
|
|
}
|
|
|
|
|
2018-01-05 09:08:43 +00:00
|
|
|
if len(req.ThreadID) > 0 {
|
|
|
|
payload.ThreadID(req.ThreadID)
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
for k, v := range req.Data {
|
|
|
|
payload.Custom(k, v)
|
|
|
|
}
|
|
|
|
|
|
|
|
payload = iosAlertDictionary(payload, req)
|
|
|
|
|
|
|
|
notification.Payload = payload
|
|
|
|
|
|
|
|
return notification
|
|
|
|
}
|
|
|
|
|
2021-07-16 04:10:34 +00:00
|
|
|
func getApnsClient(cfg config.ConfYaml, req PushNotification) (client *apns2.Client) {
|
2017-10-25 14:51:33 +00:00
|
|
|
if req.Production {
|
|
|
|
client = ApnsClient.Production()
|
|
|
|
} else if req.Development {
|
|
|
|
client = ApnsClient.Development()
|
|
|
|
} else {
|
2021-07-16 04:10:34 +00:00
|
|
|
if cfg.Ios.Production {
|
2017-10-25 14:51:33 +00:00
|
|
|
client = ApnsClient.Production()
|
|
|
|
} else {
|
|
|
|
client = ApnsClient.Development()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
// PushToIOS provide send notification to APNs server.
|
2021-07-16 08:30:01 +00:00
|
|
|
func PushToIOS(req PushNotification) {
|
2021-07-13 08:32:39 +00:00
|
|
|
logx.LogAccess.Debug("Start push notification for iOS")
|
2017-07-17 03:22:48 +00:00
|
|
|
|
2021-07-17 12:14:19 +00:00
|
|
|
if req.Cfg.Core.Sync && !core.IsLocalQueue(core.Queue(req.Cfg.Queue.Engine)) {
|
|
|
|
req.Cfg.Core.Sync = false
|
|
|
|
}
|
|
|
|
|
2017-07-17 03:22:48 +00:00
|
|
|
var (
|
|
|
|
retryCount = 0
|
2021-07-16 08:30:01 +00:00
|
|
|
maxRetry = req.Cfg.Ios.MaxRetry
|
2017-07-17 03:22:48 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if req.Retry > 0 && req.Retry < maxRetry {
|
|
|
|
maxRetry = req.Retry
|
|
|
|
}
|
|
|
|
|
|
|
|
Retry:
|
2021-01-23 01:39:06 +00:00
|
|
|
var newTokens []string
|
2017-07-17 03:22:48 +00:00
|
|
|
|
|
|
|
notification := GetIOSNotification(req)
|
2021-07-16 08:30:01 +00:00
|
|
|
client := getApnsClient(req.Cfg, req)
|
2017-07-17 03:22:48 +00:00
|
|
|
|
2020-04-23 13:02:53 +00:00
|
|
|
var wg sync.WaitGroup
|
2017-07-17 03:22:48 +00:00
|
|
|
for _, token := range req.Tokens {
|
2020-04-23 13:02:53 +00:00
|
|
|
// occupy push slot
|
|
|
|
MaxConcurrentIOSPushes <- struct{}{}
|
|
|
|
wg.Add(1)
|
2020-07-14 15:57:23 +00:00
|
|
|
go func(notification apns2.Notification, token string) {
|
2020-04-23 13:02:53 +00:00
|
|
|
notification.DeviceToken = token
|
|
|
|
|
|
|
|
// send ios notification
|
2020-07-14 15:57:23 +00:00
|
|
|
res, err := client.Push(¬ification)
|
2020-07-14 14:52:39 +00:00
|
|
|
if err != nil || (res != nil && res.StatusCode != http.StatusOK) {
|
2020-04-23 13:02:53 +00:00
|
|
|
if err == nil {
|
|
|
|
// error message:
|
|
|
|
// ref: https://github.com/sideshow/apns2/blob/master/response.go#L14-L65
|
|
|
|
err = errors.New(res.Reason)
|
|
|
|
}
|
|
|
|
// apns server error
|
2021-07-16 08:30:01 +00:00
|
|
|
logPush(req.Cfg, core.FailedPush, token, req, err)
|
2020-04-23 13:02:53 +00:00
|
|
|
|
2021-07-16 08:30:01 +00:00
|
|
|
if req.Cfg.Core.Sync {
|
|
|
|
req.AddLog(createLogPushEntry(req.Cfg, core.FailedPush, token, req, err))
|
|
|
|
} else if req.Cfg.Core.FeedbackURL != "" {
|
2021-07-13 08:32:39 +00:00
|
|
|
go func(logger *logrus.Logger, log logx.LogPushEntry, url string, timeout int64) {
|
2020-04-23 13:02:53 +00:00
|
|
|
err := DispatchFeedback(log, url, timeout)
|
|
|
|
if err != nil {
|
|
|
|
logger.Error(err)
|
|
|
|
}
|
2021-07-16 08:30:01 +00:00
|
|
|
}(logx.LogError, createLogPushEntry(req.Cfg, core.FailedPush, token, req, err), req.Cfg.Core.FeedbackURL, req.Cfg.Core.FeedbackTimeout)
|
2020-04-23 13:02:53 +00:00
|
|
|
}
|
|
|
|
|
2021-07-13 15:58:47 +00:00
|
|
|
status.StatStorage.AddIosError(1)
|
2020-05-06 11:37:06 +00:00
|
|
|
// We should retry only "retryable" statuses. More info about response:
|
|
|
|
// https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/handling_notification_responses_from_apns
|
2020-07-14 14:52:39 +00:00
|
|
|
if res != nil && res.StatusCode >= http.StatusInternalServerError {
|
2020-05-06 11:37:06 +00:00
|
|
|
newTokens = append(newTokens, token)
|
|
|
|
}
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 15:57:23 +00:00
|
|
|
if res != nil && res.Sent() {
|
2021-07-16 08:30:01 +00:00
|
|
|
logPush(req.Cfg, core.SucceededPush, token, req, nil)
|
2021-07-13 15:58:47 +00:00
|
|
|
status.StatStorage.AddIosSuccess(1)
|
2020-04-23 13:02:53 +00:00
|
|
|
}
|
2021-07-16 08:30:01 +00:00
|
|
|
|
2020-04-23 13:02:53 +00:00
|
|
|
// free push slot
|
|
|
|
<-MaxConcurrentIOSPushes
|
|
|
|
wg.Done()
|
2020-07-14 15:57:23 +00:00
|
|
|
}(*notification, token)
|
2017-07-17 03:22:48 +00:00
|
|
|
}
|
2021-07-16 08:30:01 +00:00
|
|
|
|
2020-04-23 13:02:53 +00:00
|
|
|
wg.Wait()
|
2017-07-17 03:22:48 +00:00
|
|
|
|
2020-07-14 15:57:23 +00:00
|
|
|
if len(newTokens) > 0 && retryCount < maxRetry {
|
2017-07-17 03:22:48 +00:00
|
|
|
retryCount++
|
|
|
|
|
|
|
|
// resend fail token
|
|
|
|
req.Tokens = newTokens
|
|
|
|
goto Retry
|
|
|
|
}
|
|
|
|
}
|