handle notifications at-most-once

This commit is contained in:
Andrea Maria Piana 2020-08-26 07:54:00 +02:00
parent d7222c89e0
commit 640f5533ca
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
9 changed files with 120 additions and 22 deletions

View File

@ -2125,7 +2125,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
continue
}
logger.Debug("Handling PushNotificationRequest")
if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ParsedMessage.Interface().(protobuf.PushNotificationRequest)); err != nil {
if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ID, msg.ParsedMessage.Interface().(protobuf.PushNotificationRequest)); err != nil {
logger.Warn("failed to handle PushNotificationRequest", zap.Error(err))
}
// We continue in any case, no changes to messenger

View File

@ -2,7 +2,6 @@ package pushnotificationserver
import (
"bytes"
"encoding/hex"
"encoding/json"
"io/ioutil"
"net/http"
@ -60,9 +59,9 @@ func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*Requ
Message: defaultNotificationMessage,
Topic: registration.ApnTopic,
Data: &GoRushRequestData{
EncryptedMessage: hex.EncodeToString(request.Message),
EncryptedMessage: types.EncodeHex(request.Message),
ChatID: types.EncodeHex(request.ChatId),
PublicKey: hex.EncodeToString(request.PublicKey),
PublicKey: types.EncodeHex(request.PublicKey),
},
})
}

View File

@ -1,11 +1,11 @@
package pushnotificationserver
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/protobuf"
)
@ -13,10 +13,10 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
message1 := []byte("message-1")
message2 := []byte("message-2")
message3 := []byte("message-3")
hexMessage1 := hex.EncodeToString(message1)
hexMessage2 := hex.EncodeToString(message2)
hexMessage3 := hex.EncodeToString(message3)
chatID := "chat-id"
hexMessage1 := types.EncodeHex(message1)
hexMessage2 := types.EncodeHex(message2)
hexMessage3 := types.EncodeHex(message3)
chatID := []byte("chat-id")
publicKey1 := []byte("public-key-1")
publicKey2 := []byte("public-key-2")
installationID1 := "installation-id-1"
@ -76,8 +76,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
Message: defaultNotificationMessage,
Data: &GoRushRequestData{
EncryptedMessage: hexMessage1,
ChatID: chatID,
PublicKey: hex.EncodeToString(publicKey1),
ChatID: types.EncodeHex(chatID),
PublicKey: types.EncodeHex(publicKey1),
},
},
{
@ -86,8 +86,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
Message: defaultNotificationMessage,
Data: &GoRushRequestData{
EncryptedMessage: hexMessage2,
ChatID: chatID,
PublicKey: hex.EncodeToString(publicKey1),
ChatID: types.EncodeHex(chatID),
PublicKey: types.EncodeHex(publicKey1),
},
},
{
@ -96,8 +96,8 @@ func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
Message: defaultNotificationMessage,
Data: &GoRushRequestData{
EncryptedMessage: hexMessage3,
ChatID: chatID,
PublicKey: hex.EncodeToString(publicKey2),
ChatID: types.EncodeHex(chatID),
PublicKey: types.EncodeHex(publicKey2),
},
},
},

View File

