Skip to content

Commit

Permalink
switch from request to axios for http(s)
Browse files Browse the repository at this point in the history
Since request is no longer receiving updates, switch to axios. Attempt to keep the same options as much as possible.

However these changes occurred in the switch:

  1. There is only one option for redirects `followRedirect: true|false` (defaults to true).
  2. `httpSignature` is not implemented in axios and now not available. (If this feature is still desired, please help by providing a pull request to implement it.)
  3. `auth` now allows `username` and `password` but not the aliases `user` and `pass`
  • Loading branch information
jeffbski committed Apr 19, 2020
1 parent d0f5f86 commit 26dcf9e
Show file tree
Hide file tree
Showing 7 changed files with 534 additions and 708 deletions.
25 changes: 9 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ var opts = {
'tcp:foo.com:8000',
'socket:/my/sock',
'http://unix:/my/sock:/my/url',
'http-get://unix:/my/sock:/my/url'
'http-get://unix:/my/sock:/my/url',
],
delay: 1000, // initial delay in ms, default 0
interval: 100, // poll interval in ms, default 250ms
Expand All @@ -176,22 +176,17 @@ var opts = {
passphrase: 'yourpassphrase',
auth: {
user: 'theuser', // or username
pass: 'thepassword' // or password
},
httpSignature: {
keyId: 'yourKeyId',
key: 'yourKey'
pass: 'thepassword', // or password
},
strictSSL: false,
followAllRedirects: true,
followRedirect: true,
headers: {
'x-custom': 'headers'
}
'x-custom': 'headers',
},
};

