From 832cc03e65cc1e58e7a81b7b73be0d449e85c811 Mon Sep 17 00:00:00 2001 From: sogaani Date: Mon, 17 Sep 2018 21:59:56 +0900 Subject: [PATCH] Delete dependency on git repo --- .eslintignore | 1 + .travis.yml | 5 +- package.json | 2 +- test/support/supertest/http2wrapper.js | 188 +++++++++++++++++++++++++ test/support/supertest/index.js | 32 +++++ test/test.js | 3 +- 6 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 test/support/supertest/http2wrapper.js create mode 100644 test/support/supertest/index.js diff --git a/.eslintignore b/.eslintignore index 62562b7..3e841d3 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ coverage node_modules +/test/support/supertest/http2wrapper.js \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index f5b4af3..c914a9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,6 @@ before_install: # Setup Node.js version-specific dependencies - "test $TRAVIS_NODE_VERSION != '0.8' || npm rm --save-dev istanbul" - "test $(echo $TRAVIS_NODE_VERSION | cut -d. -f1) -ge 4 || npm rm --save-dev $(grep -E '\"eslint\\S*\"' package.json | cut -d'\"' -f2)" - - "test -z $(echo $HTTP2_TEST) || npm install --only=dev https://github.com/sogaani/supertest.git#http2" # Update Node.js modules - "test ! -d node_modules || npm prune" @@ -38,7 +37,5 @@ after_script: - "test -e ./coverage/lcov.info && npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" matrix: include: - - node_js: "8.11" - env: HTTP2_TEST=1 - - node_js: "10.7" + - node_js: "10" env: HTTP2_TEST=1 diff --git a/package.json b/package.json index 248b30f..0dcd333 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "eslint-plugin-standard": "3.0.1", "istanbul": "0.4.5", "mocha": "2.5.3", - "supertest": "1.1.0" + "supertest": "2.0" }, "files": [ "LICENSE", diff --git a/test/support/supertest/http2wrapper.js b/test/support/supertest/http2wrapper.js new file mode 100644 index 0000000..4e87068 --- /dev/null +++ b/test/support/supertest/http2wrapper.js @@ -0,0 +1,188 @@ +'use strict'; + +const http2 = require('http2'); +const Stream = require('stream'); +const util = require('util'); +const net = require('net'); +const tls = require('tls'); +const parse = require('url').parse; + +const { + HTTP2_HEADER_PATH, + HTTP2_HEADER_STATUS, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_HOST, + HTTP2_HEADER_SET_COOKIE, + NGHTTP2_CANCEL, +} = http2.constants; + + +function setProtocol(protocol) { + return { + request: function (options) { + return new Request(protocol, options); + } + } +} + +function Request(protocol, options) { + Stream.call(this); + const defaultPort = protocol === 'https:' ? 443 : 80; + const defaultHost = 'localhost' + const port = options.port || defaultPort; + const host = options.host || defaultHost; + + delete options.port + delete options.host + + this.method = options.method.toUpperCase(); + this.path = options.path; + this.protocol = protocol; + this.host = host; + + delete options.method + delete options.path + + const sessionOptions = Object.assign({}, options); + if (options.socketPath) { + sessionOptions.socketPath = options.socketPath; + sessionOptions.createConnection = this.createUnixConnection.bind(this); + } + + this._headers = {}; + + const session = http2.connect(`${protocol}//${host}:${port}`, sessionOptions); + this.setHeader('host', `${host}:${port}`) + + session.on('error', (err) => this.emit('error', err)); + + this.session = session; +} + +/** + * Inherit from `Stream` (which inherits from `EventEmitter`). + */ +util.inherits(Request, Stream); + +Request.prototype.createUnixConnection = function (authority, options) { + switch (this.protocol) { + case 'http:': + return net.connect(options.socketPath); + case 'https:': + options.ALPNProtocols = ['h2']; + options.servername = this.host; + options.allowHalfOpen = true; + return tls.connect(options.socketPath, options); + default: + throw new Error('Unsupported protocol', this.protocol); + } +} + +Request.prototype.setNoDelay = function (bool) { + // We can not use setNoDelay with HTTP/2. + // Node 10 limits http2session.socket methods to ones safe to use with HTTP/2. + // See also https://nodejs.org/api/http2.html#http2_http2session_socket +} + +Request.prototype.getFrame = function () { + if (this.frame) { + return this.frame; + } + + const method = { + [HTTP2_HEADER_PATH]: this.path, + [HTTP2_HEADER_METHOD]: this.method, + } + + let headers = this.mapToHttp2Header(this._headers); + + headers = Object.assign(headers, method); + + const frame = this.session.request(headers); + frame.once('response', (headers, flags) => { + headers = this.mapToHttpHeader(headers); + frame.headers = headers; + frame.status = frame.statusCode = headers[HTTP2_HEADER_STATUS]; + this.emit('response', frame); + }); + + this._headerSent = true; + + frame.once('drain', () => this.emit('drain')); + frame.on('error', (err) => this.emit('error', err)); + frame.on('close', () => this.session.close()); + + this.frame = frame; + return frame; +} + +Request.prototype.mapToHttpHeader = function (headers) { + const keys = Object.keys(headers); + const http2Headers = {}; + for (var i = 0; i < keys.length; i++) { + let key = keys[i]; + let value = headers[key]; + key = key.toLowerCase(); + switch (key) { + case HTTP2_HEADER_SET_COOKIE: + value = Array.isArray(value) ? value : [value]; + break; + default: + break; + } + http2Headers[key] = value; + } + return http2Headers; +} + +Request.prototype.mapToHttp2Header = function (headers) { + const keys = Object.keys(headers); + const http2Headers = {}; + for (var i = 0; i < keys.length; i++) { + let key = keys[i]; + let value = headers[key]; + key = key.toLowerCase(); + switch (key) { + case HTTP2_HEADER_HOST: + key = HTTP2_HEADER_AUTHORITY; + value = /^http\:\/\/|^https\:\/\//.test(value) ? parse(value).host : value; + break; + default: + break; + } + http2Headers[key] = value; + } + return http2Headers; +} + +Request.prototype.setHeader = function (name, value) { + this._headers[name.toLowerCase()] = value; +} + +Request.prototype.getHeader = function (name) { + return this._headers[name.toLowerCase()]; +} + +Request.prototype.write = function (data, encoding) { + const frame = this.getFrame(); + return frame.write(data, encoding); +}; + +Request.prototype.pipe = function (stream, options) { + const frame = this.getFrame(); + return frame.pipe(stream, options); +} + +Request.prototype.end = function (data) { + const frame = this.getFrame(); + frame.end(data); +} + +Request.prototype.abort = function (data) { + const frame = this.getFrame(); + frame.close(NGHTTP2_CANCEL); + this.session.destroy(); +} + +exports.setProtocol = setProtocol; diff --git a/test/support/supertest/index.js b/test/support/supertest/index.js new file mode 100644 index 0000000..cf424dc --- /dev/null +++ b/test/support/supertest/index.js @@ -0,0 +1,32 @@ +var request = require('supertest') + +if (process.env.HTTP2_TEST) { + var http2 = require('http2') + var http2wrapper = require('./http2wrapper') + var agent = require('superagent') + var tls = require('tls') + agent.protocols = { + 'http:': http2wrapper.setProtocol('http:'), + 'https:': http2wrapper.setProtocol('https:') + } + request.Test.prototype.serverAddress = function (app, path, host) { + var addr = app.address() + var port + var protocol + + if (!addr) this._server = app.listen(0) + port = app.address().port + + protocol = app instanceof tls.Server ? 'https' : 'http' + return protocol + '://' + (host || '127.0.0.1') + ':' + port + path + } + var originalRequest = request + request = function (app) { + if (typeof app === 'function') { + app = http2.createServer(app) + } + return originalRequest(app) + } +} + +module.exports = request diff --git a/test/test.js b/test/test.js index aca3b0c..a45b67c 100644 --- a/test/test.js +++ b/test/test.js @@ -1,11 +1,10 @@ var assert = require('assert') var http = require('http') -var request = require('supertest') +var request = require('./support/supertest') var vhost = require('..') if (process.env.HTTP2_TEST) { - request.http2 = true http = require('http2') }