Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate to es6 class instead of util.inherit in http2wrapper #1764

Merged
merged 1 commit into from
Aug 15, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
275 changes: 137 additions & 138 deletions src/node/http2wrapper.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;