Skip to content

Commit

Permalink
fix: Prefer X-GraphQL-Event-Stream-Token header name for clarity
Browse files Browse the repository at this point in the history
  • Loading branch information
enisdenjo committed Aug 23, 2021
1 parent 2dc4516 commit 9aaa0a9
Show file tree
Hide file tree
Showing 6 changed files with 19 additions and 17 deletions.
2 changes: 1 addition & 1 deletion PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ The server accepts the reservation request by responding with `201` (Created) an

The reservation token MUST accompany future HTTP requests to aid the server with the stream matching process. Token SHOULD be transmitted by the client through either:

- A header value `X-GraphQL-Stream-Token`
- A header value `X-GraphQL-Event-Stream-Token`
- A search parameter `token`

For security reasons, **only one** SSE connection can fulfil a reservation at a time, there MUST never be multiple SSE connections behind a single reservation.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,11 +573,11 @@ import {
const handler = createHandler({
schema,
authenticate: async (req, res) => {
let token = req.headers['x-graphql-stream-token'];
let token = req.headers['x-graphql-event-stream-token'];
if (token) {
// When the client is working in a "single connection mode"
// all subsequent requests for operations will have the
// stream token set in the `X-GraphQL-Stream-Token` header.
// stream token set in the `X-GraphQL-Event-Stream-Token` header.
//
// It is considered safe to accept the header token always
// because if a stream reservation does not exist, or is already
Expand Down Expand Up @@ -607,14 +607,14 @@ const handler = createHandler({
// Read more: https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#distinct-connections-mode
if (req.method === 'POST' && req.headers.accept === 'text/event-stream') {
// "distinct connections mode" requests an event-stream with a POST
// method. These two checks, together with the lack of `X-GraphQL-Stream-Token`
// method. These two checks, together with the lack of `X-GraphQL-Event-Stream-Token`
// header, are sufficient for accurate detection.
return ''; // return token; is OK too
}

// On the other hand, clients operating in "single connection mode"
// need a unique stream token which will be provided alongside the
// incoming event stream request inside the `X-GraphQL-Stream-Token` header.
// incoming event stream request inside the `X-GraphQL-Event-Stream-Token` header.
//
// Read more: https://github.com/enisdenjo/graphql-sse/blob/master/PROTOCOL.md#single-connection-mode
return token;
Expand Down
2 changes: 1 addition & 1 deletion docs/interfaces/handler.HandlerOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ If you want to respond to the client with a custom status or body,
you should do so using the provided `res` argument which will stop
further execution.

**`default`** 'req.headers["x-graphql-stream-token"] || req.url.searchParams["token"] || generateRandomUUID()' // https://gist.github.com/jed/982883
**`default`** 'req.headers["x-graphql-event-stream-token"] || req.url.searchParams["token"] || generateRandomUUID()' // https://gist.github.com/jed/982883

#### Parameters

Expand Down
16 changes: 8 additions & 8 deletions src/__tests__/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ it('should only accept valid accept headers', async () => {

let res = await request('GET', {
accept: 'gibberish',
['x-graphql-stream-token']: token,
['x-graphql-event-stream-token']: token,
});
expect(res.statusCode).toBe(406);

res = await request('GET', {
accept: 'application/graphql+json',
['x-graphql-stream-token']: token,
['x-graphql-event-stream-token']: token,
});
expect(res.statusCode).toBe(400);
expect(res.statusMessage).toBe('Missing query');

res = await request('GET', {
accept: 'application/json',
['x-graphql-stream-token']: token,
['x-graphql-event-stream-token']: token,
});
expect(res.statusCode).toBe(400);
expect(res.statusMessage).toBe('Missing query');
Expand Down Expand Up @@ -89,7 +89,7 @@ describe('single connection mode', () => {
// token can be sent through the header
let res = await request('PUT');
es = new EventSource(url, {
headers: { ['x-graphql-stream-token']: res.data },
headers: { ['x-graphql-event-stream-token']: res.data },
});
await new Promise<void>((resolve, reject) => {
es.onopen = () => resolve();
Expand Down Expand Up @@ -120,7 +120,7 @@ describe('single connection mode', () => {

const { statusCode, statusMessage } = await request(
'POST',
{ 'x-graphql-stream-token': token },
{ 'x-graphql-event-stream-token': token },
{ query: '{ getValue }' },
);

Expand All @@ -144,7 +144,7 @@ describe('single connection mode', () => {

const { statusCode } = await request(
'POST',
{ 'x-graphql-stream-token': token },
{ 'x-graphql-event-stream-token': token },
{ query: '{ getValue }', extensions: { operationId: '1' } },
);
expect(statusCode).toBe(202);
Expand All @@ -169,7 +169,7 @@ describe('single connection mode', () => {

const { statusCode } = await request(
'POST',
{ 'x-graphql-stream-token': token },
{ 'x-graphql-event-stream-token': token },
{ query: 'subscription { greetings }', extensions: { operationId: '1' } },
);
expect(statusCode).toBe(202);
Expand All @@ -190,7 +190,7 @@ describe('single connection mode', () => {

const { statusCode, data } = await request(
'POST',
{ 'x-graphql-stream-token': token },
{ 'x-graphql-event-stream-token': token },
{ query: '{ notExists }', extensions: { operationId: '1' } },
);
expect(statusCode).toBe(400);
Expand Down
2 changes: 1 addition & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export function createClient(options: ClientOptions): Client {
if (res.status !== 201) throw new NetworkError(res);

const token = await res.text();
headers['x-graphql-stream-token'] = token;
headers['x-graphql-event-stream-token'] = token;

const connected = await connect({
signal: connCtrl.signal,
Expand Down
6 changes: 4 additions & 2 deletions src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export interface HandlerOptions {
* you should do so using the provided `res` argument which will stop
* further execution.
*
* @default 'req.headers["x-graphql-stream-token"] || req.url.searchParams["token"] || generateRandomUUID()' // https://gist.github.com/jed/982883
* @default 'req.headers["x-graphql-event-stream-token"] || req.url.searchParams["token"] || generateRandomUUID()' // https://gist.github.com/jed/982883
*/
authenticate?: (
req: IncomingMessage,
Expand Down Expand Up @@ -326,7 +326,9 @@ export function createHandler(options: HandlerOptions): Handler {
execute = graphqlExecute,
subscribe = graphqlSubscribe,
authenticate = function extractOrCreateStreamToken(req) {
const headerToken = req.headers['x-graphql-stream-token'];
const headerToken =
req.headers['x-graphql-event-stream-token'] ||
req.headers['x-graphql-stream-token']; // @deprecated >v1.0.0
if (headerToken)
return Array.isArray(headerToken) ? headerToken.join('') : headerToken;

Expand Down

0 comments on commit 9aaa0a9

Please sign in to comment.