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

pull/89/head
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 ( const (
firebaseControlTopic = "~control" // See Android if changed firebaseControlTopic = "~control" // See Android if changed
emptyMessageBody = "triggered" emptyMessageBody = "triggered"
fcmMessageLimitReal = 4100 // see maybeTruncateFCMMessage for details
) )
// New instantiates a new Server. It creates the cache and adds a Firebase // 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", Priority: "high",
} }
} }
_, err := msg.Send(context.Background(), &messaging.Message{ _, err := msg.Send(context.Background(), maybeTruncateFCMMessage(&messaging.Message{
Topic: m.Topic, Topic: m.Topic,
Data: data, Data: data,
Android: androidConfig, Android: androidConfig,
}) }))
return err return err
}, nil }, 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 // Run executes the main server. It listens on HTTP (+ HTTPS, if configured), and starts
// a manager go routine to print stats and prune messages. // a manager go routine to print stats and prune messages.
func (s *Server) Run() error { func (s *Server) Run() error {

View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"context" "context"
"encoding/json" "encoding/json"
"firebase.google.com/go/messaging"
"fmt" "fmt"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"net/http" "net/http"
@ -591,6 +592,35 @@ func TestServer_UnifiedPushDiscovery(t *testing.T) {
require.Equal(t, `{"unifiedpush":{"version":1}}`+"\n", response.Body.String()) 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 { func newTestConfig(t *testing.T) *Config {
conf := NewConfig() conf := NewConfig()
conf.CacheFile = filepath.Join(t.TempDir(), "cache.db") conf.CacheFile = filepath.Join(t.TempDir(), "cache.db")