From 290c7f7fde5e604b2a8ac90f93b15e143ea09a92 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Mon, 26 Dec 2022 14:48:28 +0300 Subject: [PATCH] Ping/pong for SSE (#2150) * Ping/pong for SSE * Update packages/graphql-yoga/src/plugins/resultProcessor/push.ts Co-authored-by: Denis Badurina Co-authored-by: Denis Badurina --- .changeset/wise-geckos-beam.md | 5 +++++ .../src/plugins/resultProcessor/push.ts | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .changeset/wise-geckos-beam.md diff --git a/.changeset/wise-geckos-beam.md b/.changeset/wise-geckos-beam.md new file mode 100644 index 0000000000..4240e34455 --- /dev/null +++ b/.changeset/wise-geckos-beam.md @@ -0,0 +1,5 @@ +--- +'graphql-yoga': minor +--- + +Ping the client every 12 seconds to keep the connection alive diff --git a/packages/graphql-yoga/src/plugins/resultProcessor/push.ts b/packages/graphql-yoga/src/plugins/resultProcessor/push.ts index 22676fa7e0..d2517c7c95 100644 --- a/packages/graphql-yoga/src/plugins/resultProcessor/push.ts +++ b/packages/graphql-yoga/src/plugins/resultProcessor/push.ts @@ -9,6 +9,8 @@ export function processPushResult( result: ResultProcessorInput, fetchAPI: FetchAPI, ): Response { + const timeoutInSeconds = 12 + const headersInit = { 'Content-Type': 'text/event-stream', Connection: 'keep-alive', @@ -20,9 +22,19 @@ export function processPushResult( let iterator: AsyncIterator> + let pingInterval: number const textEncoder = new fetchAPI.TextEncoder() const readableStream = new fetchAPI.ReadableStream({ - start() { + start(controller) { + // ping client every 12 seconds to keep the connection alive + pingInterval = setInterval(() => { + if (!controller.desiredSize) { + clearInterval(pingInterval) + return + } + controller.enqueue(textEncoder.encode(':\n\n')) + }, timeoutInSeconds * 1000) as unknown as number + if (isAsyncIterable(result)) { iterator = result[Symbol.asyncIterator]() } else { @@ -45,10 +57,12 @@ export function processPushResult( controller.enqueue(textEncoder.encode(`data: ${chunk}\n\n`)) } if (done) { + clearInterval(pingInterval) controller.close() } }, async cancel(e) { + clearInterval(pingInterval) await iterator.return?.(e) }, })