diff --git a/package.json b/package.json index 3f9580db..7eb031af 100644 --- a/package.json +++ b/package.json @@ -29,8 +29,8 @@ "fix-prettier": "prettier --single-quote --trailing-comma es5 --print-width 120 --write {bin,lib,probes,tests}/**/*.js *.js", "fix-eslint": "eslint --fix {bin,lib,probes,tests}/**/*.js *.js", "pretest": "eslint .", - "test": "tap --reporter tap --timeout=120 tests/*tests.js tests/probes/http-outbound-probe-test.js tests/probes/http-probe-test.js tests/headless_test.js", - "travis": "tap --reporter tap --timeout=120 tests/*tests.js tests/probes/http-outbound-probe-test.js tests/probes/http-probe-test.js tests/headless_test.js --coverage", + "test": "tap --reporter tap --timeout=120 tests/*tests.js tests/probes/http*test.js tests/headless_test.js", + "travis": "tap --reporter tap --timeout=120 tests/*tests.js tests/probes/http*test.js tests/headless_test.js --coverage", "posttravis": "./get_code_cov.sh && tap --coverage-report=lcov && codecov --disable=gcov", "install": "node extract_all_binaries.js || node-gyp rebuild" }, diff --git a/probes/http-outbound-probe.js b/probes/http-outbound-probe.js index 1e98ff58..ad8fb6ad 100644 --- a/probes/http-outbound-probe.js +++ b/probes/http-outbound-probe.js @@ -23,6 +23,7 @@ var am = require('../'); var semver = require('semver'); var methods; +// In Node.js < v8.0.0 'get' calls 'request' so we only instrument 'request' if (semver.lt(process.version, '8.0.0')) { methods = ['request']; } else { @@ -38,7 +39,7 @@ util.inherits(HttpOutboundProbe, Probe); HttpOutboundProbe.prototype.attach = function(name, target) { var that = this; - if (name == 'http') { + if (name === 'http') { if (target.__outboundProbeAttached__) return target; target.__outboundProbeAttached__ = true; @@ -47,40 +48,41 @@ HttpOutboundProbe.prototype.attach = function(name, target) { methods, // Before 'http.request' function function(obj, methodName, methodArgs, probeData) { - // Get HTTP request method from options - var options = methodArgs[0]; - var requestMethod = 'GET'; - var urlRequested = ''; - var headers = ''; - if (typeof options === 'object') { - urlRequested = formatURL(options); - if (options.method) { - requestMethod = options.method; - } - if (options.headers) { - headers = options.headers; - } - } else if (typeof options === 'string') { - urlRequested = options; - var parsedOptions = url.parse(options); - if (parsedOptions.method) { - requestMethod = parsedOptions.method; - } - if (parsedOptions.headers) { - headers = parsedOptions.headers; - } - } // Start metrics - that.metricsProbeStart(probeData, requestMethod, urlRequested); - that.requestProbeStart(probeData, requestMethod, urlRequested); + that.metricsProbeStart(probeData); + that.requestProbeStart(probeData); // End metrics aspect.aroundCallback( methodArgs, probeData, function(target, args, probeData) { - methodArgs.statusCode = args[0].statusCode; + + // Get HTTP request method from options + var options = methodArgs[0]; + var requestMethod = 'GET'; + var urlRequested = ''; + var headers = ''; + if (options !== null && typeof options === 'object') { + urlRequested = formatURL(options); + if (options.method) { + requestMethod = options.method; + } + if (options.headers) { + headers = options.headers; + } + } else if (typeof options === 'string') { + urlRequested = options; + var parsedOptions = url.parse(options); + if (parsedOptions.method) { + requestMethod = parsedOptions.method; + } + if (parsedOptions.headers) { + headers = parsedOptions.headers; + } + } + that.metricsProbeEnd(probeData, requestMethod, urlRequested, args[0], headers); that.requestProbeEnd(probeData, requestMethod, urlRequested, args[0], headers); }, @@ -93,13 +95,13 @@ HttpOutboundProbe.prototype.attach = function(name, target) { // After 'http.request' function returns function(target, methodName, methodArgs, probeData, rc) { // If no callback has been used then end the metrics after returning from the method instead - if (aspect.findCallbackArg(methodArgs) == undefined) { + if (aspect.findCallbackArg(methodArgs) === undefined) { // Need to get request method and URL again var options = methodArgs[0]; var requestMethod = 'GET'; var urlRequested = ''; var headers = ''; - if (typeof options === 'object') { + if (options !== null && typeof options === 'object') { urlRequested = formatURL(options); if (options.method) { requestMethod = options.method; @@ -181,7 +183,7 @@ HttpOutboundProbe.prototype.metricsEnd = function(probeData, method, url, res, h url: url, duration: probeData.timer.timeDelta, statusCode: res.statusCode, - contentType: res.headers ? res.headers['content-type'] : 'undefined', + contentType: res.headers ? res.headers['content-type'] : undefined, requestHeaders: headers, }); } @@ -201,7 +203,7 @@ HttpOutboundProbe.prototype.requestEnd = function(probeData, method, url, res, h probeData.req.stop({ url: url, statusCode: res.statusCode, - contentType: res.headers ? res.headers['content-type'] : 'undefined', + contentType: res.headers ? res.headers['content-type'] : undefined, requestHeaders: headers, }); }; diff --git a/probes/http-probe.js b/probes/http-probe.js index 45aca8fb..3d10370c 100644 --- a/probes/http-probe.js +++ b/probes/http-probe.js @@ -30,7 +30,7 @@ util.inherits(HttpProbe, Probe); HttpProbe.prototype.attach = function(name, target) { var that = this; - if (name == 'http') { + if (name === 'http') { if (target.__probeAttached__) return target; target.__probeAttached__ = true; var methods = ['on', 'addListener']; @@ -61,7 +61,7 @@ HttpProbe.prototype.attach = function(name, target) { /* * Custom req.url parser that strips out any trailing query */ -var parse = function(url) { +function parse(url) { ['?', '#'].forEach(function(separator) { var index = url.indexOf(separator); if (index !== -1) url = url.substring(0, index); @@ -75,7 +75,7 @@ var parse = function(url) { HttpProbe.prototype.filterUrl = function(req) { var resultUrl = parse(req.url); var filters = this.config.filters; - if (filters.length == 0) return resultUrl; + if (filters.length === 0) return resultUrl; var identifier = req.method + ' ' + resultUrl; for (var i = 0; i < filters.length; ++i) { @@ -124,7 +124,7 @@ HttpProbe.prototype.requestStart = function(probeData, method, url) { }; HttpProbe.prototype.requestEnd = function(probeData, method, url, res, httpReq) { - if (probeData && probeData.req) + if (probeData && probeData.req) { probeData.req.stop({ url: url, method: method, @@ -133,6 +133,7 @@ HttpProbe.prototype.requestEnd = function(probeData, method, url, res, httpReq) header: res._header, contentType: res.getHeader('content-type'), }); + } }; /* diff --git a/probes/https-outbound-probe.js b/probes/https-outbound-probe.js new file mode 100644 index 00000000..2f73a522 --- /dev/null +++ b/probes/https-outbound-probe.js @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +'use strict'; + +var am = require('../'); +var aspect = require('../lib/aspect.js'); +var Probe = require('../lib/probe.js'); +var request = require('../lib/request.js'); + +var url = require('url'); +var util = require('util'); + +// In https 'get' calls 'request' so we only instrument 'request' +var methods = ['request']; + +// Probe to instrument outbound https requests + +function HttpsOutboundProbe() { + Probe.call(this, 'https'); // match the name of the module we're instrumenting +} +util.inherits(HttpsOutboundProbe, Probe); + +HttpsOutboundProbe.prototype.attach = function(name, target) { + var that = this; + if (name === 'https') { + if (target.__outboundProbeAttached__) return target; + target.__outboundProbeAttached__ = true; + + aspect.around( + target, + methods, + // Before 'https.request' function + function(obj, methodName, methodArgs, probeData) { + + // Start metrics + that.metricsProbeStart(probeData); + that.requestProbeStart(probeData); + + // End metrics + aspect.aroundCallback( + methodArgs, + probeData, + function(target, args, probeData) { + + // Get HTTPS request method from options + var options = methodArgs[0]; + var requestMethod = 'GET'; + var urlRequested = ''; + var headers = ''; + if (options !== null && typeof options === 'object') { + urlRequested = formatURL(options); + if (options.method) { + requestMethod = options.method; + } + if (options.headers) { + headers = options.headers; + } + } else if (typeof options === 'string') { + urlRequested = options; + var parsedOptions = url.parse(options); + if (parsedOptions.method) { + requestMethod = parsedOptions.method; + } + if (parsedOptions.headers) { + headers = parsedOptions.headers; + } + } + + that.metricsProbeEnd(probeData, requestMethod, urlRequested, args[0], headers); + that.requestProbeEnd(probeData, requestMethod, urlRequested, args[0], headers); + }, + function(target, args, probeData, ret) { + // Don't need to do anything after the callback + return ret; + } + ); + }, + // After 'https.request' function returns + function(target, methodName, methodArgs, probeData, rc) { + // If no callback has been used then end the metrics after returning from the method instead + if (aspect.findCallbackArg(methodArgs) === undefined) { + // Need to get request method and URL again + var options = methodArgs[0]; + var requestMethod = 'GET'; + var urlRequested = ''; + var headers = ''; + if (options !== null && typeof options === 'object') { + urlRequested = formatURL(options); + if (options.method) { + requestMethod = options.method; + } + if (options.headers) { + headers = options.headers; + } + } else if (typeof options === 'string') { + urlRequested = options; + var parsedOptions = url.parse(options); + if (parsedOptions.method) { + requestMethod = parsedOptions.method; + } + if (parsedOptions.headers) { + headers = parsedOptions.headers; + } + } + + // End metrics (no response available so pass empty object) + that.metricsProbeEnd(probeData, requestMethod, urlRequested, {}, headers); + that.requestProbeEnd(probeData, requestMethod, urlRequested, {}, headers); + } + return rc; + } + ); + } + return target; +}; + +// Get a URL as a string from the options object passed to https.get or https.request +// See https://nodejs.org/api/http.html#http_http_request_options_callback +function formatURL(httpsOptions) { + var url; + if (httpsOptions.protocol) { + url = httpsOptions.protocol; + } else { + url = 'https:'; + } + url += '//'; + if (httpsOptions.auth) { + url += httpsOptions.auth + '@'; + } + if (httpsOptions.host) { + url += httpsOptions.host; + } else if (httpsOptions.hostname) { + url += httpsOptions.host; + } else { + url += 'localhost'; + } + if (httpsOptions.port) { + url += ':' + httpsOptions.port; + } + if (httpsOptions.path) { + url += httpsOptions.path; + } else { + url += '/'; + } + return url; +} + +/* + * Lightweight metrics probe for HTTPS requests + * + * These provide: + * time: time event started + * method: HTTPS method, eg. GET, POST, etc + * url: The url requested + * requestHeaders: The HTTPS headers for the request + * duration: The time for the request to respond + * contentType: HTTPS content-type + * statusCode: HTTPS status code + */ +HttpsOutboundProbe.prototype.metricsEnd = function(probeData, method, url, res, headers) { + if (probeData && probeData.timer) { + probeData.timer.stop(); + am.emit('https-outbound', { + time: probeData.timer.startTimeMillis, + method: method, + url: url, + duration: probeData.timer.timeDelta, + statusCode: res.statusCode, + contentType: res.headers ? res.headers['content-type'] : undefined, + requestHeaders: headers, + }); + } +}; + +/* + * Heavyweight request probes for HTTPS outbound requests + */ +HttpsOutboundProbe.prototype.requestStart = function(probeData, method, url) { + var reqType = 'https-outbound'; + // Do not mark as a root request + probeData.req = request.startRequest(reqType, url, false, probeData.timer); +}; + +HttpsOutboundProbe.prototype.requestEnd = function(probeData, method, url, res, headers) { + if (probeData && probeData.req) + probeData.req.stop({ + url: url, + statusCode: res.statusCode, + contentType: res.headers ? res.headers['content-type'] : undefined, + requestHeaders: headers, + }); +}; + +module.exports = HttpsOutboundProbe; diff --git a/probes/https-probe.js b/probes/https-probe.js new file mode 100644 index 00000000..bf815082 --- /dev/null +++ b/probes/https-probe.js @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright 2015 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +'use strict'; + +var am = require('../'); +var aspect = require('../lib/aspect.js'); +var Probe = require('../lib/probe.js'); +var request = require('../lib/request.js'); + +var util = require('util'); + +function HttpsProbe() { + Probe.call(this, 'https'); + this.config = { + filters: [], + }; +} +util.inherits(HttpsProbe, Probe); + +HttpsProbe.prototype.attach = function(name, target) { + var that = this; + if (name === 'https') { + if (target.__probeAttached__) return target; + target.__probeAttached__ = true; + var methods = ['on', 'addListener']; + + aspect.before(target.Server.prototype, methods, function(obj, methodName, args, probeData) { + if (args[0] !== 'request') return; + if (obj.__httpsProbe__) return; + obj.__httpsProbe__ = true; + aspect.aroundCallback(args, probeData, function(obj, args, probeData) { + var httpsReq = args[0]; + var res = args[1]; + // Filter out urls where filter.to is '' + var traceUrl = that.filterUrl(httpsReq); + if (traceUrl !== '') { + that.metricsProbeStart(probeData, httpsReq.method, traceUrl); + that.requestProbeStart(probeData, httpsReq.method, traceUrl); + aspect.after(res, 'end', probeData, function(obj, methodName, args, probeData, ret) { + that.metricsProbeEnd(probeData, httpsReq.method, traceUrl, res, httpsReq); + that.requestProbeEnd(probeData, httpsReq.method, traceUrl, res, httpsReq); + }); + } + }); + }); + } + return target; +}; + +/* + * Custom req.url parser that strips out any trailing query + */ +function parse(url) { + ['?', '#'].forEach(function(separator) { + var index = url.indexOf(separator); + if (index !== -1) url = url.substring(0, index); + }); + return url; +}; + +/* + * Ignore requests for URLs which we've been configured via regex to ignore + */ +HttpsProbe.prototype.filterUrl = function(req) { + var resultUrl = parse(req.url); + var filters = this.config.filters; + if (filters.length === 0) return resultUrl; + + var identifier = req.method + ' ' + resultUrl; + for (var i = 0; i < filters.length; ++i) { + var filter = filters[i]; + if (filter.regex.test(identifier)) { + return filter.to; + } + } + return resultUrl; +}; + +/* + * Lightweight metrics probe for HTTPS requests + * + * These provide: + * time: time event started + * method: HTTPS method, eg. GET, POST, etc + * url: The url requested + * duration: the time for the request to respond + */ + +HttpsProbe.prototype.metricsEnd = function(probeData, method, url, res, httpsReq) { + if (probeData && probeData.timer) { + probeData.timer.stop(); + am.emit('https', { + time: probeData.timer.startTimeMillis, + method: method, + url: url, + duration: probeData.timer.timeDelta, + header: res._header, + statusCode: res.statusCode, + contentType: res.getHeader('content-type'), + requestHeader: httpsReq.headers, + }); + } +}; + +/* + * Heavyweight request probes for HTTPS requests + */ + +HttpsProbe.prototype.requestStart = function(probeData, method, url) { + var reqType = 'https'; + // Mark as a root request as this happens due to an external event + probeData.req = request.startRequest(reqType, url, true, probeData.timer); +}; + +HttpsProbe.prototype.requestEnd = function(probeData, method, url, res, httpsReq) { + if (probeData && probeData.req) { + probeData.req.stop({ + url: url, + method: method, + requestHeader: httpsReq.headers, + statusCode: res.statusCode, + header: res._header, + contentType: res.getHeader('content-type'), + }); + } +}; + +/* + * Set configuration by merging passed in config with current one + */ +HttpsProbe.prototype.setConfig = function(newConfig) { + if (typeof newConfig.filters !== 'undefined') { + newConfig.filters.forEach(function(filter) { + if (typeof filter.regex === 'undefined') { + filter.regex = new RegExp(filter.pattern); + } + }); + } + for (var prop in newConfig) { + if (typeof newConfig[prop] !== 'undefined') { + this.config[prop] = newConfig[prop]; + } + } +}; + +module.exports = HttpsProbe; diff --git a/tests/probes/http-outbound-probe-test.js b/tests/probes/http-outbound-probe-test.js index ed95655f..e180a117 100644 --- a/tests/probes/http-outbound-probe-test.js +++ b/tests/probes/http-outbound-probe-test.js @@ -15,7 +15,7 @@ *******************************************************************************/ 'use strict'; -var appmetrics = (appmetrics = require('../../')); +var appmetrics = require('../../'); var monitor = appmetrics.monitor(); var server = require('../test_http_server').server; var http = require('http'); @@ -28,16 +28,12 @@ tap.tearDown(function() { server.close(); }); -var completedTests = 0; monitor.on('http-outbound', function(data) { - if (completedTests < 3) { - tap.test('HTTP Outbound Event', function(t) { - checkHttpOutboundData(data, t); - t.end(); - completedTests++; - }); - } + tap.test('HTTP Outbound Event', function(t) { + checkHttpOutboundData(data, t); + t.end(); + }); }); function checkHttpOutboundData(data, t) { diff --git a/tests/probes/http-probe-test.js b/tests/probes/http-probe-test.js index aeccc9c7..108f119c 100644 --- a/tests/probes/http-probe-test.js +++ b/tests/probes/http-probe-test.js @@ -15,7 +15,7 @@ ******************************************************************************/ 'use strict'; -var appmetrics = (appmetrics = require('../../')); +var appmetrics = require('../../'); var monitor = appmetrics.monitor(); var server = require('../test_http_server').server; var http = require('http'); diff --git a/tests/probes/https-outbound-probe-test.js b/tests/probes/https-outbound-probe-test.js new file mode 100644 index 00000000..c5ff1c1c --- /dev/null +++ b/tests/probes/https-outbound-probe-test.js @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +'use strict'; + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; +var appmetrics = require('../../'); +var monitor = appmetrics.monitor(); +var server = require('../test_https_server').server; +var https = require('https'); + +var tap = require('tap'); + +tap.plan(3); + +tap.tearDown(function() { + setTimeout(function() { + server.close(); + }, 1000); +}); + +monitor.on('https-outbound', function(data) { + tap.test('HTTPS Outbound Event', function(t) { + checkHttpOutboundData(data, t); + t.end(); + }); +}); + +function checkHttpOutboundData(data, t) { + t.ok(isInteger(data.time), 'Timestamp is an integer'); + t.equals(data.method, 'GET', 'Should report GET as HTTP request method'); + t.equals(data.url, 'https://localhost:8000/', 'Should report https://localhost:8000/ as URL'); + if (data.requestHeaders) { + t.equals(data.requestHeaders.hello, 'world', 'Should report world as value of hello header'); + } +} + +function isInteger(n) { + return isNumeric(n) && n % 1 == 0; +} + +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +var options = { + host: 'localhost', + port: 8000, + headers: { + hello: 'world', + }, +}; + +// Request with a callback +https.get('https://localhost:8000/', function(res) {}); + +// Request without a callback +https.get('https://localhost:8000/'); + +// Request with headers +https.request(options).end(); diff --git a/tests/probes/https-probe-test.js b/tests/probes/https-probe-test.js new file mode 100644 index 00000000..72a56895 --- /dev/null +++ b/tests/probes/https-probe-test.js @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +'use strict'; + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; +var appmetrics = require('../../'); +var monitor = appmetrics.monitor(); +var server = require('../test_https_server').server; +var https = require('https'); + +var tap = require('tap'); + +tap.plan(5); + +tap.tearDown(function() { + server.close(); +}); + +var completedTests = 0; + +monitor.on('https', function(data) { + // First 3 calls should have an 'https' event emitted, then 2 'request' events, + // so don't allow more than 3 tests to run here. + if (completedTests < 3) { + tap.test('HTTPS Event', function(t) { + checkHttpsData(data, t); + t.end(); + completedTests++; + }); + } +}); + +monitor.on('request', function(data) { + tap.test('HTTPS Request Event', function(t) { + checkHttpsRequestData(data.request.context, t); + t.end(); + }); +}); + +function checkHttpsData(data, t) { + t.ok(isInteger(data.time), 'Timestamp is an integer'); + t.equals(data.method, 'GET', 'Should report GET as HTTPS request method'); + t.equals(data.url, '/', 'Should report / as URL'); + t.equals(data.hasOwnProperty('duration'), true, 'Should have HTTPS property duration;'); + t.ok(isNumeric(data.duration), 'duration is a number'); + t.equals(data.hasOwnProperty('header'), true, 'Should have HTTPS property header;'); + t.equals(data.hasOwnProperty('statusCode'), true, 'Should have HTTPS property statusCode;'); + t.ok(isInteger(data.statusCode), 'statusCode is an integer'); + t.equals(data.hasOwnProperty('contentType'), true, 'Should have HTTPS property contentType;'); + t.equals(data.hasOwnProperty('requestHeader'), true, 'Should have HTTPS property requestHeader;'); +} + +function checkHttpsRequestData(data, t) { + t.equals(data.method, 'GET', 'Should report GET as HTTPS request method'); + t.equals(data.url, '/', 'Should report / as URL'); + t.equals(data.hasOwnProperty('statusCode'), true, 'Should have HTTPS property statusCode;'); + t.ok(isInteger(data.statusCode), 'statusCode is an integer'); + t.equals(data.hasOwnProperty('requestHeader'), true, 'Should have HTTPS property requestHeader;'); + t.equals(data.hasOwnProperty('header'), true, 'Should have HTTPS property header;'); + t.equals(data.hasOwnProperty('contentType'), true, 'Should have HTTPS property contentType;'); +} + +function isInteger(n) { + return isNumeric(n) && n % 1 == 0; +} + +function isNumeric(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +} + +// Request with a callback +https.get('https://localhost:8000/', function(res) {}); + +// Request without a callback +https.get('https://localhost:8000/'); + +var options = { + host: 'localhost', + port: 8000, + headers: { + hello: 'world', + }, +}; + +// Request with headers +https.request(options).end(); + +setTimeout(function() { + // Enable requests + monitor.enable('requests'); + monitor.disable('https'); + + // Request with a callback + https.get('https://localhost:8000/', function(res) {}); + + // Request without a callback + https.get('https://localhost:8000/'); +}, 2000); diff --git a/tests/test_https_server.js b/tests/test_https_server.js new file mode 100644 index 00000000..d7f93c67 --- /dev/null +++ b/tests/test_https_server.js @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright 2017 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *******************************************************************************/ +'use strict'; + +var https = require('https'); +var fs = require('fs'); +var path = require('path'); + +const httpsOptions = { + key: fs.readFileSync(path.resolve(__dirname, 'testkey.pem')), + cert: fs.readFileSync(path.resolve(__dirname, 'testcert.crt')), +}; + +module.exports.server = https.createServer(httpsOptions, (req, res) => { + // Send "Hello World" to every request + res.writeHead(200, { 'Content-Type': 'text/plain' }); + res.end('Hello World'); +}); + +this.server.listen(8000); diff --git a/tests/testcert.crt b/tests/testcert.crt new file mode 100644 index 00000000..cab9bc5f --- /dev/null +++ b/tests/testcert.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsTCCApmgAwIBAgIJAOR9C+ySiqNzMA0GCSqGSIb3DQEBCwUAMG8xCzAJBgNV +BAYTAlVLMRIwEAYDVQQIDAlIYW1wc2hpcmUxEzARBgNVBAcMCldpbmNoZXN0ZXIx +ETAPBgNVBAoMCFJ1bnRpbWVzMRAwDgYDVQQLDAdUZXN0aW5nMRIwEAYDVQQDDAls +b2NhbGhvc3QwHhcNMTcwNjE0MTMzNjUwWhcNMTcwNzE0MTMzNjUwWjBvMQswCQYD +VQQGEwJVSzESMBAGA1UECAwJSGFtcHNoaXJlMRMwEQYDVQQHDApXaW5jaGVzdGVy +MREwDwYDVQQKDAhSdW50aW1lczEQMA4GA1UECwwHVGVzdGluZzESMBAGA1UEAwwJ +bG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApsKfuQ75 +W+G6RU2MXc1kpJcZYV3JRxciCH0/egj0Jqk7nEDb6t0AhwUtRu36JJtt8tsjqjSB +AqCrzMOzRXeXKx2JFHh88lHBWUBfJoTsyfTo7Xb6/qnnWYHuJgtaKlcxBl/O71KN +W+34hW/RdpeKbdDHp9a9cVFBGjPtaTj4ImQy74mQ8qOZ2ACxpNYC6FU9G1SG1leV +0q0HXBHdlZbNAwzNdUuEE9BeqdAsKWXnzKOGcUqmMm+yLqrjggQS8dsj/u75dGpL +my0RGWn5v0dbQp0+lRGapUwc0qxEXO6JJCZcXwPw5aqBZvaPnWqrEH1ytmR1W+k8 +mD6BKvB0UFmeRwIDAQABo1AwTjAdBgNVHQ4EFgQUOpILqdkveJVZtuuzHfNK1fXs +NO4wHwYDVR0jBBgwFoAUOpILqdkveJVZtuuzHfNK1fXsNO4wDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAQEAAuOjv3dmnIca/AoWiRRTUJkuFQ3scjuXE4pZ +kGVf8dscE7MwxEakgPCjyUaRXc+28tQr2yeUpQBbeYTyZ2pJHvOb6LR979Or7GpG +7m+llhF9xAH3djQO1wsFV1At1Z3wyukXFCuj5iTTBferew9JTivVE3UlJvmLIv6Q +UwBAUqLALRxg2fK0xV+mTInI519e2JLZN1rqyE8mGf9eWLb6zPVo+OIKhm+ia8N8 +QJGV2ZrssimpaJ+4ce2fIE26v2RKs1gCdJIwJ4iTxL+XfDxVi5lBXSVf8344bUiK +OhjdvCl0jZTz38xto42YuUG0nhIYgf28ie5Ur1u1euqvkoLH9g== +-----END CERTIFICATE----- diff --git a/tests/testkey.pem b/tests/testkey.pem new file mode 100644 index 00000000..d7a0e811 --- /dev/null +++ b/tests/testkey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEApsKfuQ75W+G6RU2MXc1kpJcZYV3JRxciCH0/egj0Jqk7nEDb +6t0AhwUtRu36JJtt8tsjqjSBAqCrzMOzRXeXKx2JFHh88lHBWUBfJoTsyfTo7Xb6 +/qnnWYHuJgtaKlcxBl/O71KNW+34hW/RdpeKbdDHp9a9cVFBGjPtaTj4ImQy74mQ +8qOZ2ACxpNYC6FU9G1SG1leV0q0HXBHdlZbNAwzNdUuEE9BeqdAsKWXnzKOGcUqm +Mm+yLqrjggQS8dsj/u75dGpLmy0RGWn5v0dbQp0+lRGapUwc0qxEXO6JJCZcXwPw +5aqBZvaPnWqrEH1ytmR1W+k8mD6BKvB0UFmeRwIDAQABAoIBABWKE/liPsEocdfb +wWKoUfSUc7nI1GOzUDkKfHV1zJtlxoZTNlSqwROvjn6X5HRwAw6YUY39hCBjwm0L +Zg919/egtAEJ4xGlj7TIhMHD75XSZXeuo5LbPT+pcapGUxoe8v0gyOjOn+Wc4jnS +DJslzcKLcg5yxTtqxF7vEB5ENoCyfRNwOTTIRqnH68pOfbhNFdEPyxS4uY5doVRF ++n1jHDHyttZ2dVQF3IDaKz380YkkiIj1oqrUq2USZ9zuJg1kn4MIJScSzjPGi4Vh +uqehOCOvNjK5J21zJqcYQ0cKy9hbIwoTZghLPSSwFBciueIKBQBj+Cbuk34ujoS0 +yC4TXwECgYEA2Yx96bRc+fmOmcJjolwNuoJKGA1FJhJ4U9liPxlWO7HRFG2HPsKp +eVFcubQ+Bgcjn1sjek5mB/tQlwRwGzErTrnnj3QQG9h1SUTXfCUrP8wthNK/salH +bKs9Pawu7tVL0qnPCudD7m+MIjpxK+wzQ6VYEZTRYj2xIfnYjRE7bMcCgYEAxDwV +pNqdA02wX/makXSyQu8ANOLnmu8TWnNA0aThKo16e1S5XBMmVephHhPGLxJXx/PS +r6ROq7C2ax2ehdgiZLA/cn4pN1V153dEfrXJAj5bWJAL/FI4pBw3ZkbWj1sScZkw +a1c+dbG7Jn4TvwzyHg7fMQjJkscewUoYNYw/woECgYEAuhSCyvkcojEE+Z4dTQ4C +LCsvp10KbaNUyxvg/mn0qcDSPu/kxBkLjxq5/poeh1dp7++r6/zNJzye1V3Valzn +V0KKv9idwsOmptI8oxLD6KEDul3t29AYqOnbyznRNza40WuwDfp8z6EYM1rb3J7/ +xnfSWc96PdbZAV/NC/TWaJcCgYEAlIYxj5PTUUPE4e9muP8NSTHiAyVWTOL0z/n5 +rnNP5Ugt0xj+Lp1j35VFKmGnF8uXWwTFiyEeVj6q4woJCJ7c8T6g1wR8J6BsW+Pe +mNT4LdWKh86G89Q7gKLGn4ewL9KrdAWRKz1eTfBWiTzVrz2aJ3v0HAXdo1VIht2Q +0z6yp4ECgYBJ98DMPEN0zBZhQ0TzsP09pnjzpztqDImxuMbw00Z56x8QiFFU5WHp +DvOzVetPbFBDX/KB/YZ0IgIGwGNVeRLBGSrav9NoEb7aEawF1rMd0E+SIUlaIdEp +CNSS0rT6CkOy/4Ruj4jrNDSMgOwiP9idf8f5ICAiucqtUnZfWTjuUw== +-----END RSA PRIVATE KEY-----