@ -2,6 +2,8 @@
// sources:
// 1593601728_initial_schema.down.sql (200B)
// 1593601728_initial_schema.up.sql (675B)
// 1598419937_add_push_notifications_table.down.sql (51B)
// 1598419937_add_push_notifications_table.up.sql (104B)
// doc.go (382B)
package migrations
@ -86,7 +88,7 @@ func _1593601728_initial_schemaDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1595840384, 0)}
info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1596198373, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x8a, 0x61, 0x81, 0x57, 0x45, 0x9b, 0x97, 0x9b, 0x1f, 0xf6, 0x94, 0x8a, 0x20, 0xb3, 0x2b, 0xff, 0x69, 0x49, 0xf4, 0x58, 0xcc, 0xd0, 0x55, 0xcc, 0x9a, 0x8b, 0xb6, 0x7f, 0x29, 0x53, 0xc1}}
return a, nil
}
@ -106,11 +108,51 @@ func _1593601728_initial_schemaUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1595840384, 0)}
info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1596198373, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x61, 0x90, 0x79, 0xd9, 0x14, 0x65, 0xe9, 0x96, 0x53, 0x17, 0x33, 0x54, 0xeb, 0x8b, 0x5d, 0x95, 0x99, 0x10, 0x36, 0x58, 0xdd, 0xb2, 0xbf, 0x45, 0xd9, 0xbb, 0xc4, 0x92, 0xe, 0xce, 0x2}}
return a, nil
}
var __1598419937_add_push_notifications_tableDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x2f\x4e\x2d\x2a\x4b\x2d\x42\x11\x2b\xb6\xe6\x02\x04\x00\x00\xff\xff\xb7\xdc\x38\x53\x33\x00\x00\x00")
func _1598419937_add_push_notifications_tableDownSqlBytes() ([]byte, error) {
return bindataRead(
__1598419937_add_push_notifications_tableDownSql,
"1598419937_add_push_notifications_table.down.sql",
)
}
func _1598419937_add_push_notifications_tableDownSql() (*asset, error) {
bytes, err := _1598419937_add_push_notifications_tableDownSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1598419937_add_push_notifications_table.down.sql", size: 51, mode: os.FileMode(0644), modTime: time.Unix(1598420015, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x98, 0xc8, 0x30, 0x45, 0x5b, 0xc5, 0x7d, 0x13, 0x5d, 0xe7, 0xc8, 0x23, 0x43, 0xf7, 0xdc, 0x9c, 0xe2, 0xdd, 0x63, 0xf0, 0xb7, 0x16, 0x40, 0xc, 0xda, 0xb9, 0x16, 0x70, 0x2b, 0x5a, 0x7e}}
return a, nil
}
var __1598419937_add_push_notifications_tableUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x0e\x72\x75\x0c\x71\x55\x08\x71\x74\xf2\x71\x55\xf0\x74\x53\xf0\xf3\x0f\x51\x70\x8d\xf0\x0c\x0e\x09\x56\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x2f\x4e\x2d\x2a\x4b\x2d\x42\x11\x2b\x56\xd0\xe0\x52\x50\xc8\x4c\x51\x70\xf2\xf1\x77\x02\xeb\xf6\x0b\xf5\xf1\xd1\xe1\x52\x50\x08\xf5\xf3\x0c\x0c\x75\xd5\xc8\x4c\xd1\xe4\xd2\xb4\xe6\x02\x04\x00\x00\xff\xff\xf3\xc8\x52\x6b\x68\x00\x00\x00")
func _1598419937_add_push_notifications_tableUpSqlBytes() ([]byte, error) {
return bindataRead(
__1598419937_add_push_notifications_tableUpSql,
"1598419937_add_push_notifications_table.up.sql",
)
}
func _1598419937_add_push_notifications_tableUpSql() (*asset, error) {
bytes, err := _1598419937_add_push_notifications_tableUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1598419937_add_push_notifications_table.up.sql", size: 104, mode: os.FileMode(0644), modTime: time.Unix(1598419975, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2, 0x3e, 0xef, 0xf, 0xc2, 0xdf, 0xbc, 0x99, 0x7a, 0xc2, 0xd3, 0x64, 0x4f, 0x4c, 0x7e, 0xfc, 0x2e, 0x8c, 0xa7, 0x54, 0xd3, 0x4d, 0x25, 0x98, 0x41, 0xbc, 0xea, 0xd7, 0x2, 0xc1, 0xd0, 0x52}}
return a, nil
}
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00")
func docGoBytes() ([]byte, error) {
@ -126,7 +168,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595840384, 0)}
info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1596198373, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}}
return a, nil
}
@ -226,6 +268,10 @@ var _bindata = map[string]func() (*asset, error){
"1593601728_initial_schema.up.sql": _1593601728_initial_schemaUpSql,
"1598419937_add_push_notifications_table.down.sql": _1598419937_add_push_notifications_tableDownSql,
"1598419937_add_push_notifications_table.up.sql": _1598419937_add_push_notifications_tableUpSql,
"doc.go": docGo,
}
@ -272,6 +318,8 @@ type bintree struct {
var _bintree = &bintree{nil, map[string]*bintree{
"1593601728_initial_schema.down.sql": &bintree{_1593601728_initial_schemaDownSql, map[string]*bintree{}},
"1593601728_initial_schema.up.sql": &bintree{_1593601728_initial_schemaUpSql, map[string]*bintree{}},
"1598419937_add_push_notifications_table.down.sql": &bintree{_1598419937_add_push_notifications_tableDownSql, map[string]*bintree{}},
"1598419937_add_push_notifications_table.up.sql": &bintree{_1598419937_add_push_notifications_tableUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}

View File

@ -0,0 +1 @@
DROP TABLE push_notification_server_notifications;

View File

@ -0,0 +1,4 @@
CREATE TABLE IF NOT EXISTS push_notification_server_notifications (
id BLOB NOT NULL,
UNIQUE(id)
);

View File

@ -6,6 +6,7 @@ import (
"strings"
"github.com/golang/protobuf/proto"
sqlite3 "github.com/mutecomm/go-sqlcipher"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/protobuf"
@ -32,6 +33,8 @@ type Persistence interface {
GetIdentity() (*ecdsa.PrivateKey, error)
// SaveIdentity saves the server identity key
SaveIdentity(*ecdsa.PrivateKey) error
// PushNotificationExists checks whether a push notification exists and inserts it otherwise
PushNotificationExists([]byte) (bool, error)
}
type SQLitePersistence struct {
@ -178,3 +181,14 @@ func (p *SQLitePersistence) GetIdentity() (*ecdsa.PrivateKey, error) {
}
return pk, nil
}
func (p *SQLitePersistence) PushNotificationExists(messageID []byte) (bool, error) {
_, err := p.db.Exec(`INSERT INTO push_notification_server_notifications VALUES (?)`, messageID)
if err != nil && err.(sqlite3.Error).ExtendedCode == sqlite3.ErrConstraintUnique {
return true, nil
} else if err != nil {
return false, err
}
return false, nil
}

View File

@ -81,3 +81,22 @@ func (s *SQLitePersistenceSuite) TestSaveDifferentIdenities() {
s.Require().NoError(s.persistence.SaveIdentity(key1))
s.Require().Error(s.persistence.SaveIdentity(key2))
}
func (s *SQLitePersistenceSuite) TestExists() {
messageID1 := []byte("1")
messageID2 := []byte("2")
result, err := s.persistence.PushNotificationExists(messageID1)
s.Require().NoError(err)
s.Require().False(result)
result, err = s.persistence.PushNotificationExists(messageID1)
s.Require().NoError(err)
s.Require()
s.Require().True(result)
result, err = s.persistence.PushNotificationExists(messageID2)
s.Require().NoError(err)
s.Require().False(result)
}

View File

@ -137,8 +137,21 @@ func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, message
// HandlePushNotificationRequest will send a gorush notification and send a response back to the user
func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey,
messageID []byte,
request protobuf.PushNotificationRequest) error {
s.config.Logger.Info("handling pn request")
s.config.Logger.Info("handling pn request", zap.Binary("message-id", messageID))
// This is at-most-once semantic for now
exists, err := s.persistence.PushNotificationExists(messageID)
if err != nil {
return err
}
if exists {
s.config.Logger.Info("already handled")
return nil
}
response := s.buildPushNotificationRequestResponseAndSendNotification(&request)
if response == nil {
return nil