diff --git a/src/node/http2wrapper.js b/src/node/http2wrapper.js index 61eea973..4141692c 100644 --- a/src/node/http2wrapper.js +++ b/src/node/http2wrapper.js @@ -1,5 +1,4 @@ const Stream = require('stream'); -const util = require('util'); const net = require('net'); const tls = require('tls'); // eslint-disable-next-line node/no-deprecated-api @@ -31,169 +30,169 @@ function setProtocol(protocol) { }; } -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; +class Request extends Stream { + constructor(protocol, options) { + super(); + 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; + this.path = options.path; + this.protocol = protocol; + this.host = host; + + delete options.method; + delete options.path; + + const sessionOptions = { ...options }; + if (options.socketPath) { + sessionOptions.socketPath = options.socketPath; + sessionOptions.createConnection = this.createUnixConnection.bind(this); + } - delete options.port; - delete options.host; + this._headers = {}; - this.method = options.method; - this.path = options.path; - this.protocol = protocol; - this.host = host; + const session = http2.connect( + `${protocol}//${host}:${port}`, + sessionOptions + ); + this.setHeader('host', `${host}:${port}`); - delete options.method; - delete options.path; + session.on('error', (error) => this.emit('error', error)); - const sessionOptions = { ...options }; - if (options.socketPath) { - sessionOptions.socketPath = options.socketPath; - sessionOptions.createConnection = this.createUnixConnection.bind(this); + this.session = session; } - this._headers = {}; + createUnixConnection(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); + } + } - const session = http2.connect(`${protocol}//${host}:${port}`, sessionOptions); - this.setHeader('host', `${host}:${port}`); + setNoDelay(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 + } - session.on('error', (error) => this.emit('error', error)); + getFrame() { + if (this.frame) { + return this.frame; + } - this.session = session; -} + const method = { + [HTTP2_HEADER_PATH]: this.path, + [HTTP2_HEADER_METHOD]: this.method + }; -/** - * 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); - } -}; + let headers = this.mapToHttp2Header(this._headers); -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 -}; + headers = Object.assign(headers, method); -Request.prototype.getFrame = function () { - if (this.frame) { - return this.frame; - } + const frame = this.session.request(headers); - const method = { - [HTTP2_HEADER_PATH]: this.path, - [HTTP2_HEADER_METHOD]: this.method - }; + frame.once('response', (headers, flags) => { + headers = this.mapToHttpHeader(headers); + frame.headers = headers; + frame.statusCode = headers[HTTP2_HEADER_STATUS]; + frame.status = frame.statusCode; + this.emit('response', frame); + }); - let headers = this.mapToHttp2Header(this._headers); + this._headerSent = true; - headers = Object.assign(headers, method); + frame.once('drain', () => this.emit('drain')); + frame.on('error', (error) => this.emit('error', error)); + frame.on('close', () => this.session.close()); - const frame = this.session.request(headers); + this.frame = frame; + return frame; + } - frame.once('response', (headers, flags) => { - headers = this.mapToHttpHeader(headers); - frame.headers = headers; - frame.statusCode = headers[HTTP2_HEADER_STATUS]; - frame.status = frame.statusCode; - this.emit('response', frame); - }); + mapToHttpHeader(headers) { + const keys = Object.keys(headers); + const http2Headers = {}; + for (let key of keys) { + 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; + } - this._headerSent = true; + return http2Headers; + } - frame.once('drain', () => this.emit('drain')); - frame.on('error', (error) => this.emit('error', error)); - frame.on('close', () => this.session.close()); + mapToHttp2Header(headers) { + const keys = Object.keys(headers); + const http2Headers = {}; + for (let key of keys) { + 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; + } - this.frame = frame; - return frame; -}; + return http2Headers; + } -Request.prototype.mapToHttpHeader = function (headers) { - const keys = Object.keys(headers); - const http2Headers = {}; - for (let key of keys) { - let value = headers[key]; - key = key.toLowerCase(); - switch (key) { - case HTTP2_HEADER_SET_COOKIE: - value = Array.isArray(value) ? value : [value]; - break; - default: - break; - } + setHeader(name, value) { + this._headers[name.toLowerCase()] = value; + } - http2Headers[key] = value; + getHeader(name) { + return this._headers[name.toLowerCase()]; } - return http2Headers; -}; - -Request.prototype.mapToHttp2Header = function (headers) { - const keys = Object.keys(headers); - const http2Headers = {}; - for (let key of keys) { - 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; - } + write(data, encoding) { + const frame = this.getFrame(); + return frame.write(data, encoding); + } + + pipe(stream, options) { + const frame = this.getFrame(); + return frame.pipe(stream, options); + } - http2Headers[key] = value; + end(data) { + const frame = this.getFrame(); + frame.end(data); } - 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(); -}; + abort(data) { + const frame = this.getFrame(); + frame.close(NGHTTP2_CANCEL); + this.session.destroy(); + } +} exports.setProtocol = setProtocol;