mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-20 05:56:56 +00:00
fix(sse): gate event dispatch on data presence, not event: alone
This commit is contained in:
@@ -51,15 +51,21 @@ func readSSE(r io.Reader, fn func(sseEvent) error) error {
|
|||||||
var (
|
var (
|
||||||
event string
|
event string
|
||||||
dataLines []string
|
dataLines []string
|
||||||
pending bool
|
hasData bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// dispatch fires fn on a blank line, but only when at least one
|
||||||
|
// "data:" line was accumulated — matching the EventSource spec, an
|
||||||
|
// "event:" field alone (no data) discards silently rather than
|
||||||
|
// producing a data-less event. It always resets the accumulated
|
||||||
|
// fields, dispatched or not.
|
||||||
dispatch := func() error {
|
dispatch := func() error {
|
||||||
if !pending {
|
fire := hasData
|
||||||
|
ev := sseEvent{event: event, data: []byte(strings.Join(dataLines, "\n"))}
|
||||||
|
event, dataLines, hasData = "", nil, false
|
||||||
|
if !fire {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ev := sseEvent{event: event, data: []byte(strings.Join(dataLines, "\n"))}
|
|
||||||
event, dataLines, pending = "", nil, false
|
|
||||||
return fn(ev)
|
return fn(ev)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +86,9 @@ func readSSE(r io.Reader, fn func(sseEvent) error) error {
|
|||||||
switch field {
|
switch field {
|
||||||
case "event":
|
case "event":
|
||||||
event = value
|
event = value
|
||||||
pending = true
|
|
||||||
case "data":
|
case "data":
|
||||||
dataLines = append(dataLines, value)
|
dataLines = append(dataLines, value)
|
||||||
pending = true
|
hasData = true
|
||||||
case "id", "retry":
|
case "id", "retry":
|
||||||
// recognized fields, intentionally ignored
|
// recognized fields, intentionally ignored
|
||||||
}
|
}
|
||||||
|
|||||||
+22
@@ -177,6 +177,28 @@ func TestReadSSE_BlankLinesWithoutFieldsDoNotDispatch(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestReadSSE_EventFieldAloneDoesNotDispatch proves an "event:" field with
|
||||||
|
// no "data:" line does not produce a data-less event on the blank line
|
||||||
|
// that follows it — matching the EventSource spec, which discards an
|
||||||
|
// event whose data buffer is empty even when the event type was set.
|
||||||
|
func TestReadSSE_EventFieldAloneDoesNotDispatch(t *testing.T) {
|
||||||
|
raw := "event: ping\n\ndata: real\n\n"
|
||||||
|
|
||||||
|
var got []sseEvent
|
||||||
|
err := readSSE(strings.NewReader(raw), func(ev sseEvent) error {
|
||||||
|
got = append(got, ev)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("readSSE: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := []sseEvent{{event: "", data: []byte("real")}}
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("got %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestNewSSEWriter_SetsHeaders proves newSSEWriter sets the three SSE
|
// TestNewSSEWriter_SetsHeaders proves newSSEWriter sets the three SSE
|
||||||
// response headers on construction, before any body write.
|
// response headers on construction, before any body write.
|
||||||
func TestNewSSEWriter_SetsHeaders(t *testing.T) {
|
func TestNewSSEWriter_SetsHeaders(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user