Support HTTP proxy for APNs (#445)
This commit is contained in:
parent
6f2b614265
commit
1edfa9f532
|
@ -77,7 +77,7 @@ A push notification micro server using [Gin](https://github.com/gin-gonic/gin) f
|
||||||
- Support store app stat to memory, [Redis](http://redis.io/), [BoltDB](https://github.com/boltdb/bolt), [BuntDB](https://github.com/tidwall/buntdb), [LevelDB](https://github.com/syndtr/goleveldb) or [BadgerDB](https://github.com/dgraph-io/badger).
|
- Support store app stat to memory, [Redis](http://redis.io/), [BoltDB](https://github.com/boltdb/bolt), [BuntDB](https://github.com/tidwall/buntdb), [LevelDB](https://github.com/syndtr/goleveldb) or [BadgerDB](https://github.com/dgraph-io/badger).
|
||||||
- Support `p8`, `p12` or `pem` format of iOS certificate file.
|
- Support `p8`, `p12` or `pem` format of iOS certificate file.
|
||||||
- Support `/sys/stats` show response time, status code count, etc.
|
- Support `/sys/stats` show response time, status code count, etc.
|
||||||
- Support for HTTP proxy to Google server (FCM).
|
- Support for HTTP proxy.
|
||||||
- Support retry send notification if server response is fail.
|
- Support retry send notification if server response is fail.
|
||||||
- Support expose [prometheus](https://prometheus.io/) metrics.
|
- Support expose [prometheus](https://prometheus.io/) metrics.
|
||||||
- Support install TLS certificates from [Let's Encrypt](https://letsencrypt.org/) automatically.
|
- Support install TLS certificates from [Let's Encrypt](https://letsencrypt.org/) automatically.
|
||||||
|
@ -103,7 +103,7 @@ core:
|
||||||
key_path: "key.pem"
|
key_path: "key.pem"
|
||||||
cert_base64: ""
|
cert_base64: ""
|
||||||
key_base64: ""
|
key_base64: ""
|
||||||
http_proxy: "" # only working for FCM server
|
http_proxy: ""
|
||||||
pid:
|
pid:
|
||||||
enabled: false
|
enabled: false
|
||||||
path: "gorush.pid"
|
path: "gorush.pid"
|
||||||
|
@ -254,7 +254,7 @@ Server Options:
|
||||||
-t, --token <token> Notification token
|
-t, --token <token> Notification token
|
||||||
-e, --engine <engine> Storage engine (memory, redis ...)
|
-e, --engine <engine> Storage engine (memory, redis ...)
|
||||||
--title <title> Notification title
|
--title <title> Notification title
|
||||||
--proxy <proxy> Proxy URL (only for GCM)
|
--proxy <proxy> Proxy URL
|
||||||
--pid <pid path> Process identifier path
|
--pid <pid path> Process identifier path
|
||||||
--redis-addr <redis addr> Redis addr (default: localhost:6379)
|
--redis-addr <redis addr> Redis addr (default: localhost:6379)
|
||||||
iOS Options:
|
iOS Options:
|
||||||
|
@ -292,7 +292,7 @@ gorush --android --topic "/topics/foo-bar" \
|
||||||
- `-t`: Device token.
|
- `-t`: Device token.
|
||||||
- `--title`: Notification title.
|
- `--title`: Notification title.
|
||||||
- `--topic`: Send messages to topics. note: don't add device token.
|
- `--topic`: Send messages to topics. note: don't add device token.
|
||||||
- `--proxy`: Set http proxy url. (only working for FCM)
|
- `--proxy`: Set http proxy url.
|
||||||
|
|
||||||
### Send iOS notification
|
### Send iOS notification
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ core:
|
||||||
key_path: "key.pem"
|
key_path: "key.pem"
|
||||||
cert_base64: ""
|
cert_base64: ""
|
||||||
key_base64: ""
|
key_base64: ""
|
||||||
http_proxy: "" # only working for FCM server
|
http_proxy: ""
|
||||||
pid:
|
pid:
|
||||||
enabled: false
|
enabled: false
|
||||||
path: "gorush.pid"
|
path: "gorush.pid"
|
||||||
|
|
|
@ -13,7 +13,7 @@ core:
|
||||||
key_path: "key.pem"
|
key_path: "key.pem"
|
||||||
cert_base64: ""
|
cert_base64: ""
|
||||||
key_base64: ""
|
key_base64: ""
|
||||||
http_proxy: "" # only working for FCM server
|
http_proxy: ""
|
||||||
pid:
|
pid:
|
||||||
enabled: false
|
enabled: false
|
||||||
path: "gorush.pid"
|
path: "gorush.pid"
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -14,8 +15,11 @@ import (
|
||||||
"github.com/sideshow/apns2/payload"
|
"github.com/sideshow/apns2/payload"
|
||||||
"github.com/sideshow/apns2/token"
|
"github.com/sideshow/apns2/token"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var idleConnTimeout = 90 * time.Second
|
||||||
|
|
||||||
// Sound sets the aps sound on the payload.
|
// Sound sets the aps sound on the payload.
|
||||||
type Sound struct {
|
type Sound struct {
|
||||||
Critical int `json:"critical,omitempty"`
|
Critical int `json:"critical,omitempty"`
|
||||||
|
@ -84,23 +88,87 @@ func InitAPNSClient() error {
|
||||||
// TeamID from developer account (View Account -> Membership)
|
// TeamID from developer account (View Account -> Membership)
|
||||||
TeamID: PushConf.Ios.TeamID,
|
TeamID: PushConf.Ios.TeamID,
|
||||||
}
|
}
|
||||||
if PushConf.Ios.Production {
|
|
||||||
ApnsClient = apns2.NewTokenClient(token).Production()
|
ApnsClient, err = newApnsTokenClient(token)
|
||||||
} else {
|
} else {
|
||||||
ApnsClient = apns2.NewTokenClient(token).Development()
|
ApnsClient, err = newApnsClient(certificateKey)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if PushConf.Ios.Production {
|
|
||||||
ApnsClient = apns2.NewClient(certificateKey).Production()
|
|
||||||
} else {
|
|
||||||
ApnsClient = apns2.NewClient(certificateKey).Development()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
LogError.Error("Transport Error:", err.Error())
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newApnsClient(certificate tls.Certificate) (*apns2.Client, error) {
|
||||||
|
var client *apns2.Client
|
||||||
|
|
||||||
|
if PushConf.Ios.Production {
|
||||||
|
client = apns2.NewClient(certificate).Production()
|
||||||
|
} else {
|
||||||
|
client = apns2.NewClient(certificate).Development()
|
||||||
|
}
|
||||||
|
|
||||||
|
if PushConf.Core.HTTPProxy == "" {
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{certificate},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(certificate.Certificate) > 0 {
|
||||||
|
tlsConfig.BuildNameToCertificate()
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
TLSClientConfig: tlsConfig,
|
||||||
|
Proxy: http.DefaultTransport.(*http.Transport).Proxy,
|
||||||
|
IdleConnTimeout: idleConnTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
transportErr := http2.ConfigureTransport(transport)
|
||||||
|
if transportErr != nil {
|
||||||
|
return nil, transportErr
|
||||||
|
}
|
||||||
|
|
||||||
|
client.HTTPClient.Transport = transport
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApnsTokenClient(token *token.Token) (*apns2.Client, error) {
|
||||||
|
var client *apns2.Client
|
||||||
|
|
||||||
|
if PushConf.Ios.Production {
|
||||||
|
client = apns2.NewTokenClient(token).Production()
|
||||||
|
} else {
|
||||||
|
client = apns2.NewTokenClient(token).Development()
|
||||||
|
}
|
||||||
|
|
||||||
|
if PushConf.Core.HTTPProxy == "" {
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.DefaultTransport.(*http.Transport).Proxy,
|
||||||
|
IdleConnTimeout: idleConnTimeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
transportErr := http2.ConfigureTransport(transport)
|
||||||
|
if transportErr != nil {
|
||||||
|
return nil, transportErr
|
||||||
|
}
|
||||||
|
|
||||||
|
client.HTTPClient.Transport = transport
|
||||||
|
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
func iosAlertDictionary(payload *payload.Payload, req PushNotification) *payload.Payload {
|
func iosAlertDictionary(payload *payload.Payload, req PushNotification) *payload.Payload {
|
||||||
// Alert dictionary
|
// Alert dictionary
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -638,6 +640,42 @@ func TestAPNSClientVaildToken(t *testing.T) {
|
||||||
assert.Equal(t, apns2.HostProduction, ApnsClient.Host)
|
assert.Equal(t, apns2.HostProduction, ApnsClient.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPNSClientUseProxy(t *testing.T) {
|
||||||
|
PushConf, _ = config.LoadConf("")
|
||||||
|
|
||||||
|
PushConf.Ios.Enabled = true
|
||||||
|
PushConf.Ios.KeyPath = "../certificate/certificate-valid.p12"
|
||||||
|
PushConf.Core.HTTPProxy = "http://127.0.0.1:8080"
|
||||||
|
_ = SetProxy(PushConf.Core.HTTPProxy)
|
||||||
|
err := InitAPNSClient()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, apns2.HostDevelopment, ApnsClient.Host)
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", apns2.HostDevelopment, nil)
|
||||||
|
actualProxyURL, err := ApnsClient.HTTPClient.Transport.(*http.Transport).Proxy(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
expectedProxyURL, _ := url.ParseRequestURI(PushConf.Core.HTTPProxy)
|
||||||
|
assert.Equal(t, expectedProxyURL, actualProxyURL)
|
||||||
|
|
||||||
|
PushConf.Ios.KeyPath = "../certificate/authkey-valid.p8"
|
||||||
|
PushConf.Ios.TeamID = "example.team"
|
||||||
|
PushConf.Ios.KeyID = "example.key"
|
||||||
|
err = InitAPNSClient()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, apns2.HostDevelopment, ApnsClient.Host)
|
||||||
|
assert.NotNil(t, ApnsClient.Token)
|
||||||
|
|
||||||
|
req, _ = http.NewRequest("GET", apns2.HostDevelopment, nil)
|
||||||
|
actualProxyURL, err = ApnsClient.HTTPClient.Transport.(*http.Transport).Proxy(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
expectedProxyURL, _ = url.ParseRequestURI(PushConf.Core.HTTPProxy)
|
||||||
|
assert.Equal(t, expectedProxyURL, actualProxyURL)
|
||||||
|
|
||||||
|
http.DefaultTransport.(*http.Transport).Proxy = nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestPushToIOS(t *testing.T) {
|
func TestPushToIOS(t *testing.T) {
|
||||||
PushConf, _ = config.LoadConf("")
|
PushConf, _ = config.LoadConf("")
|
||||||
|
|
||||||
|
|
16
main.go
16
main.go
|
@ -26,7 +26,6 @@ func main() {
|
||||||
topic string
|
topic string
|
||||||
message string
|
message string
|
||||||
token string
|
token string
|
||||||
proxy string
|
|
||||||
title string
|
title string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,7 +56,7 @@ func main() {
|
||||||
flag.BoolVar(&opts.Ios.Enabled, "ios", false, "send ios notification")
|
flag.BoolVar(&opts.Ios.Enabled, "ios", false, "send ios notification")
|
||||||
flag.BoolVar(&opts.Ios.Production, "production", false, "production mode in iOS")
|
flag.BoolVar(&opts.Ios.Production, "production", false, "production mode in iOS")
|
||||||
flag.StringVar(&topic, "topic", "", "apns topic in iOS")
|
flag.StringVar(&topic, "topic", "", "apns topic in iOS")
|
||||||
flag.StringVar(&proxy, "proxy", "", "http proxy url")
|
flag.StringVar(&opts.Core.HTTPProxy, "proxy", "", "http proxy url")
|
||||||
flag.BoolVar(&ping, "ping", false, "ping server")
|
flag.BoolVar(&ping, "ping", false, "ping server")
|
||||||
|
|
||||||
flag.Usage = usage
|
flag.Usage = usage
|
||||||
|
@ -113,14 +112,11 @@ func main() {
|
||||||
log.Fatalf("Can't load log module, error: %v", err)
|
log.Fatalf("Can't load log module, error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set http proxy for GCM
|
if opts.Core.HTTPProxy != "" {
|
||||||
if proxy != "" {
|
gorush.PushConf.Core.HTTPProxy = opts.Core.HTTPProxy
|
||||||
err = gorush.SetProxy(proxy)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
gorush.LogError.Fatalf("Set Proxy error: %v", err)
|
|
||||||
}
|
}
|
||||||
} else if gorush.PushConf.Core.HTTPProxy != "" {
|
|
||||||
|
if gorush.PushConf.Core.HTTPProxy != "" {
|
||||||
err = gorush.SetProxy(gorush.PushConf.Core.HTTPProxy)
|
err = gorush.SetProxy(gorush.PushConf.Core.HTTPProxy)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -268,7 +264,7 @@ Server Options:
|
||||||
-t, --token <token> Notification token
|
-t, --token <token> Notification token
|
||||||
-e, --engine <engine> Storage engine (memory, redis ...)
|
-e, --engine <engine> Storage engine (memory, redis ...)
|
||||||
--title <title> Notification title
|
--title <title> Notification title
|
||||||
--proxy <proxy> Proxy URL (only for GCM)
|
--proxy <proxy> Proxy URL
|
||||||
--pid <pid path> Process identifier path
|
--pid <pid path> Process identifier path
|
||||||
--redis-addr <redis addr> Redis addr (default: localhost:6379)
|
--redis-addr <redis addr> Redis addr (default: localhost:6379)
|
||||||
--ping healthy check command for container
|
--ping healthy check command for container
|
||||||
|
|
Loading…
Reference in New Issue