From 6dce41b08b1e74550513a11073c4d14f312e3fe1 Mon Sep 17 00:00:00 2001 From: Trevor Scheer Date: Tue, 14 Feb 2023 12:16:29 -0800 Subject: [PATCH 1/3] initial attempt --- src/__tests__/http2.test.ts | 94 +++++++++++++++++++++++++++++++++++++ src/utils.ts | 4 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/http2.test.ts diff --git a/src/__tests__/http2.test.ts b/src/__tests__/http2.test.ts new file mode 100644 index 00000000..7afd4db2 --- /dev/null +++ b/src/__tests__/http2.test.ts @@ -0,0 +1,94 @@ +import Koa from 'koa'; +import http2 from 'http2'; +import { ApolloServer } from '@apollo/server'; +import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'; +import cors from '@koa/cors'; +import bodyParser from 'koa-bodyparser'; +import { koaMiddleware } from '..'; +import { urlForHttpServer } from '../utils'; + +describe('http2', () => { + it('works', async () => { + const app = new Koa(); + // disable logs to console.error + app.silent = true; + + // For browser-based implementations, you need to provide a key and cert and + // use http2.createSecureServer. This is because browsers will only use + // http2 with SSL. + const http2Server = http2.createServer(app.callback()); + const server = new ApolloServer({ + typeDefs: `#graphql + type Query { + hello: String! + } + `, + resolvers: { + Query: { + hello: () => 'hello world!', + }, + }, + plugins: [ + ApolloServerPluginDrainHttpServer({ + // @ts-ignore drain server currently only accepts an http.Server + httpServer: http2Server, + }), + ], + }); + + await server.start(); + app.use(cors()); + app.use(bodyParser()); + app.use(koaMiddleware(server)); + await new Promise((resolve) => { + http2Server.listen({ port: 0 }, resolve); + }); + const url = urlForHttpServer(http2Server); + + const client = http2.connect(url); + client.on('error', (err) => console.error(err)); + + // Not working, server sees no POST body ...is it because this is a stream + // and the server isn't processing it correctly? const req = + // client.request({ ':method': 'POST', ':path': '/', 'content-type': + // 'application/json', + // }); + // req.write(JSON.stringify({query: '{hello}'})) + + // GET works but presumably this is all happening in one part, which isn't a + // realistic expectation + const req = client.request({ + ':method': 'GET', + ':path': `/?query=${encodeURIComponent('{hello}')}`, + 'apollo-require-preflight': 't', + }); + + // req.on('response', (headers) => { + // for (const name in headers) { + // console.log(`${name}: ${headers[name]}`); + // } + // }); + + req.setEncoding('utf8'); + let data = ''; + req.on('data', (chunk) => { + data += chunk; + }); + + let r: () => void; + const p = new Promise((resolve) => (r = resolve)); + req.on('end', () => { + client.close(r); + }); + + req.end(); + await p; + expect(data).toMatchInlineSnapshot(` + "{"data":{"hello":"hello world!"}} + " + `); + + client.destroy(); + await server.stop(); + }); +}); diff --git a/src/utils.ts b/src/utils.ts index 24a5f10c..e295168f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,8 +1,10 @@ import type { Server } from 'http'; +import type { Http2Server } from 'http2'; + import type { AddressInfo } from 'net'; import { format } from 'url'; -export function urlForHttpServer(httpServer: Server): string { +export function urlForHttpServer(httpServer: Server | Http2Server): string { const { address, port } = httpServer.address() as AddressInfo; // Convert IPs which mean "any address" (IPv4 or IPv6) into localhost From fee1e11b5580a4bba168276becd09895d641d032 Mon Sep 17 00:00:00 2001 From: Trevor Scheer Date: Tue, 14 Feb 2023 14:50:35 -0800 Subject: [PATCH 2/3] Got POST working --- src/__tests__/http2.test.ts | 27 ++++++++------------------- src/index.ts | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/__tests__/http2.test.ts b/src/__tests__/http2.test.ts index 7afd4db2..2a7fc13d 100644 --- a/src/__tests__/http2.test.ts +++ b/src/__tests__/http2.test.ts @@ -30,7 +30,7 @@ describe('http2', () => { }, plugins: [ ApolloServerPluginDrainHttpServer({ - // @ts-ignore drain server currently only accepts an http.Server + // @ts-expect-error drain server plugin currently only accepts an http.Server httpServer: http2Server, }), ], @@ -48,26 +48,15 @@ describe('http2', () => { const client = http2.connect(url); client.on('error', (err) => console.error(err)); - // Not working, server sees no POST body ...is it because this is a stream - // and the server isn't processing it correctly? const req = - // client.request({ ':method': 'POST', ':path': '/', 'content-type': - // 'application/json', - // }); - // req.write(JSON.stringify({query: '{hello}'})) - - // GET works but presumably this is all happening in one part, which isn't a - // realistic expectation + const body = JSON.stringify({ query: '{hello}' }); + const contentLength = Buffer.byteLength(body); const req = client.request({ - ':method': 'GET', - ':path': `/?query=${encodeURIComponent('{hello}')}`, - 'apollo-require-preflight': 't', + ':method': 'POST', + ':path': '/', + 'content-type': 'application/json', + 'content-length': contentLength, }); - - // req.on('response', (headers) => { - // for (const name in headers) { - // console.log(`${name}: ${headers[name]}`); - // } - // }); + req.write(body); req.setEncoding('utf8'); let data = ''; diff --git a/src/index.ts b/src/index.ts index 80dd0b43..75aca447 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,6 +59,24 @@ export function koaMiddleware( return; } + if (Object.keys(ctx.request.body).length === 0) { + let r: () => void; + let p = new Promise((resolve) => r = resolve); + // empty body, is it a stream? + let data = ''; + if (!ctx.req.complete) { + ctx.req.on('data', (chunk) => { + data += chunk; + }) + ctx.req.on('end', () => { + ctx.request.body = data; + r(); + }); + await p; + console.log(data); + } + } + const incomingHeaders = new HeaderMap(); for (const [key, value] of Object.entries(ctx.headers)) { if (value !== undefined) { From c60c2fbb5242f7a55c6edcc2f9467e745b1767b6 Mon Sep 17 00:00:00 2001 From: Trevor Scheer Date: Tue, 14 Feb 2023 14:52:11 -0800 Subject: [PATCH 3/3] remove junk --- src/index.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/index.ts b/src/index.ts index 75aca447..80dd0b43 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,24 +59,6 @@ export function koaMiddleware( return; } - if (Object.keys(ctx.request.body).length === 0) { - let r: () => void; - let p = new Promise((resolve) => r = resolve); - // empty body, is it a stream? - let data = ''; - if (!ctx.req.complete) { - ctx.req.on('data', (chunk) => { - data += chunk; - }) - ctx.req.on('end', () => { - ctx.request.body = data; - r(); - }); - await p; - console.log(data); - } - } - const incomingHeaders = new HeaderMap(); for (const [key, value] of Object.entries(ctx.headers)) { if (value !== undefined) {