Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions router/core/subscription_response_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ func GetSubscriptionResponseWriter(ctx *resolve.Context, r *http.Request, w http

if wgParams.UseMultipart || wgParams.UseSse {
ctx.ExecutionOptions.SendHeartbeat = true
// Flush the response head immediately so the client establishes the connection
// before the first message, instead of blocking until one is streamed.
flusher.Flush()
}

return ctx, flushWriter, true
Expand Down
25 changes: 24 additions & 1 deletion router/core/subscription_response_writer_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package core

import (
"github.com/stretchr/testify/assert"
"context"
"net/http"
"net/http/httptest"
"net/url"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/wundergraph/graphql-go-tools/v2/pkg/engine/resolve"
)

func TestNegotiateSubscriptionParams(t *testing.T) {
Expand Down Expand Up @@ -121,3 +126,21 @@ func TestNegotiateSubscriptionParams(t *testing.T) {
})
}
}

func TestGetSubscriptionResponseWriter(t *testing.T) {
// Headers set on a ResponseWriter are only sent to the client on the first
// Write/WriteHeader/Flush. An SSE subscription must flush the response head
// (200 + text/event-stream) as soon as it is established, otherwise clients
// block until the first message arrives instead of connecting immediately.
t.Run("flushes the SSE response head before any message is written", func(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodPost, "/graphql", nil)
req.Header.Set("Accept", sseMimeType)

_, _, ok := GetSubscriptionResponseWriter(resolve.NewContext(context.Background()), req, recorder, false)
require.True(t, ok)

assert.Equal(t, sseMimeType, recorder.Header().Get("Content-Type"))
assert.True(t, recorder.Flushed, "expected the SSE response head to be flushed before any message is written")
})
}
Loading