diff --git a/docs/README.md b/docs/README.md index 1c32a6dd..d2df47ec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,3 +11,4 @@ graphql-sse - [handler](modules/handler.md) - [use/fetch](modules/use_fetch.md) - [use/http](modules/use_http.md) +- [use/http2](modules/use_http2.md) diff --git a/docs/interfaces/use_http2.RequestContext.md b/docs/interfaces/use_http2.RequestContext.md new file mode 100644 index 00000000..eca9b0fe --- /dev/null +++ b/docs/interfaces/use_http2.RequestContext.md @@ -0,0 +1,17 @@ +[graphql-sse](../README.md) / [use/http2](../modules/use_http2.md) / RequestContext + +# Interface: RequestContext + +[use/http2](../modules/use_http2.md).RequestContext + +## Table of contents + +### Properties + +- [res](use_http2.RequestContext.md#res) + +## Properties + +### res + +• **res**: `Http2ServerResponse` diff --git a/docs/modules/use_http2.md b/docs/modules/use_http2.md new file mode 100644 index 00000000..9b66fca7 --- /dev/null +++ b/docs/modules/use_http2.md @@ -0,0 +1,48 @@ +[graphql-sse](../README.md) / use/http2 + +# Module: use/http2 + +## Table of contents + +### Interfaces + +- [RequestContext](../interfaces/use_http2.RequestContext.md) + +### Functions + +- [createHandler](use_http2.md#createhandler) + +## Functions + +### createHandler + +▸ **createHandler**<`Context`\>(`options`): (`req`: `Http2ServerRequest`, `res`: `Http2ServerResponse`) => `Promise`<`void`\> + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `Context` | extends [`OperationContext`](handler.md#operationcontext) = `undefined` | + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `options` | [`HandlerOptions`](../interfaces/handler.HandlerOptions.md)<`Http2ServerRequest`, [`RequestContext`](../interfaces/use_http2.RequestContext.md), `Context`\> | + +#### Returns + +`fn` + +▸ (`req`, `res`): `Promise`<`void`\> + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `req` | `Http2ServerRequest` | +| `res` | `Http2ServerResponse` | + +##### Returns + +`Promise`<`void`\> diff --git a/package.json b/package.json index 6ec7a38b..bef7c252 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,11 @@ "require": "./lib/use/http.js", "import": "./lib/use/http.mjs" }, + "./lib/use/http2": { + "types": "./lib/use/http2.d.ts", + "require": "./lib/use/http2.js", + "import": "./lib/use/http2.mjs" + }, "./package.json": "./package.json" }, "types": "lib/index.d.ts", diff --git a/src/use/http2.ts b/src/use/http2.ts new file mode 100644 index 00000000..e02d95cf --- /dev/null +++ b/src/use/http2.ts @@ -0,0 +1,54 @@ +import type { Http2ServerRequest, Http2ServerResponse } from 'http2'; +import { + createHandler as createRawHandler, + HandlerOptions, + OperationContext, +} from '../handler'; + +export interface RequestContext { + res: Http2ServerResponse; +} + +export function createHandler( + options: HandlerOptions, +): (req: Http2ServerRequest, res: Http2ServerResponse) => Promise { + const handler = createRawHandler(options); + return async function handleRequest(req, res) { + const [body, init] = await handler({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The method will always be available with http requests. + method: req.method!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- The url will always be available with http requests. + url: req.url!, + headers: req.headers, + body: () => + new Promise((resolve, reject) => { + let body = ''; + req.on('data', (chunk) => (body += chunk)); + req.once('error', reject); + req.once('end', () => { + req.off('error', reject); + resolve(body); + }); + }), + raw: req, + context: { res }, + }); + + res.writeHead(init.status, init.statusText, init.headers); + + if (!body || typeof body === 'string') { + return new Promise((resolve) => + res.end(body || '', () => resolve()), + ); + } + + res.once('close', body.return); + for await (const value of body) { + await new Promise((resolve, reject) => + res.write(value, (err) => (err ? reject(err) : resolve())), + ); + } + res.off('close', body.return); + return new Promise((resolve) => res.end(resolve)); + }; +}