Rate limit Web Push checks
No need to re-check that a Web Push subscription is valid every time a downstream connects. Mobile devices may reconnect pretty frequently. Check at most once a day.
This commit is contained in:
parent
b6c0841291
commit
8f1f67f1f0
@ -255,9 +255,11 @@ type WebPushConfig struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WebPushSubscription struct {
|
type WebPushSubscription struct {
|
||||||
ID int64
|
ID int64
|
||||||
Endpoint string
|
Endpoint string
|
||||||
Keys struct {
|
CreatedAt, UpdatedAt time.Time // read-only
|
||||||
|
|
||||||
|
Keys struct {
|
||||||
Auth string
|
Auth string
|
||||||
P256DH string
|
P256DH string
|
||||||
VAPID string
|
VAPID string
|
||||||
|
@ -849,7 +849,7 @@ func (db *PostgresDB) ListWebPushSubscriptions(ctx context.Context, userID, netw
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.db.QueryContext(ctx, `
|
rows, err := db.db.QueryContext(ctx, `
|
||||||
SELECT id, endpoint, key_auth, key_p256dh, key_vapid
|
SELECT id, endpoint, created_at, updated_at, key_auth, key_p256dh, key_vapid
|
||||||
FROM "WebPushSubscription"
|
FROM "WebPushSubscription"
|
||||||
WHERE "user" = $1 AND network IS NOT DISTINCT FROM $2`, userID, nullNetworkID)
|
WHERE "user" = $1 AND network IS NOT DISTINCT FROM $2`, userID, nullNetworkID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -860,7 +860,7 @@ func (db *PostgresDB) ListWebPushSubscriptions(ctx context.Context, userID, netw
|
|||||||
var subs []WebPushSubscription
|
var subs []WebPushSubscription
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var sub WebPushSubscription
|
var sub WebPushSubscription
|
||||||
if err := rows.Scan(&sub.ID, &sub.Endpoint, &sub.Keys.Auth, &sub.Keys.P256DH, &sub.Keys.VAPID); err != nil {
|
if err := rows.Scan(&sub.ID, &sub.Endpoint, &sub.CreatedAt, &sub.UpdatedAt, &sub.Keys.Auth, &sub.Keys.P256DH, &sub.Keys.VAPID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
subs = append(subs, sub)
|
subs = append(subs, sub)
|
||||||
|
@ -1067,7 +1067,7 @@ func (db *SqliteDB) ListWebPushSubscriptions(ctx context.Context, userID, networ
|
|||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.db.QueryContext(ctx, `
|
rows, err := db.db.QueryContext(ctx, `
|
||||||
SELECT id, endpoint, key_auth, key_p256dh, key_vapid
|
SELECT id, endpoint, created_at, updated_at, key_auth, key_p256dh, key_vapid
|
||||||
FROM WebPushSubscription
|
FROM WebPushSubscription
|
||||||
WHERE user = ? AND network IS ?`, userID, nullNetworkID)
|
WHERE user = ? AND network IS ?`, userID, nullNetworkID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1078,9 +1078,12 @@ func (db *SqliteDB) ListWebPushSubscriptions(ctx context.Context, userID, networ
|
|||||||
var subs []WebPushSubscription
|
var subs []WebPushSubscription
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var sub WebPushSubscription
|
var sub WebPushSubscription
|
||||||
if err := rows.Scan(&sub.ID, &sub.Endpoint, &sub.Keys.Auth, &sub.Keys.P256DH, &sub.Keys.VAPID); err != nil {
|
var createdAt, updatedAt sqliteTime
|
||||||
|
if err := rows.Scan(&sub.ID, &sub.Endpoint, &createdAt, &updatedAt, &sub.Keys.Auth, &sub.Keys.P256DH, &sub.Keys.VAPID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
sub.CreatedAt = createdAt.Time
|
||||||
|
sub.UpdatedAt = updatedAt.Time
|
||||||
subs = append(subs, sub)
|
subs = append(subs, sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3230,10 +3230,13 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateSub := true
|
||||||
oldSub := findWebPushSubscription(subs, endpoint)
|
oldSub := findWebPushSubscription(subs, endpoint)
|
||||||
if oldSub != nil {
|
if oldSub != nil {
|
||||||
// Update the old subscription instead of creating a new one
|
// Update the old subscription instead of creating a new one
|
||||||
newSub.ID = oldSub.ID
|
newSub.ID = oldSub.ID
|
||||||
|
// Rate-limit subscription checks
|
||||||
|
updateSub = time.Since(oldSub.UpdatedAt) > webpushCheckSubscriptionDelay || oldSub.Keys != newSub.Keys
|
||||||
}
|
}
|
||||||
|
|
||||||
var networkID int64
|
var networkID int64
|
||||||
@ -3242,32 +3245,34 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send a test Web Push message, to make sure the endpoint is valid
|
// Send a test Web Push message, to make sure the endpoint is valid
|
||||||
err = dc.srv.sendWebPush(ctx, &webpush.Subscription{
|
if updateSub {
|
||||||
Endpoint: newSub.Endpoint,
|
err = dc.srv.sendWebPush(ctx, &webpush.Subscription{
|
||||||
Keys: webpush.Keys{
|
Endpoint: newSub.Endpoint,
|
||||||
Auth: newSub.Keys.Auth,
|
Keys: webpush.Keys{
|
||||||
P256dh: newSub.Keys.P256DH,
|
Auth: newSub.Keys.Auth,
|
||||||
},
|
P256dh: newSub.Keys.P256DH,
|
||||||
}, newSub.Keys.VAPID, &irc.Message{
|
},
|
||||||
Command: "NOTE",
|
}, newSub.Keys.VAPID, &irc.Message{
|
||||||
Params: []string{"WEBPUSH", "REGISTERED", "Push notifications enabled"},
|
Command: "NOTE",
|
||||||
})
|
Params: []string{"WEBPUSH", "REGISTERED", "Push notifications enabled"},
|
||||||
if err != nil {
|
})
|
||||||
dc.logger.Printf("failed to send Web push notification to endpoint %q: %v", newSub.Endpoint, err)
|
if err != nil {
|
||||||
return ircError{&irc.Message{
|
dc.logger.Printf("failed to send Web push notification to endpoint %q: %v", newSub.Endpoint, err)
|
||||||
Command: "FAIL",
|
return ircError{&irc.Message{
|
||||||
Params: []string{"WEBPUSH", "INVALID_PARAMS", subcommand, "Invalid endpoint"},
|
Command: "FAIL",
|
||||||
}}
|
Params: []string{"WEBPUSH", "INVALID_PARAMS", subcommand, "Invalid endpoint"},
|
||||||
}
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: limit max number of subscriptions, prune old ones
|
// TODO: limit max number of subscriptions, prune old ones
|
||||||
|
|
||||||
if err := dc.user.srv.db.StoreWebPushSubscription(ctx, dc.user.ID, networkID, &newSub); err != nil {
|
if err := dc.user.srv.db.StoreWebPushSubscription(ctx, dc.user.ID, networkID, &newSub); err != nil {
|
||||||
dc.logger.Printf("failed to store Web push subscription: %v", err)
|
dc.logger.Printf("failed to store Web push subscription: %v", err)
|
||||||
return ircError{&irc.Message{
|
return ircError{&irc.Message{
|
||||||
Command: "FAIL",
|
Command: "FAIL",
|
||||||
Params: []string{"WEBPUSH", "INTERNAL_ERROR", subcommand, "Internal error"},
|
Params: []string{"WEBPUSH", "INTERNAL_ERROR", subcommand, "Internal error"},
|
||||||
}}
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.SendMessage(&irc.Message{
|
dc.SendMessage(&irc.Message{
|
||||||
|
@ -39,6 +39,7 @@ var upstreamMessageBurst = 10
|
|||||||
var backlogTimeout = 10 * time.Second
|
var backlogTimeout = 10 * time.Second
|
||||||
var handleDownstreamMessageTimeout = 10 * time.Second
|
var handleDownstreamMessageTimeout = 10 * time.Second
|
||||||
var downstreamRegisterTimeout = 30 * time.Second
|
var downstreamRegisterTimeout = 30 * time.Second
|
||||||
|
var webpushCheckSubscriptionDelay = 24 * time.Hour
|
||||||
var chatHistoryLimit = 1000
|
var chatHistoryLimit = 1000
|
||||||
var backlogLimit = 4000
|
var backlogLimit = 4000
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user