Truncate FCM messages if they are too long; This was trickier than expected; relates to #84

This commit is contained in:
Philipp Heckel 2022-01-04 20:43:37 +01:00
parent b4f71ce01a
commit 807d2b0d9d
2 changed files with 51 additions and 2 deletions

View file

@ -138,6 +138,7 @@ var (
const (
firebaseControlTopic = "~control" // See Android if changed
emptyMessageBody = "triggered"
fcmMessageLimitReal = 4100 // see maybeTruncateFCMMessage for details
)
// New instantiates a new Server. It creates the cache and adds a Firebase
@ -219,15 +220,33 @@ func createFirebaseSubscriber(conf *Config) (subscriber, error) {
Priority: "high",
}
}
_, err := msg.Send(context.Background(), &messaging.Message{
_, err := msg.Send(context.Background(), maybeTruncateFCMMessage(&messaging.Message{
Topic: m.Topic,
Data: data,
Android: androidConfig,
})
}))
return err
}, nil
}
// maybeTruncateFCMMessage performs best-effort truncation of FCM messages.
// The docs says the limit is 4000 characters, but the real FCM message limit is 4100 of the
// serialized payload; I tested this diligently.
func maybeTruncateFCMMessage(m *messaging.Message) *messaging.Message {
s, err := json.Marshal(m)
if err != nil {
return m
}
if len(s) > fcmMessageLimitReal {
over := len(s) - fcmMessageLimitReal
message, ok := m.Data["message"]
if ok && len(message) > over {
m.Data["message"] = message[:len(message)-over]
}
}
return m
}
// Run executes the main server. It listens on HTTP (+ HTTPS, if configured), and starts
// a manager go routine to print stats and prune messages.
func (s *Server) Run() error {

View file

@ -4,6 +4,7 @@ import (
"bufio"
"context"
"encoding/json"
"firebase.google.com/go/messaging"
"fmt"
"github.com/stretchr/testify/require"
"net/http"
@ -591,6 +592,35 @@ func TestServer_UnifiedPushDiscovery(t *testing.T) {
require.Equal(t, `{"unifiedpush":{"version":1}}`+"\n", response.Body.String())
}
func TestServer_MaybeTruncateFCMMessage(t *testing.T) {
origMessage := strings.Repeat("this is a long string", 300)
origFCMMessage := &messaging.Message{
Topic: "mytopic",
Data: map[string]string{
"id": "abcdefg",
"time": "1641324761",
"event": "message",
"topic": "mytopic",
"priority": "0",
"tags": "",
"title": "",
"message": origMessage,
},
Android: &messaging.AndroidConfig{
Priority: "high",
},
}
origMessageLength := len(origFCMMessage.Data["message"])
serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
require.Greater(t, len(serializedOrigFCMMessage), fcmMessageLimitReal) // Pre-condition
truncatedFCMMessage := maybeTruncateFCMMessage(origFCMMessage)
truncatedMessageLength := len(truncatedFCMMessage.Data["message"])
serializedTruncatedFCMMessage, _ := json.Marshal(truncatedFCMMessage)
require.Equal(t, fcmMessageLimitReal, len(serializedTruncatedFCMMessage))
require.NotEqual(t, origMessageLength, truncatedMessageLength)
}
func newTestConfig(t *testing.T) *Config {
conf := NewConfig()
conf.CacheFile = filepath.Join(t.TempDir(), "cache.db")