chore(plugin-https): sync https tests with http (#597)

* chore(plugin-https): sync https tests with http

* chore: use Http instead typeof http

* chore: review finding, improve https detection

* chore: fix node 8

* chore: fix path to test files
This commit is contained in:
Gerhard Stöbich 2019-12-10 17:21:27 +01:00 committed by Olivier Albertini
parent daff102296
commit 54879ab3d6
13 changed files with 693 additions and 468 deletions

View File

@ -6,7 +6,7 @@
"types": "build/src/index.d.ts",
"repository": "open-telemetry/opentelemetry-js",
"scripts": {
"test": "nyc ts-mocha -p tsconfig.json test/**/*/*.test.ts",
"test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts",
"tdd": "yarn test -- --watch-extensions ts --watch",
"clean": "rimraf build/*",
"check": "gts check",

View File

@ -29,7 +29,7 @@ import { HttpPlugin, plugin } from '../../src/http';
import { assertSpan } from '../utils/assertSpan';
import { DummyPropagation } from '../utils/DummyPropagation';
import { httpRequest } from '../utils/httpRequest';
import * as utils from '../../src/utils';
import { OT_REQUEST_HEADER } from '../../src/utils';
import { HttpPluginConfig, Http } from '../../src/types';
import { AttributeNames } from '../../src/enums/AttributeNames';
@ -77,8 +77,8 @@ describe('HttpPlugin', () => {
assert.strictEqual(process.versions.node, plugin.version);
});
it('moduleName should be http', () => {
assert.strictEqual('http', plugin.moduleName);
it(`moduleName should be ${protocol}`, () => {
assert.strictEqual(protocol, plugin.moduleName);
});
describe('enable()', () => {
@ -123,7 +123,7 @@ describe('HttpPlugin', () => {
it('should generate valid spans (client side and server side)', async () => {
const result = await httpRequest.get(
`http://${hostname}:${serverPort}${pathname}`
`${protocol}://${hostname}:${serverPort}${pathname}`
);
const spans = memoryExporter.getFinishedSpans();
const [incomingSpan, outgoingSpan] = spans;
@ -142,14 +142,14 @@ describe('HttpPlugin', () => {
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
});
it(`should not trace requests with '${utils.OT_REQUEST_HEADER}' header`, async () => {
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
const testPath = '/outgoing/do-not-trace';
doNock(hostname, testPath, 200, 'Ok');
const options = {
host: hostname,
path: testPath,
headers: { [utils.OT_REQUEST_HEADER]: 1 },
headers: { [OT_REQUEST_HEADER]: 1 },
};
const result = await httpRequest.get(options);
@ -171,7 +171,7 @@ describe('HttpPlugin', () => {
(url: string) => url.endsWith(`/ignored/function`),
],
ignoreOutgoingUrls: [
`http://${hostname}:${serverPort}/ignored/string`,
`${protocol}://${hostname}:${serverPort}/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
],
@ -190,11 +190,11 @@ describe('HttpPlugin', () => {
plugin.disable();
});
it('http module should be patched', () => {
it(`${protocol} module should be patched`, () => {
assert.strictEqual(http.Server.prototype.emit.__wrapped, true);
});
it("should not patch if it's not a http module", () => {
it(`should not patch if it's not a ${protocol} module`, () => {
const httpNotPatched = new HttpPlugin(
plugin.component,
process.versions.node
@ -204,7 +204,7 @@ describe('HttpPlugin', () => {
it('should generate valid spans (client side and server side)', async () => {
const result = await httpRequest.get(
`http://${hostname}:${serverPort}${pathname}`
`${protocol}://${hostname}:${serverPort}${pathname}`
);
const spans = memoryExporter.getFinishedSpans();
const [incomingSpan, outgoingSpan] = spans;
@ -223,14 +223,14 @@ describe('HttpPlugin', () => {
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
});
it(`should not trace requests with '${utils.OT_REQUEST_HEADER}' header`, async () => {
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
const testPath = '/outgoing/do-not-trace';
doNock(hostname, testPath, 200, 'Ok');
const options = {
host: hostname,
path: testPath,
headers: { [utils.OT_REQUEST_HEADER]: 1 },
headers: { [OT_REQUEST_HEADER]: 1 },
};
const result = await httpRequest.get(options);
@ -395,13 +395,13 @@ describe('HttpPlugin', () => {
}
for (const arg of ['string', {}, new Date()]) {
it(`should be tracable and not throw exception in http plugin when passing the following argument ${JSON.stringify(
it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify(
arg
)}`, async () => {
try {
await httpRequest.get(arg);
} catch (error) {
// http request has been made
// request has been made
// nock throw
assert.ok(error.message.startsWith('Nock: No match for request'));
}
@ -411,14 +411,14 @@ describe('HttpPlugin', () => {
}
for (const arg of [true, 1, false, 0, '']) {
it(`should not throw exception in http plugin when passing the following argument ${JSON.stringify(
it(`should not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify(
arg
)}`, async () => {
try {
// @ts-ignore
await httpRequest.get(arg);
} catch (error) {
// http request has been made
// request has been made
// nock throw
assert.ok(
error.stack.indexOf(
@ -447,7 +447,7 @@ describe('HttpPlugin', () => {
const promiseRequest = new Promise((resolve, reject) => {
const req = http.request(
`http://${hostname}${testPath}`,
`${protocol}://${hostname}${testPath}`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
@ -488,7 +488,7 @@ describe('HttpPlugin', () => {
const promiseRequest = new Promise((resolve, reject) => {
const req = http.request(
`http://${hostname}${testPath}`,
`${protocol}://${hostname}${testPath}`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
@ -512,14 +512,14 @@ describe('HttpPlugin', () => {
});
it('should have 1 ended span when request is aborted', async () => {
nock('http://my.server.com')
nock(`${protocol}://my.server.com`)
.get('/')
.socketDelay(50)
.reply(200, '<html></html>');
const promiseRequest = new Promise((resolve, reject) => {
const req = http.request(
'http://my.server.com',
`${protocol}://my.server.com`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {

View File

@ -38,6 +38,7 @@ import { HttpPluginConfig } from '../../src/types';
import { customAttributeFunction } from './http-enable.test';
const memoryExporter = new InMemorySpanExporter();
const protocol = 'http';
describe('Packages', () => {
describe('get', () => {
@ -93,8 +94,8 @@ describe('Packages', () => {
// https://github.com/nock/nock/pull/1551
// https://github.com/sindresorhus/got/commit/bf1aa5492ae2bc78cbbec6b7d764906fb156e6c2#diff-707a4781d57c42085155dcb27edb9ccbR258
// TODO: check if this is still the case when new version
'http://info.cern.ch/'
: `http://www.google.com/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8`
`${protocol}://info.cern.ch/`
: `${protocol}://www.google.com/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8`
);
const result = await httpPackage.get(urlparsed.href!);
if (!resHeaders) {

View File

@ -30,7 +30,7 @@ import {
SimpleSpanProcessor,
} from '@opentelemetry/tracing';
import { HttpPluginConfig } from '../../src/types';
const protocol = 'http';
const serverPort = 32345;
const hostname = 'localhost';
const memoryExporter = new InMemorySpanExporter();
@ -70,7 +70,7 @@ describe('HttpPlugin Integration tests', () => {
before(() => {
const ignoreConfig = [
`http://${hostname}:${serverPort}/ignored/string`,
`${protocol}://${hostname}:${serverPort}/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
];
@ -93,7 +93,9 @@ describe('HttpPlugin Integration tests', () => {
let spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 0);
const result = await httpRequest.get(`http://google.fr/?query=test`);
const result = await httpRequest.get(
`${protocol}://google.fr/?query=test`
);
spans = memoryExporter.getFinishedSpans();
const span = spans[0];
@ -118,7 +120,7 @@ describe('HttpPlugin Integration tests', () => {
assert.strictEqual(spans.length, 0);
const result = await httpRequest.get(
new url.URL('http://google.fr/?query=test')
new url.URL(`${protocol}://google.fr/?query=test`)
);
spans = memoryExporter.getFinishedSpans();
@ -144,7 +146,7 @@ describe('HttpPlugin Integration tests', () => {
assert.strictEqual(spans.length, 0);
const result = await httpRequest.get(
new url.URL('http://google.fr/?query=test'),
new url.URL(`${protocol}://google.fr/?query=test`),
{ headers: { 'x-foo': 'foo' } }
);
@ -168,7 +170,7 @@ describe('HttpPlugin Integration tests', () => {
});
it('custom attributes should show up on client spans', async () => {
const result = await httpRequest.get(`http://google.fr/`);
const result = await httpRequest.get(`${protocol}://google.fr/`);
const spans = memoryExporter.getFinishedSpans();
const span = spans[0];
const validations = {
@ -192,7 +194,7 @@ describe('HttpPlugin Integration tests', () => {
assert.strictEqual(spans.length, 0);
const options = Object.assign(
{ headers: { Expect: '100-continue' } },
url.parse('http://google.fr/')
url.parse(`${protocol}://google.fr/`)
);
const result = await httpRequest.get(options);
@ -223,7 +225,7 @@ describe('HttpPlugin Integration tests', () => {
{ Expect: '100-continue', 'user-agent': 'http-plugin-test' },
{ 'user-agent': 'http-plugin-test' },
]) {
it(`should create a span for GET requests and add propagation when using the following signature: http.get(url, options, callback) and following headers: ${JSON.stringify(
it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify(
headers
)}`, done => {
let validations: {
@ -239,7 +241,7 @@ describe('HttpPlugin Integration tests', () => {
assert.strictEqual(spans.length, 0);
const options = { headers };
const req = http.get(
'http://google.fr/',
`${protocol}://google.fr/`,
options,
(resp: http.IncomingMessage) => {
const res = (resp as unknown) as http.IncomingMessage & {
@ -266,7 +268,7 @@ describe('HttpPlugin Integration tests', () => {
}
);
req.once('close', () => {
req.on('close', () => {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
assert.ok(spans[0].name.indexOf('GET /') >= 0);

View File

@ -5,7 +5,7 @@
[![devDependencies][devDependencies-image]][devDependencies-url]
[![Apache License][license-image]][license-image]
This module provides automatic instrumentation for [`https`](http://nodejs.org/dist/latest/docs/api/https.html).
This module provides automatic instrumentation for [`https`](http://nodejs.org/api/https.html).
For automatic instrumentation see the
[@opentelemetry/node](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-node) package.

View File

@ -6,10 +6,11 @@
"types": "build/src/index.d.ts",
"repository": "open-telemetry/opentelemetry-js",
"scripts": {
"test": "nyc ts-mocha -p tsconfig.json 'test/**/*.ts'",
"test": "nyc ts-mocha -p tsconfig.json test/**/*.test.ts",
"tdd": "yarn test -- --watch-extensions ts --watch",
"clean": "rimraf build/*",
"check": "gts check",
"codecov": "nyc report --reporter=json && codecov -f coverage/*.json -p ../../",
"precompile": "tsc --version",
"compile": "tsc -p .",
"fix": "gts fix",

View File

@ -17,6 +17,7 @@
import { HttpPlugin, Func, HttpRequestArgs } from '@opentelemetry/plugin-http';
import * as http from 'http';
import * as https from 'https';
import { URL } from 'url';
import * as semver from 'semver';
import * as shimmer from 'shimmer';
import * as utils from './utils';
@ -81,12 +82,13 @@ export class HttpsPlugin extends HttpPlugin {
return (original: Func<http.ClientRequest>): Func<http.ClientRequest> => {
const plugin = this;
return function httpsOutgoingRequest(
options,
options: https.RequestOptions | string | URL,
...args: HttpRequestArgs
): http.ClientRequest {
// Makes sure options will have default HTTPS parameters
if (typeof options === 'object') {
utils.setDefaultOptions(options);
if (typeof options === 'object' && !(options instanceof URL)) {
options = Object.assign({}, options);
utils.setDefaultOptions(options as https.RequestOptions);
}
return plugin._getPatchOutgoingRequestFunction()(original)(
options,
@ -105,17 +107,9 @@ export class HttpsPlugin extends HttpPlugin {
) {
return (original: Func<http.ClientRequest>): Func<http.ClientRequest> => {
return function httpsOutgoingRequest(
options: https.RequestOptions | string,
options: https.RequestOptions | string | URL,
...args: HttpRequestArgs
): http.ClientRequest {
const optionsType = typeof options;
// Makes sure options will have default HTTPS parameters
if (optionsType === 'object') {
utils.setDefaultOptions(options as https.RequestOptions);
} else if (typeof args[0] === 'object' && optionsType === 'string') {
utils.setDefaultOptions(args[0] as https.RequestOptions);
}
return plugin._getPatchOutgoingGetFunction(clientRequest)(original)(
options,
...args

View File

@ -14,16 +14,17 @@
* limitations under the License.
*/
import { NoopLogger } from '@opentelemetry/core';
import { NodeTracer } from '@opentelemetry/node';
import { Http } from '@opentelemetry/plugin-http';
import * as assert from 'assert';
import * as fs from 'fs';
import * as https from 'https';
import { AddressInfo } from 'net';
import * as nock from 'nock';
import * as sinon from 'sinon';
import { plugin } from '../../src/https';
import { NodeTracer } from '@opentelemetry/node';
import { NoopLogger } from '@opentelemetry/core';
import { Http } from '@opentelemetry/plugin-http';
import { AddressInfo } from 'net';
import { DummyPropagation } from '../utils/DummyPropagation';
import { httpsRequest } from '../utils/httpsRequest';

View File

@ -24,24 +24,36 @@ import {
Http,
HttpPluginConfig,
OT_REQUEST_HEADER,
AttributeNames,
} from '@opentelemetry/plugin-http';
import { CanonicalCode, Span as ISpan, SpanKind } from '@opentelemetry/types';
import * as assert from 'assert';
import * as fs from 'fs';
import * as http from 'http';
import * as https from 'https';
import * as path from 'path';
import * as nock from 'nock';
import { HttpsPlugin, plugin } from '../../src/https';
import { assertSpan } from '../utils/assertSpan';
import { DummyPropagation } from '../utils/DummyPropagation';
import { httpsRequest } from '../utils/httpsRequest';
const applyCustomAttributesOnSpanErrorMessage =
'bad applyCustomAttributesOnSpan function';
let server: https.Server;
const serverPort = 32345;
const protocol = 'https';
const hostname = 'localhost';
const pathname = '/test';
const memoryExporter = new InMemorySpanExporter();
const httpTextFormat = new DummyPropagation();
const logger = new NoopLogger();
const tracer = new NodeTracer({
logger,
httpTextFormat,
});
tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter));
function doNock(
hostname: string,
@ -70,191 +82,231 @@ describe('HttpsPlugin', () => {
assert.strictEqual(process.versions.node, plugin.version);
});
it('moduleName should be https', () => {
assert.strictEqual('https', plugin.moduleName);
it(`moduleName should be ${protocol}`, () => {
assert.strictEqual(protocol, plugin.moduleName);
});
describe('enable()', () => {
const httpTextFormat = new DummyPropagation();
const logger = new NoopLogger();
const tracer = new NodeTracer({
logger,
httpTextFormat,
});
tracer.addSpanProcessor(new SimpleSpanProcessor(memoryExporter));
beforeEach(() => {
memoryExporter.reset();
});
before(() => {
const config: HttpPluginConfig = {
ignoreIncomingPaths: [
`/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
],
ignoreOutgoingUrls: [
`${protocol}://${hostname}:${serverPort}/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
],
applyCustomAttributesOnSpan: customAttributeFunction,
};
plugin.enable((https as unknown) as Http, tracer, tracer.logger, config);
server = https.createServer(
{
key: fs.readFileSync('test/fixtures/server-key.pem'),
cert: fs.readFileSync('test/fixtures/server-cert.pem'),
},
(request, response) => {
response.end('Test Server Response');
}
);
server.listen(serverPort);
});
after(() => {
server.close();
plugin.disable();
});
it('https module should be patched', () => {
assert.strictEqual(https.Server.prototype.emit.__wrapped, true);
});
it("should not patch if it's not a http module", () => {
const httpNotPatched = new HttpsPlugin(process.versions.node).enable(
{} as Http,
tracer,
tracer.logger,
{}
);
assert.strictEqual(Object.keys(httpNotPatched).length, 0);
});
it('should generate valid spans (client side and server side)', async () => {
const result = await httpsRequest.get(
`${protocol}://${hostname}:${serverPort}${pathname}`
);
const spans = memoryExporter.getFinishedSpans();
const [incomingSpan, outgoingSpan] = spans;
const validations = {
hostname,
httpStatusCode: result.statusCode!,
httpMethod: result.method!,
pathname,
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assert.strictEqual(spans.length, 2);
assertSpan(incomingSpan, SpanKind.SERVER, validations);
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
});
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
const testPath = '/outgoing/do-not-trace';
doNock(hostname, testPath, 200, 'Ok');
const options = {
host: hostname,
path: testPath,
headers: { [OT_REQUEST_HEADER]: 1 },
};
const result = await httpsRequest.get(options);
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(result.data, 'Ok');
assert.strictEqual(spans.length, 0);
});
const httpErrorCodes = [400, 401, 403, 404, 429, 501, 503, 504, 500, 505];
for (let i = 0; i < httpErrorCodes.length; i++) {
it(`should test span for GET requests with https error ${httpErrorCodes[i]}`, async () => {
const testPath = '/outgoing/rootSpan/1';
doNock(
hostname,
testPath,
httpErrorCodes[i],
httpErrorCodes[i].toString()
);
const isReset = memoryExporter.getFinishedSpans().length === 0;
assert.ok(isReset);
const result = await httpsRequest.get(
`${protocol}://${hostname}${testPath}`
);
const spans = memoryExporter.getFinishedSpans();
const reqSpan = spans[0];
assert.strictEqual(result.data, httpErrorCodes[i].toString());
assert.strictEqual(spans.length, 1);
const validations = {
hostname,
httpStatusCode: result.statusCode!,
httpMethod: 'GET',
pathname: testPath,
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assertSpan(reqSpan, SpanKind.CLIENT, validations);
describe('with bad plugin options', () => {
let pluginWithBadOptions: HttpsPlugin;
beforeEach(() => {
memoryExporter.reset();
});
}
it('should create a child span for GET requests', async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(hostname, testPath, 200, 'Ok');
const name = 'TestRootSpan';
const span = tracer.startSpan(name);
return tracer.withSpan(span, async () => {
const result = await httpsRequest.get(
`${protocol}://${hostname}${testPath}`
before(() => {
const config: HttpPluginConfig = {
ignoreIncomingPaths: [
(url: string) => {
throw new Error('bad ignoreIncomingPaths function');
},
],
ignoreOutgoingUrls: [
(url: string) => {
throw new Error('bad ignoreOutgoingUrls function');
},
],
applyCustomAttributesOnSpan: () => {
throw new Error(applyCustomAttributesOnSpanErrorMessage);
},
};
pluginWithBadOptions = new HttpsPlugin(process.versions.node);
pluginWithBadOptions.enable(
(https as unknown) as Http,
tracer,
tracer.logger,
config
);
server = https.createServer(
{
key: fs.readFileSync('test/fixtures/server-key.pem'),
cert: fs.readFileSync('test/fixtures/server-cert.pem'),
},
(request, response) => {
response.end('Test Server Response');
}
);
server.listen(serverPort);
});
after(() => {
server.close();
pluginWithBadOptions.disable();
});
it('should generate valid spans (client side and server side)', async () => {
const result = await httpsRequest.get(
`${protocol}://${hostname}:${serverPort}${pathname}`
);
span.end();
const spans = memoryExporter.getFinishedSpans();
const [reqSpan, localSpan] = spans;
const [incomingSpan, outgoingSpan] = spans;
const validations = {
hostname,
httpStatusCode: result.statusCode!,
httpMethod: 'GET',
pathname: testPath,
httpMethod: result.method!,
pathname,
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0);
assert.strictEqual(spans.length, 2);
assert.ok(reqSpan.name.indexOf(testPath) >= 0);
assert.strictEqual(
localSpan.spanContext.traceId,
reqSpan.spanContext.traceId
);
assertSpan(reqSpan, SpanKind.CLIENT, validations);
assert.notStrictEqual(
localSpan.spanContext.spanId,
reqSpan.spanContext.spanId
);
assertSpan(incomingSpan, SpanKind.SERVER, validations);
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
});
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
const testPath = '/outgoing/do-not-trace';
doNock(hostname, testPath, 200, 'Ok');
const options = {
host: hostname,
path: testPath,
headers: { [OT_REQUEST_HEADER]: 1 },
};
const result = await httpsRequest.get(options);
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(result.data, 'Ok');
assert.strictEqual(spans.length, 0);
});
});
describe('with good plugin options', () => {
beforeEach(() => {
memoryExporter.reset();
});
for (let i = 0; i < httpErrorCodes.length; i++) {
it(`should test child spans for GET requests with https error ${httpErrorCodes[i]}`, async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(
hostname,
testPath,
httpErrorCodes[i],
httpErrorCodes[i].toString()
before(() => {
const config: HttpPluginConfig = {
ignoreIncomingPaths: [
`/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
],
ignoreOutgoingUrls: [
`${protocol}://${hostname}:${serverPort}/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
],
applyCustomAttributesOnSpan: customAttributeFunction,
};
plugin.enable(
(https as unknown) as Http,
tracer,
tracer.logger,
config
);
server = https.createServer(
{
key: fs.readFileSync('test/fixtures/server-key.pem'),
cert: fs.readFileSync('test/fixtures/server-cert.pem'),
},
(request, response) => {
response.end('Test Server Response');
}
);
server.listen(serverPort);
});
after(() => {
server.close();
plugin.disable();
});
it(`${protocol} module should be patched`, () => {
assert.strictEqual(https.Server.prototype.emit.__wrapped, true);
});
it(`should not patch if it's not a ${protocol} module`, () => {
const httpsNotPatched = new HttpsPlugin(process.versions.node).enable(
{} as Http,
tracer,
tracer.logger,
{}
);
assert.strictEqual(Object.keys(httpsNotPatched).length, 0);
});
it('should generate valid spans (client side and server side)', async () => {
const result = await httpsRequest.get(
`${protocol}://${hostname}:${serverPort}${pathname}`
);
const spans = memoryExporter.getFinishedSpans();
const [incomingSpan, outgoingSpan] = spans;
const validations = {
hostname,
httpStatusCode: result.statusCode!,
httpMethod: result.method!,
pathname,
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assert.strictEqual(spans.length, 2);
assertSpan(incomingSpan, SpanKind.SERVER, validations);
assertSpan(outgoingSpan, SpanKind.CLIENT, validations);
});
it(`should not trace requests with '${OT_REQUEST_HEADER}' header`, async () => {
const testPath = '/outgoing/do-not-trace';
doNock(hostname, testPath, 200, 'Ok');
const options = {
host: hostname,
path: testPath,
headers: { [OT_REQUEST_HEADER]: 1 },
};
const result = await httpsRequest.get(options);
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(result.data, 'Ok');
assert.strictEqual(spans.length, 0);
});
const httpErrorCodes = [400, 401, 403, 404, 429, 501, 503, 504, 500, 505];
for (let i = 0; i < httpErrorCodes.length; i++) {
it(`should test span for GET requests with http error ${httpErrorCodes[i]}`, async () => {
const testPath = '/outgoing/rootSpan/1';
doNock(
hostname,
testPath,
httpErrorCodes[i],
httpErrorCodes[i].toString()
);
const isReset = memoryExporter.getFinishedSpans().length === 0;
assert.ok(isReset);
const result = await httpsRequest.get(
`${protocol}://${hostname}${testPath}`
);
const spans = memoryExporter.getFinishedSpans();
const reqSpan = spans[0];
assert.strictEqual(result.data, httpErrorCodes[i].toString());
assert.strictEqual(spans.length, 1);
const validations = {
hostname,
httpStatusCode: result.statusCode!,
httpMethod: 'GET',
pathname: testPath,
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assertSpan(reqSpan, SpanKind.CLIENT, validations);
});
}
it('should create a child span for GET requests', async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(hostname, testPath, 200, 'Ok');
const name = 'TestRootSpan';
const span = tracer.startSpan(name);
return tracer.withSpan(span, async () => {
@ -288,194 +340,302 @@ describe('HttpsPlugin', () => {
);
});
});
}
it('should create multiple child spans for GET requests', async () => {
const testPath = '/outgoing/rootSpan/childs';
const num = 5;
doNock(hostname, testPath, 200, 'Ok', num);
const name = 'TestRootSpan';
const span = tracer.startSpan(name);
await tracer.withSpan(span, async () => {
for (let i = 0; i < num; i++) {
await httpsRequest.get(`${protocol}://${hostname}${testPath}`);
const spans = memoryExporter.getFinishedSpans();
assert.ok(spans[i].name.indexOf(testPath) >= 0);
assert.strictEqual(
span.context().traceId,
spans[i].spanContext.traceId
for (let i = 0; i < httpErrorCodes.length; i++) {
it(`should test child spans for GET requests with http error ${httpErrorCodes[i]}`, async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(
hostname,
testPath,
httpErrorCodes[i],
httpErrorCodes[i].toString()
);
}
span.end();
const spans = memoryExporter.getFinishedSpans();
// 5 child spans ended + 1 span (root)
assert.strictEqual(spans.length, 6);
});
});
const name = 'TestRootSpan';
const span = tracer.startSpan(name);
return tracer.withSpan(span, async () => {
const result = await httpsRequest.get(
`${protocol}://${hostname}${testPath}`
);
span.end();
const spans = memoryExporter.getFinishedSpans();
const [reqSpan, localSpan] = spans;
const validations = {
hostname,
httpStatusCode: result.statusCode!,
httpMethod: 'GET',
pathname: testPath,
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
for (const ignored of ['string', 'function', 'regexp']) {
it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => {
const testPath = `/ignored/${ignored}`;
await httpsRequest.get(
`${protocol}://${hostname}:${serverPort}${testPath}`
);
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 0);
});
}
for (const arg of ['string', '', {}, new Date()]) {
it(`should be tracable and not throw exception in https plugin when passing the following argument ${JSON.stringify(
arg
)}`, async () => {
try {
await httpsRequest.get(arg);
} catch (error) {
// https request has been made
// nock throw
assert.ok(error.message.startsWith('Nock: No match for request'));
}
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
});
}
for (const arg of [true, 1, false, 0]) {
it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify(
arg
)}`, async () => {
try {
// @ts-ignore
await httpsRequest.get(arg);
} catch (error) {
// https request has been made
// nock throw
assert.ok(
error.stack.indexOf('/node_modules/nock/lib/intercept.js') > 0
);
}
const spans = memoryExporter.getFinishedSpans();
// for this arg with don't provide trace. We pass arg to original method (https.get)
assert.strictEqual(spans.length, 0);
});
}
it('should have 1 ended span when request throw on bad "options" object', () => {
nock.cleanAll();
nock.enableNetConnect();
try {
https.request({ protocol: 'telnet' });
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
}
});
it('should have 1 ended span when response.end throw an exception', async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(hostname, testPath, 400, 'Not Ok');
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://${hostname}${testPath}`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
reject(new Error(data));
});
}
);
return req.end();
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
}
});
it('should have 1 ended span when request is aborted', async () => {
nock(`${protocol}://my.server.com`)
.get('/')
.socketDelay(50)
.reply(200, '<html></html>');
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://my.server.com`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
resolve(data);
});
}
);
req.setTimeout(10, () => {
req.abort();
reject('timeout');
assert.ok(localSpan.name.indexOf('TestRootSpan') >= 0);
assert.strictEqual(spans.length, 2);
assert.ok(reqSpan.name.indexOf(testPath) >= 0);
assert.strictEqual(
localSpan.spanContext.traceId,
reqSpan.spanContext.traceId
);
assertSpan(reqSpan, SpanKind.CLIENT, validations);
assert.notStrictEqual(
localSpan.spanContext.spanId,
reqSpan.spanContext.spanId
);
});
});
return req.end();
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
const [span] = spans;
assert.strictEqual(spans.length, 1);
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
assert.ok(Object.keys(span.attributes).length > 6);
}
});
it('should have 1 ended span when request is aborted after receiving response', async () => {
nock(`${protocol}://my.server.com`)
.get('/')
.delay({
body: 50,
})
.replyWithFile(200, `${process.cwd()}/package.json`);
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://my.server.com`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
req.abort();
data += chunk;
});
resp.on('end', () => {
resolve(data);
});
it('should create multiple child spans for GET requests', async () => {
const testPath = '/outgoing/rootSpan/childs';
const num = 5;
doNock(hostname, testPath, 200, 'Ok', num);
const name = 'TestRootSpan';
const span = tracer.startSpan(name);
await tracer.withSpan(span, async () => {
for (let i = 0; i < num; i++) {
await httpsRequest.get(`${protocol}://${hostname}${testPath}`);
const spans = memoryExporter.getFinishedSpans();
assert.ok(spans[i].name.indexOf(testPath) >= 0);
assert.strictEqual(
span.context().traceId,
spans[i].spanContext.traceId
);
}
);
return req.end();
span.end();
const spans = memoryExporter.getFinishedSpans();
// 5 child spans ended + 1 span (root)
assert.strictEqual(spans.length, 6);
});
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
const [span] = spans;
assert.strictEqual(spans.length, 1);
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
assert.ok(Object.keys(span.attributes).length > 7);
for (const ignored of ['string', 'function', 'regexp']) {
it(`should not trace ignored requests (client and server side) with type ${ignored}`, async () => {
const testPath = `/ignored/${ignored}`;
await httpsRequest.get(
`${protocol}://${hostname}:${serverPort}${testPath}`
);
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 0);
});
}
for (const arg of ['string', {}, new Date()]) {
it(`should be tracable and not throw exception in ${protocol} plugin when passing the following argument ${JSON.stringify(
arg
)}`, async () => {
try {
await httpsRequest.get(arg);
} catch (error) {
// request has been made
// nock throw
assert.ok(error.message.startsWith('Nock: No match for request'));
}
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
});
}
for (const arg of [true, 1, false, 0, '']) {
it(`should not throw exception in https plugin when passing the following argument ${JSON.stringify(
arg
)}`, async () => {
try {
// @ts-ignore
await httpsRequest.get(arg);
} catch (error) {
// request has been made
// nock throw
assert.ok(
error.stack.indexOf(
path.normalize('/node_modules/nock/lib/intercept.js')
) > 0
);
}
const spans = memoryExporter.getFinishedSpans();
// for this arg with don't provide trace. We pass arg to original method (https.get)
assert.strictEqual(spans.length, 0);
});
}
it('should have 1 ended span when request throw on bad "options" object', () => {
try {
https.request({ protocol: 'telnet' });
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
}
});
it('should have 1 ended span when response.end throw an exception', async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(hostname, testPath, 400, 'Not Ok');
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://${hostname}${testPath}`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
reject(new Error(data));
});
}
);
return req.end();
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
}
});
it('should have 1 ended span when request throw on bad "options" object', () => {
nock.cleanAll();
nock.enableNetConnect();
try {
https.request({ protocol: 'telnet' });
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
}
});
it('should have 1 ended span when response.end throw an exception', async () => {
const testPath = '/outgoing/rootSpan/childs/1';
doNock(hostname, testPath, 400, 'Not Ok');
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://${hostname}${testPath}`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
reject(new Error(data));
});
}
);
return req.end();
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);
}
});
it('should have 1 ended span when request is aborted', async () => {
nock(`${protocol}://my.server.com`)
.get('/')
.socketDelay(50)
.reply(200, '<html></html>');
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://my.server.com`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
resolve(data);
});
}
);
req.setTimeout(10, () => {
req.abort();
reject('timeout');
});
return req.end();
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
const [span] = spans;
assert.strictEqual(spans.length, 1);
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
assert.ok(Object.keys(span.attributes).length > 6);
}
});
it('should have 1 ended span when request is aborted after receiving response', async () => {
nock(`${protocol}://my.server.com`)
.get('/')
.delay({
body: 50,
})
.replyWithFile(200, `${process.cwd()}/package.json`);
const promiseRequest = new Promise((resolve, reject) => {
const req = https.request(
`${protocol}://my.server.com`,
(resp: http.IncomingMessage) => {
let data = '';
resp.on('data', chunk => {
req.abort();
data += chunk;
});
resp.on('end', () => {
resolve(data);
});
}
);
return req.end();
});
try {
await promiseRequest;
assert.fail();
} catch (error) {
const spans = memoryExporter.getFinishedSpans();
const [span] = spans;
assert.strictEqual(spans.length, 1);
assert.strictEqual(span.status.code, CanonicalCode.ABORTED);
assert.ok(Object.keys(span.attributes).length > 7);
}
});
it("should have 1 ended span when response is listened by using req.on('response')", done => {
const host = `${protocol}://${hostname}`;
nock(host)
.get('/')
.reply(404);
const req = https.request(`${host}/`);
req.on('response', response => {
response.on('data', () => {});
response.on('end', () => {
const spans = memoryExporter.getFinishedSpans();
const [span] = spans;
assert.strictEqual(spans.length, 1);
assert.ok(Object.keys(span.attributes).length > 6);
assert.strictEqual(
span.attributes[AttributeNames.HTTP_STATUS_CODE],
404
);
assert.strictEqual(span.status.code, CanonicalCode.NOT_FOUND);
done();
});
});
req.end();
});
});
});
});

View File

@ -15,7 +15,7 @@
*/
import { NoopLogger } from '@opentelemetry/core';
import { SpanKind, Span } from '@opentelemetry/types';
import { SpanKind } from '@opentelemetry/types';
import * as assert from 'assert';
import * as https from 'https';
import * as http from 'http';
@ -34,13 +34,12 @@ import {
InMemorySpanExporter,
SimpleSpanProcessor,
} from '@opentelemetry/tracing';
import { Http } from '@opentelemetry/plugin-http';
import { Http, HttpPluginConfig } from '@opentelemetry/plugin-http';
import { customAttributeFunction } from './https-enable.test';
const memoryExporter = new InMemorySpanExporter();
export const customAttributeFunction = (span: Span): void => {
span.setAttribute('span kind', SpanKind.CLIENT);
};
const protocol = 'https';
describe('Packages', () => {
describe('get', () => {
@ -57,7 +56,10 @@ describe('Packages', () => {
});
before(() => {
plugin.enable((https as unknown) as Http, tracer, tracer.logger);
const config: HttpPluginConfig = {
applyCustomAttributesOnSpan: customAttributeFunction,
};
plugin.enable((https as unknown) as Http, tracer, tracer.logger, config);
});
after(() => {
@ -93,8 +95,8 @@ describe('Packages', () => {
// https://github.com/nock/nock/pull/1551
// https://github.com/sindresorhus/got/commit/bf1aa5492ae2bc78cbbec6b7d764906fb156e6c2#diff-707a4781d57c42085155dcb27edb9ccbR258
// TODO: check if this is still the case when new version
'https://www.google.com'
: `https://www.google.com/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8`
`${protocol}://www.google.com`
: `${protocol}://www.google.com/search?q=axios&oq=axios&aqs=chrome.0.69i59l2j0l3j69i60.811j0j7&sourceid=chrome&ie=UTF-8`
);
const result = await httpPackage.get(urlparsed.href!);
if (!resHeaders) {

View File

@ -14,24 +14,25 @@
* limitations under the License.
*/
import {
InMemorySpanExporter,
SimpleSpanProcessor,
} from '@opentelemetry/tracing';
import { NoopLogger } from '@opentelemetry/core';
import { NodeTracer } from '@opentelemetry/node';
import { HttpPluginConfig, Http } from '@opentelemetry/plugin-http';
import { Span, SpanKind } from '@opentelemetry/types';
import { SpanKind, Span } from '@opentelemetry/types';
import * as assert from 'assert';
import * as http from 'http';
import * as https from 'https';
import * as url from 'url';
import { plugin } from '../../src/https';
import { assertSpan } from '../utils/assertSpan';
import { DummyPropagation } from '../utils/DummyPropagation';
import { httpsRequest } from '../utils/httpsRequest';
import * as url from 'url';
import * as utils from '../utils/utils';
import { NodeTracer } from '@opentelemetry/node';
import {
InMemorySpanExporter,
SimpleSpanProcessor,
} from '@opentelemetry/tracing';
const protocol = 'https';
const serverPort = 42345;
const hostname = 'localhost';
const memoryExporter = new InMemorySpanExporter();
@ -71,7 +72,7 @@ describe('HttpsPlugin Integration tests', () => {
before(() => {
const ignoreConfig = [
`https://${hostname}:${serverPort}/ignored/string`,
`${protocol}://${hostname}:${serverPort}/ignored/string`,
/\/ignored\/regexp$/i,
(url: string) => url.endsWith(`/ignored/function`),
];
@ -94,7 +95,9 @@ describe('HttpsPlugin Integration tests', () => {
let spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 0);
const result = await httpsRequest.get(`https://google.fr/?query=test`);
const result = await httpsRequest.get(
`${protocol}://google.fr/?query=test`
);
spans = memoryExporter.getFinishedSpans();
const span = spans[0];
@ -114,8 +117,62 @@ describe('HttpsPlugin Integration tests', () => {
assertSpan(span, SpanKind.CLIENT, validations);
});
it('should create a rootSpan for GET requests and add propagation headers if URL is used', async () => {
let spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 0);
const result = await httpsRequest.get(
new url.URL(`${protocol}://google.fr/?query=test`)
);
spans = memoryExporter.getFinishedSpans();
const span = spans[0];
const validations = {
hostname: 'google.fr',
httpStatusCode: result.statusCode!,
httpMethod: 'GET',
pathname: '/',
path: '/?query=test',
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assert.strictEqual(spans.length, 1);
assert.ok(span.name.indexOf('GET /') >= 0);
assertSpan(span, SpanKind.CLIENT, validations);
});
it('should create a rootSpan for GET requests and add propagation headers if URL and options are used', async () => {
let spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 0);
const result = await httpsRequest.get(
new url.URL(`${protocol}://google.fr/?query=test`),
{ headers: { 'x-foo': 'foo' } }
);
spans = memoryExporter.getFinishedSpans();
const span = spans[0];
const validations = {
hostname: 'google.fr',
httpStatusCode: result.statusCode!,
httpMethod: 'GET',
pathname: '/',
path: '/?query=test',
resHeaders: result.resHeaders,
reqHeaders: result.reqHeaders,
component: plugin.component,
};
assert.strictEqual(spans.length, 1);
assert.ok(span.name.indexOf('GET /') >= 0);
assert.strictEqual(result.reqHeaders['x-foo'], 'foo');
assertSpan(span, SpanKind.CLIENT, validations);
});
it('custom attributes should show up on client spans', async () => {
const result = await httpsRequest.get(`https://google.fr/`);
const result = await httpsRequest.get(`${protocol}://google.fr/`);
const spans = memoryExporter.getFinishedSpans();
const span = spans[0];
const validations = {
@ -139,7 +196,7 @@ describe('HttpsPlugin Integration tests', () => {
assert.strictEqual(spans.length, 0);
const options = Object.assign(
{ headers: { Expect: '100-continue' } },
url.parse('https://google.fr/')
url.parse(`${protocol}://google.fr/`)
);
const result = await httpsRequest.get(options);
@ -170,7 +227,7 @@ describe('HttpsPlugin Integration tests', () => {
{ Expect: '100-continue', 'user-agent': 'https-plugin-test' },
{ 'user-agent': 'https-plugin-test' },
]) {
it(`should create a span for GET requests and add propagation when using the following signature: https.get(url, options, callback) and following headers: ${JSON.stringify(
it(`should create a span for GET requests and add propagation when using the following signature: get(url, options, callback) and following headers: ${JSON.stringify(
headers
)}`, done => {
let validations: {
@ -186,7 +243,7 @@ describe('HttpsPlugin Integration tests', () => {
assert.strictEqual(spans.length, 0);
const options = { headers };
const req = https.get(
'https://google.fr/',
`${protocol}://google.fr/`,
options,
(resp: http.IncomingMessage) => {
const res = (resp as unknown) as http.IncomingMessage & {
@ -212,6 +269,7 @@ describe('HttpsPlugin Integration tests', () => {
});
}
);
req.on('close', () => {
const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 1);

View File

@ -14,16 +14,16 @@
* limitations under the License.
*/
import { SpanKind } from '@opentelemetry/types';
import { SpanKind, Status } from '@opentelemetry/types';
import { hrTimeToNanoseconds } from '@opentelemetry/core';
import * as assert from 'assert';
import * as http from 'http';
import { DummyPropagation } from './DummyPropagation';
import { ReadableSpan } from '@opentelemetry/tracing';
import {
AttributeNames,
parseResponseStatus,
} from '@opentelemetry/plugin-http';
import { DummyPropagation } from './DummyPropagation';
import { ReadableSpan } from '@opentelemetry/tracing';
export const assertSpan = (
span: ReadableSpan,
@ -36,6 +36,7 @@ export const assertSpan = (
pathname: string;
reqHeaders?: http.OutgoingHttpHeaders;
path?: string | null;
forceStatus?: Status;
component: string;
}
) => {
@ -70,14 +71,22 @@ export const assertSpan = (
span.attributes[AttributeNames.HTTP_STATUS_CODE],
validations.httpStatusCode
);
assert.ok(span.endTime);
assert.strictEqual(span.links.length, 0);
assert.strictEqual(span.events.length, 0);
assert.deepStrictEqual(
span.status,
parseResponseStatus(validations.httpStatusCode)
validations.forceStatus || parseResponseStatus(validations.httpStatusCode)
);
assert.ok(
(span.attributes[AttributeNames.HTTP_URL] as string).indexOf(
span.attributes[AttributeNames.HTTP_HOSTNAME] as string
) > -1,
'should be consistent'
);
assert.ok(span.endTime, 'must be finished');
assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration');
if (validations.reqHeaders) {

View File

@ -16,59 +16,56 @@
import * as http from 'http';
import * as https from 'https';
import { RequestOptions } from 'https';
import * as url from 'url';
import { URL } from 'url';
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
export const httpsRequest = {
get: (
options: string | RequestOptions
): Promise<{
data: string;
statusCode: number | undefined;
resHeaders: http.IncomingHttpHeaders;
reqHeaders: http.OutgoingHttpHeaders;
method: string | undefined;
}> => {
const _options =
typeof options === 'string'
? Object.assign(url.parse(options), {
headers: {
'user-agent': 'https-plugin-test',
},
})
: options;
return new Promise((resolve, reject) => {
const req = https.get(_options, (resp: http.IncomingMessage) => {
const res = (resp as unknown) as http.IncomingMessage & {
req: http.IncomingMessage;
};
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
resolve({
data,
statusCode: res.statusCode,
/* tslint:disable:no-any */
reqHeaders: (res.req as any).getHeaders
? (res.req as any).getHeaders()
: (res.req as any)._headers,
/* tslint:enable:no-any */
resHeaders: res.headers,
method: res.req.method,
});
});
resp.on('error', err => {
reject(err);
type GetResult = Promise<{
data: string;
statusCode: number | undefined;
resHeaders: http.IncomingHttpHeaders;
reqHeaders: http.OutgoingHttpHeaders;
method: string | undefined;
}>;
function get(input: string | URL, options?: https.RequestOptions): GetResult;
function get(input: https.RequestOptions): GetResult;
function get(input: any, options?: any): GetResult {
return new Promise((resolve, reject) => {
let req: http.ClientRequest;
function onGetResponseCb(resp: http.IncomingMessage): void {
const res = (resp as unknown) as http.IncomingMessage & {
req: http.IncomingMessage;
};
let data = '';
resp.on('data', chunk => {
data += chunk;
});
resp.on('end', () => {
resolve({
data,
statusCode: res.statusCode,
reqHeaders: req.getHeaders ? req.getHeaders() : (req as any)._headers,
resHeaders: res.headers,
method: res.req.method,
});
});
req.on('error', err => {
resp.on('error', err => {
reject(err);
});
return req;
}
req =
options != null
? https.get(input, options, onGetResponseCb)
: https.get(input, onGetResponseCb);
req.on('error', err => {
reject(err);
});
},
return req;
});
}
export const httpsRequest = {
get,
};