// Usage with callback function
waitOn(opts, function(err) {
waitOn(opts, function (err) {
if (err) {
return handleError(err);
}
Expand All @@ -200,10 +195,10 @@ waitOn(opts, function(err) {

// Usage with promises
waitOn(opts)
.then(function() {
.then(function () {
// once here, all resources are available
})
.catch(function(err) {
.catch(function (err) {
handleError(err);
});

Expand All @@ -227,17 +222,15 @@ waitOn(opts, [cb]) - function which triggers resource checks
- opts.tcpTimeout - optional tcp timeout in ms, default 300ms
- opts.verbose - optional flag which outputs debug output, default false
- opts.window - optional stabilization time in ms, default 750ms. Waits this amount of time for file sizes to stabilize or other resource availability to remain unchanged.
- http(s) specific options, see https://github.com/request/request#readme for specific details
- http(s) specific options, see https://nodejs.org/api/tls.html#tls_tls_connect_options_callback for specific details

- opts.ca: [ /* strings or binaries */ ],
- opts.cert: [ /* strings or binaries */ ],
- opts.key: [ /* strings or binaries */ ],
- opts.passphrase: 'yourpassphrase',
- opts.auth: { user, pass }
- opts.httpSignature: { keyId, key }
- opts.strictSSL: false,
- opts.followAllRedirects: true,
- opts.followRedirect: true,
- opts.followRedirect: false, // defaults to true
- opts.headers: { 'x-custom': 'headers' },

- cb(err) - if err is provided then, resource checks did not succeed
Expand Down
25 changes: 13 additions & 12 deletions exampleConfig.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
module.exports = {
// specify additional options here, especially http(s)
// see https://github.com/request/request#readme for specifics
ca: [ /* strings or binaries */],
cert: [ /* strings or binaries */],
key: [ /* strings or binaries */],
// see https://nodejs.org/api/tls.html#tls_tls_connect_options_callback for specifics
ca: [
/* strings or binaries */
],
cert: [
/* strings or binaries */
],
key: [
/* strings or binaries */
],
passphrase: 'yourpassphrase',
auth: {
user: 'yourusername',
pass: 'yourpassword'
},
httpSignature: {
keyId: 'keyId',
key: 'yourkey'
pass: 'yourpassword',
},
strictSSL: false,
followAllRedirects: false,
followRedirect: false,
headers: {
'x-custom': 'headers'
}
'x-custom': 'headers',
},
};
151 changes: 68 additions & 83 deletions lib/wait-on.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
const fs = require('fs');
const { promisify } = require('util');
const Joi = require('@hapi/joi');
const https = require('https');
const net = require('net');
const requestProm = require('request-promise-native');
const util = require('util');
const axios = require('axios').default;
const { isBoolean, isEmpty, negate, noop, once, partial, pick, zip } = require('lodash/fp');
const { NEVER, combineLatest, from, merge, throwError, timer } = require('rxjs');
const { distinctUntilChanged, map, mergeMap, scan, startWith, take, takeWhile } = require('rxjs/operators');
Expand All @@ -15,62 +17,34 @@ const fstat = promisify(fs.stat);
const PREFIX_RE = /^((https?-get|https?|tcp|socket|file):)(.+)$/;
const HOST_PORT_RE = /^(([^:]*):)?(\d+)$/;
const HTTP_GET_RE = /^https?-get:/;
const HTTP_UNIX_RE = /^http:\/\/unix:([^:]+):([^:]+)$/;
const TIMEOUT_ERR_MSG = 'Timeout';

const WAIT_ON_SCHEMA = Joi.object({
resources: Joi.array()
.items(Joi.string().required())
.required(),
delay: Joi.number()
.integer()
.min(0)
.default(0),
httpTimeout: Joi.number()
.integer()
.min(0),
interval: Joi.number()
.integer()
.min(0)
.default(250),
resources: Joi.array().items(Joi.string().required()).required(),
delay: Joi.number().integer().min(0).default(0),
httpTimeout: Joi.number().integer().min(0),
interval: Joi.number().integer().min(0).default(250),
log: Joi.boolean().default(false),
reverse: Joi.boolean().default(false),
simultaneous: Joi.number()
.integer()
.min(1)
.default(Infinity),
timeout: Joi.number()
.integer()
.min(0)
.default(Infinity),
simultaneous: Joi.number().integer().min(1).default(Infinity),
timeout: Joi.number().integer().min(0).default(Infinity),
verbose: Joi.boolean().default(false),
window: Joi.number()
.integer()
.min(0)
.default(750),
tcpTimeout: Joi.number()
.integer()
.min(0)
.default(300),

// http options3
window: Joi.number().integer().min(0).default(750),
tcpTimeout: Joi.number().integer().min(0).default(300),

// http/https options
ca: [Joi.string(), Joi.binary()],
cert: [Joi.string(), Joi.binary()],
key: [Joi.string(), Joi.binary()],
key: [Joi.string(), Joi.binary(), Joi.object()],
passphrase: Joi.string(),
auth: Joi.object({
user: Joi.string(),
username: Joi.string(),
password: Joi.string(),
pass: Joi.string()
}),
httpSignature: Joi.object({
keyId: Joi.string().required(),
key: Joi.string().required()
}),
strictSSL: Joi.boolean(),
followAllRedirects: Joi.boolean(),
followRedirect: Joi.boolean(),
headers: Joi.object()
strictSSL: Joi.boolean().default(false),
followRedirect: Joi.boolean().default(true), // HTTP 3XX responses
headers: Joi.object(),
});

/**
Expand All @@ -86,6 +60,9 @@ const WAIT_ON_SCHEMA = Joi.object({
- https-get: - HTTPS GET returns 2XX response. ex: https://my/bar
- tcp:my.server.com:3000 verifies a service is listening on port
- socket:/path/sock verifies a service is listening on (UDS) socket
For http over socket, use http://unix:SOCK_PATH:URL_PATH
like http://unix:/path/to/sock:/foo/bar or
http-get://unix:/path/to/sock:/foo/bar
@param opts object configuring waitOn
@param opts.resources array of string resources to wait for. prefix determines the type of resource with the default type of `file:`
Expand All @@ -107,8 +84,8 @@ function waitOn(opts, cb) {
return waitOnImpl(opts, cb);
} else {
// promise API
return new Promise(function(resolve, reject) {
waitOnImpl(opts, function(err) {
return new Promise(function (resolve, reject) {
waitOnImpl(opts, function (err) {
if (err) {
reject(err);
} else {
Expand All @@ -129,7 +106,7 @@ function waitOnImpl(opts, cbFunc) {
...validResult.value, // use defaults
// window needs to be at least interval
...(validResult.value.window < validResult.value.interval ? { window: validResult.value.interval } : {}),
...(validResult.value.verbose ? { log: true } : {}) // if debug logging then normal log is also enabled
...(validResult.value.verbose ? { log: true } : {}), // if debug logging then normal log is also enabled
};

const { resources, log: shouldLog, timeout, verbose, reverse } = validatedOpts;
Expand Down Expand Up @@ -167,14 +144,14 @@ function waitOnImpl(opts, cbFunc) {
const resourcesCompleted$ = combineLatest(resources.map(createResourceWithDeps$));

merge(timeoutError$, resourcesCompleted$)
.pipe(takeWhile(resourceStates => resourceStates.some(x => !x)))
.pipe(takeWhile((resourceStates) => resourceStates.some((x) => !x)))
.subscribe({
next: resourceStates => {
next: (resourceStates) => {
lastResourcesState = resourceStates;
logWaitingForWDeps(resourceStates);
},
error: cleanup,
complete: cleanup
complete: cleanup,
});
}

Expand Down Expand Up @@ -214,7 +191,7 @@ function createFileResource$(
) {
const filePath = extractPath(resource);
const checkOperator = reverse
? map(size => size === -1) // check that file does not exist
? map((size) => size === -1) // check that file does not exist
: scan(
// check that file exists and the size is stable
(acc, x) => {
Expand Down Expand Up @@ -244,7 +221,7 @@ function createFileResource$(
return from(getFileSize(filePath));
}, simultaneous),
checkOperator,
map(x => (isNotABoolean(x) ? false : x)),
map((x) => (isNotABoolean(x) ? false : x)),
startWith(false),
distinctUntilChanged(),
take(2)
Expand Down Expand Up @@ -277,35 +254,39 @@ async function getFileSize(filePath) {
}

function createHTTP$({ validatedOpts, output }, resource) {
const { delay, interval, reverse, simultaneous } = validatedOpts;
const method = HTTP_GET_RE.test(resource) ? 'GET' : 'HEAD';
const uri = resource.replace('-get:', ':');
const {
delay,
followRedirect,
httpTimeout: timeout,
interval,
reverse,
simultaneous,
strictSSL: rejectUnauthorized,
} = validatedOpts;
const method = HTTP_GET_RE.test(resource) ? 'get' : 'head';
const url = resource.replace('-get:', ':');
const matchHttpUnixSocket = HTTP_UNIX_RE.exec(url); // http://unix:/sock:/url
const urlSocketOptions = matchHttpUnixSocket
? { socketPath: matchHttpUnixSocket[1], url: matchHttpUnixSocket[2] }
: { url };
const socketPathDesc = urlSocketOptions.socketPath ? `socketPath:${urlSocketOptions.socketPath}` : '';
const httpOptions = {
...pick(
[
'auth',
'httpSignature',
'followRedirect',
'followAllRedirects',
'strictSSL',
'headers',
'cert',
'key',
'passphrase',
'ca'
],
validatedOpts
),
...(validatedOpts.httpTimeout ? { timeout: validatedOpts.httpTimeout } : {}),
uri,
...pick(['auth', 'headers'], validatedOpts),
httpsAgent: new https.Agent({
rejectUnauthorized,
...pick(['ca', 'cert', 'key', 'passphrase'], validatedOpts),
}),
...(followRedirect ? {} : { maxRedirects: 0 }), // defaults to 5 (enabled)
...(timeout && { timeout }),
...urlSocketOptions,
method,
resolveWithFullResponse: true,
simple: true // statusCodes other than 2xx will reject
// by default it provides full response object
// validStatus is 2xx unless followRedirect is true (default)
};
const checkFn = reverse ? negateAsync(httpCallSucceeds) : httpCallSucceeds;
return timer(delay, interval).pipe(
mergeMap(() => {
output(`making HTTP ${method} request to ${uri} ...`);
output(`making HTTP(S) ${method} request to ${socketPathDesc} url:${urlSocketOptions.url} ...`);
return from(checkFn(output, httpOptions));
}, simultaneous),
startWith(false),
Expand All @@ -316,11 +297,15 @@ function createHTTP$({ validatedOpts, output }, resource) {

async function httpCallSucceeds(output, httpOptions) {
try {
const result = await requestProm(httpOptions);
output(` HTTP result for ${httpOptions.uri}: ${JSON.stringify(result)}`);
const result = await axios(httpOptions);
output(
` HTTP(S) result for ${httpOptions.url}: ${util.inspect(
pick(['status', 'statusText', 'headers', 'data'], result)
)}`
);
return true;
} catch (err) {
output(` HTTP error for ${httpOptions.uri} ${err.toString()}`);
output(` HTTP(S) error for ${httpOptions.url} ${err.toString()}`);
return false;
}
}
Expand All @@ -342,10 +327,10 @@ function createTCP$({ validatedOpts: { delay, interval, tcpTimeout, reverse, sim
async function tcpExists(output, tcpPath, tcpTimeout) {
const [, , /* full, hostWithColon */ hostMatched, port] = HOST_PORT_RE.exec(tcpPath);
const host = hostMatched || 'localhost';
return new Promise(resolve => {
return new Promise((resolve) => {
const conn = net
.connect(port, host)
.on('error', err => {
.on('error', (err) => {
output(` error connecting to TCP host:${host} port:${port} ${err.toString()}`);
resolve(false);
})
Expand Down Expand Up @@ -378,10 +363,10 @@ function createSocket$({ validatedOpts: { delay, interval, reverse, simultaneous
}

async function socketExists(output, socketPath) {
return new Promise(resolve => {
return new Promise((resolve) => {
const conn = net
.connect(socketPath)
.on('error', err => {
.on('error', (err) => {
output(` error connecting to socket socket:${socketPath} ${err.toString()}`);
resolve(false);
})
Expand All @@ -394,7 +379,7 @@ async function socketExists(output, socketPath) {
}

function negateAsync(asyncFn) {
return async function(...args) {
return async function (...args) {
return !(await asyncFn(...args));
};
}
Expand Down
Loading

0 comments on commit 26dcf9e

Please sign in to comment.