From da57804c847fccb1662c9c86b62988e73f4bb1b8 Mon Sep 17 00:00:00 2001 From: Lam Wei Li Date: Tue, 4 Oct 2022 03:59:48 +0800 Subject: [PATCH] feat: supports http2 --- README.md | 31 ++++++++++++++++++++++ index.js | 25 +++++++++++++++--- lib/agent.js | 27 ++++++++++++++++++-- lib/test.js | 2 +- package.json | 1 + test/supertest.js | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 145 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1aa6a27..84aa1e8 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,37 @@ request(app) }); ``` +To enable http2 protocol, simply append an options to `request` or `request.agent`: + +```js +const request = require('supertest'); +const express = require('express'); + +const app = express(); + +app.get('/user', function(req, res) { + res.status(200).json({ name: 'john' }); +}); + +request(app, { http2: true }) + .get('/user') + .expect('Content-Type', /json/) + .expect('Content-Length', '15') + .expect(200) + .end(function(err, res) { + if (err) throw err; + }); + +request.agent(app, { http2: true }) + .get('/user') + .expect('Content-Type', /json/) + .expect('Content-Length', '15') + .expect(200) + .end(function(err, res) { + if (err) throw err; + }); +``` + Here's an example with mocha, note how you can pass `done` straight to any of the `.expect()` calls: ```js diff --git a/index.js b/index.js index dc206a4..f3058ea 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,12 @@ */ const methods = require('methods'); const http = require('http'); +let http2; +try { + http2 = require('http2'); // eslint-disable-line global-require +} catch (_) { + // eslint-disable-line no-empty +} const Test = require('./lib/test.js'); const agent = require('./lib/agent.js'); @@ -16,16 +22,29 @@ const agent = require('./lib/agent.js'); * @return {Test} * @api public */ -module.exports = function(app) { +module.exports = function(app, options = {}) { const obj = {}; if (typeof app === 'function') { - app = http.createServer(app); // eslint-disable-line no-param-reassign + if (options.http2) { + if (!http2) { + throw new Error( + 'supertest: this version of Node.js does not support http2' + ); + } + app = http2.createServer(app); // eslint-disable-line no-param-reassign + } else { + app = http.createServer(app); // eslint-disable-line no-param-reassign + } } methods.forEach(function(method) { obj[method] = function(url) { - return new Test(app, method, url); + var test = new Test(app, method, url); + if (options.http2) { + test.http2(); + } + return test; }; }); diff --git a/lib/agent.js b/lib/agent.js index c501642..9a7810e 100644 --- a/lib/agent.js +++ b/lib/agent.js @@ -7,6 +7,12 @@ const { agent: Agent } = require('superagent'); const methods = require('methods'); const http = require('http'); +let http2; +try { + http2 = require('http2'); // eslint-disable-line global-require +} catch (_) { + // eslint-disable-line no-empty +} const Test = require('./test.js'); /** @@ -17,10 +23,24 @@ const Test = require('./test.js'); * @api public */ -function TestAgent(app, options) { +function TestAgent(app, options = {}) { if (!(this instanceof TestAgent)) return new TestAgent(app, options); - if (typeof app === 'function') app = http.createServer(app); // eslint-disable-line no-param-reassign + Agent.call(this, options); + this._options = options; + + if (typeof app === 'function') { + if (options.http2) { + if (!http2) { + throw new Error( + 'supertest: this version of Node.js does not support http2' + ); + } + app = http2.createServer(app); // eslint-disable-line no-param-reassign + } else { + app = http.createServer(app); // eslint-disable-line no-param-reassign + } + } this.app = app; } @@ -40,6 +60,9 @@ TestAgent.prototype.host = function(host) { methods.forEach(function(method) { TestAgent.prototype[method] = function(url, fn) { // eslint-disable-line no-unused-vars const req = new Test(this.app, method.toUpperCase(), url); + if (this._options.http2) { + req.http2(); + } if (this._host) { req.set('host', this._host); diff --git a/lib/test.js b/lib/test.js index 83266eb..3b3b255 100644 --- a/lib/test.js +++ b/lib/test.js @@ -6,7 +6,7 @@ const { inspect } = require('util'); const { STATUS_CODES } = require('http'); -const { Server } = require('https'); +const { Server } = require('tls'); const { deepStrictEqual } = require('assert'); const { Request } = require('superagent'); diff --git a/package.json b/package.json index 426ad89..cd2a056 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "mocha": "^10.0.0", "nock": "^13.2.8", "nyc": "^15.1.0", + "proxyquire": "^2.1.3", "should": "^13.2.3" } } diff --git a/test/supertest.js b/test/supertest.js index d6e6690..9931223 100644 --- a/test/supertest.js +++ b/test/supertest.js @@ -1,6 +1,12 @@ 'use strict'; const https = require('https'); +let http2; +try { + http2 = require('http2'); // eslint-disable-line global-require +} catch (_) { + // eslint-disable-line no-empty +} const fs = require('fs'); const path = require('path'); const should = require('should'); @@ -1340,3 +1346,62 @@ describe('request.get(url).query(vals) works as expected', function () { }); }); }); + +const describeHttp2 = (http2) ? describe : describe.skip; +describeHttp2('http2', function() { + // eslint-disable-next-line global-require + const proxyquire = require('proxyquire'); + + const tests = [ + { + title: 'request(app)', + api: request, + mockApi: proxyquire('../index.js', { http2: null }) + }, + { + title: 'request.agent(app)', + api: request.agent, + mockApi: proxyquire('../lib/agent.js', { http2: null }) + } + ]; + + tests.forEach(({ title, api, mockApi }) => { + describe(title, function () { + const app = function(req, res) { + res.end('hey'); + }; + + it('should fire up the app on an ephemeral port', function (done) { + api(app, { http2: true }) + .get('/') + .end(function (err, res) { + res.status.should.equal(200); + res.text.should.equal('hey'); + done(); + }); + }); + + it('should work with an active server', function (done) { + const server = http2.createServer(app); + + server.listen(function () { + api(server) + .get('/') + .http2() + .end(function (err, res) { + res.status.should.equal(200); + res.text.should.equal('hey'); + // close the external server explictly + server.close(done); + }); + }); + }); + + it('should throw error if http2 is not supported', function() { + (function() { + mockApi(app, { http2: true }); + }).should.throw('supertest: this version of Node.js does not support http2'); + }); + }); + }); +});