v0.4.3 : anti-buffering SSE (Cloudflare) - fin des derniers messages en retard au refresh

Le chat E2E d'app.ajean.link traverse Cloudflare (ajean.link, proxy orange) qui
bufferise le flux SSE par lots : la queue du replay arrivait 20-30s apres le reste,
de facon intermittente (surtout Safari iOS). Corrige par la technique standard :
padding d'amorcage en tete de flux + padding sur caught_up (force le flush de la
queue), Cache-Control: no-transform (interdit a Cloudflare de bufferiser/compresser),
et heartbeat 15s->4s (borne le pire cas).
This commit is contained in:
nathaninline committed 2026-07-22 09:15:15 +02:00
1 parent 7d104e3213
commit 51c2c2d80f
7 files changed
+25 -8

No files matched your search

+14 -1
View File
@@ -311,6 +311,16 @@ func (c *Conversation) Subscribe(ctx context.Context, from int, emit func(map[st
c.mu.Unlock()
}()
// 0. Amorçage anti-buffering. Le chat E2E d'app.ajean.link traverse Cloudflare
// (ajean.link, proxy orange) : tant qu'un proxy intermédiaire n'a pas reçu assez
// d'octets, il bufferise la réponse et ne la relaie qu'en retard (symptôme :
// derniers messages qui arrivent 20-30 s après le reste, de façon intermittente
// sur Safari). Envoyer d'emblée un gros événement de padding force le proxy à
// basculer en mode streaming tout de suite. Le client ignore la clé `pad`.
if !emit(map[string]any{"pad": strings.Repeat("·", 2048)}) {
return
}
// 1. Replay coalescé (snapshot hors verrou pour ne pas bloquer la génération).
c.mu.Lock()
snapshot := append([]LogEvent(nil), c.Log...)
@@ -328,7 +338,10 @@ func (c *Conversation) Subscribe(ctx context.Context, from int, emit func(map[st
last = s
}
}
if !emit(map[string]any{"caught_up": true}) {
// Padding aussi sur caught_up : force le flush de la QUEUE du replay à travers un
// proxy qui re-bufferise par lots (Cloudflare) → les derniers messages arrivent
// avec le reste, plus 20-30 s après.
if !emit(map[string]any{"caught_up": true, "pad": strings.Repeat("·", 2048)}) {
return
}