Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

preq

Package Overview
Dependencies
Maintainers
3
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

preq - npm Package Compare versions

Comparing version 0.5.6 to 0.5.7

.eslintignore

233

index.js
"use strict";
var P = require('bluebird');
var url = require('url');
var util = require('util');
var querystring = require('querystring');
var semver = require('semver');
const P = require('bluebird');
const url = require('url');
const querystring = require('querystring');
const request = require('requestretry');
function createConnectTimeoutAgent(protocol) {
var http = require(protocol);
var Agent = http.Agent;
const http = require(`${protocol}`);
// Many concurrent connections to the same host
function ConnectTimeoutAgent() {
Agent.apply(this, arguments);
}
util.inherits(ConnectTimeoutAgent, Agent);
if (semver.gte(process.version, '5.7.0')) {
ConnectTimeoutAgent.prototype.createSocket = function(req, options, cb) {
var connectTimeoutTimer = setTimeout(function() {
var e = new Error('ETIMEDOUT');
class ConnectTimeoutAgent extends http.Agent {
createSocket(req, options, cb) {
const connectTimeoutTimer = setTimeout(() => {
const e = new Error('ETIMEDOUT');
e.code = 'ETIMEDOUT';
cb(e);
}, this.options.connectTimeout);
Agent.prototype.createSocket.apply(this, [req, options, function(error, newSocket) {
newSocket.on('connect', function() {
super.createSocket(req, options, (error, newSocket) => {
newSocket.on('connect', () => {
clearTimeout(connectTimeoutTimer);
});
cb(error, newSocket);
}]);
};
} else {
ConnectTimeoutAgent.prototype.createSocket = function() {
var s = Agent.prototype.createSocket.apply(this, arguments);
// Set up a connect timeout if connectTimeout option is set
if (this.options.connectTimeout && !s.connectTimeoutTimer) {
s.connectTimeoutTimer = setTimeout(function () {
var e = new Error('ETIMEDOUT');
e.code = 'ETIMEDOUT';
s.end();
s.emit('error', e);
s.destroy();
}, this.options.connectTimeout);
s.once('connect', function () {
if (this.connectTimeoutTimer) {
clearTimeout(this.connectTimeoutTimer);
this.connectTimeoutTimer = undefined;
}
});
}
return s;
};
});
}
}
return ConnectTimeoutAgent;
}
var defaultAgentOptions = {
const defaultAgentOptions = {
connectTimeout: (process.env.PREQ_CONNECT_TIMEOUT || 5) * 1000,

@@ -66,7 +39,5 @@ // Setting this too high (especially 'Infinity') leads to high

};
var httpAgentClass = createConnectTimeoutAgent('http');
var httpsAgentClass = createConnectTimeoutAgent('https');
const httpAgentClass = createConnectTimeoutAgent('http');
const httpsAgentClass = createConnectTimeoutAgent('https');
var request = P.promisify(require('request'), { multiArgs: true });
function getOptions(uri, o, method) {

@@ -78,3 +49,3 @@ if (!o || o.constructor !== Object) {

} else {
o = { uri: uri };
o = { uri };
}

@@ -90,3 +61,3 @@ } else {

o.headers = o.headers || {};
Object.keys(o.headers).forEach(function(header) {
Object.keys(o.headers).forEach((header) => {
if (header.toLowerCase() !== header) {

@@ -107,5 +78,7 @@ o.headers[header.toLowerCase()] = o.headers[header];

if ((o.method === 'get' || o.method === 'put')
&& o.retries === undefined) {
&& o.retries === undefined) {
// Idempotent methods: Retry once by default
o.retries = 1;
o.maxAttempts = 2;
} else {
o.maxAttempts = o.retries + 1;
}

@@ -123,3 +96,4 @@

if ((o.headers && /\bgzip\b/.test(o.headers['accept-encoding'])) || (o.gzip === undefined && o.method === 'get')) {
if ((o.headers && /\bgzip\b/.test(o.headers['accept-encoding']))
|| (o.gzip === undefined && o.method === 'get')) {
o.gzip = true;

@@ -146,15 +120,14 @@ }

*/
function HTTPError(response) {
Error.call(this);
Error.captureStackTrace(this, HTTPError);
this.name = this.constructor.name;
this.message = response.status.toString();
if (response.body && response.body.type) {
this.message += ': ' + response.body.type;
class HTTPError extends Error {
constructor(response) {
super();
Error.captureStackTrace(this, HTTPError);
this.name = this.constructor.name;
this.message = response.status.toString();
if (response.body && response.body.type) {
this.message += `: ${response.body.type}`;
}
Object.assign(this, response);
}
for (var key in response) {
this[key] = response[key];
}
}
util.inherits(HTTPError, Error);

@@ -165,42 +138,28 @@

*/
function Request (method, url, options) {
this.options = getOptions(url, options, method);
this.retries = this.options.retries;
this.timeout = this.options.timeout;
this.delay = 100; // start with 100ms
}
Request.prototype.retry = function (err) {
if (this.retries) {
this.retries--;
// exponential backoff with some fuzz, but start with a short delay
this.delay = this.delay * 2 + this.delay * Math.random();
// grow the timeout linearly, plus some fuzz
this.timeout += this.options.timeout + Math.random() * this.options.timeout;
return P.bind(this)
.delay(this.delay)
.then(this.run);
} else {
throw err;
class Request {
constructor(method, url, options) {
this.options = getOptions(url, options, method);
this.delay = 100; // start with 100ms
this.options.delayStrategy = () => {
// exponential backoff with some fuzz, but start with a short delay
const delay = this.delay;
this.delay = this.delay * 2 + this.delay * Math.random();
return delay;
};
this.options.promiseFactory = resolver => new P(resolver);
this.options.retryStrategy = (err, response) => {
if (response && response.statusCode === 503
&& /^[0-9]+$/.test(response.headers['retry-after'])) {
this.delay = parseInt(response.headers['retry-after'], 10) * 1000;
return true;
}
return request.RetryStrategies.HTTPOrNetworkError(err, response);
};
}
};
Request.prototype.run = function () {
var self = this;
return P.try(function() { return request(self.options) })
.bind(this)
.then(function(responses) {
if (!responses || responses.length < 2) {
return this.retry(new HTTPError({
status: 502,
body: {
type: 'empty_response',
}
}));
} else {
var response = responses[0];
var body = responses[1]; // decompressed
if (self.options.gzip && response.headers) {
run() {
return request(this.options)
.then((response) => {
let body = response.body;
if (this.options.gzip && response.headers) {
delete response.headers['content-encoding'];

@@ -210,4 +169,4 @@ delete response.headers['content-length'];

if (body && response.headers && !self.options._encodingProvided) {
var contentType = response.headers['content-type'];
if (body && response.headers && !this.options._encodingProvided) {
const contentType = response.headers['content-type'];
// Decodes: "text/...", "application/json...", "application/vnd.geo+json..."

@@ -227,20 +186,20 @@ if (/^text\/|application\/([^+;]+\+)?json\b/.test(contentType)) {

if (response.statusCode === 204 || response.statusCode === 205
|| response.statusCode === 304) {
|| response.statusCode === 304) {
body = undefined;
}
var res = {
const res = {
status: response.statusCode,
headers: response.headers,
body: body
body
};
// Check if we were redirected
var origURI = self.options.uri;
if (self.options.qs && Object.keys(self.options.qs).length) {
origURI += '?' + querystring.stringify(self.options.qs);
let origURI = this.options.uri;
if (this.options.qs && Object.keys(this.options.qs).length) {
origURI += `?${querystring.stringify(this.options.qs)}`;
}
if (origURI !== response.request.uri.href
&& url.format(origURI) !== response.request.uri.href) {
&& url.format(origURI) !== response.request.uri.href) {
if (!res.headers['content-location']) {

@@ -254,3 +213,3 @@ // Indicate the redirect via an injected Content-Location

res.headers['content-location'] = url.parse(response.request.uri)
.resolve(res.headers['content-location']);
.resolve(res.headers['content-location']);
}

@@ -260,9 +219,2 @@ }

if (res.status >= 400) {
if (res.status === 503
&& /^[0-9]+$/.test(response.headers['retry-after'])
&& parseInt(response.headers['retry-after']) * 1000 < self.options.timeout) {
self.delay = parseInt(response.headers['retry-after']) * 1000;
return self.retry(new HTTPError(res));
}
throw new HTTPError(res);

@@ -272,32 +224,29 @@ } else {

}
}
},
function (err) {
return this.retry(new HTTPError({
status: 504,
body: {
type: 'internal_http_error',
description: err.toString(),
error: err,
stack: err.stack,
uri: self.options.uri,
method: self.options.method,
},
stack: err.stack
}));
});
};
}, (err) => {
throw new HTTPError({
status: err.status || 504,
body: {
type: 'internal_http_error',
description: err.toString(),
error: err,
stack: err.stack,
uri: this.options.uri,
method: this.options.method,
},
stack: err.stack
});
});
}
}
var preq = function preq (url, options) {
var method = (options || url || {}).method || 'get';
const preq = (url, options) => {
const method = (options || url || {}).method || 'get';
return new Request(method, url, options).run();
};
var methods = ['get','head','put','post','delete','trace','options','mkcol','patch'];
methods.forEach(function(method) {
preq[method] = function (url, options) {
return new Request(method, url, options).run();
};
const methods = ['get', 'head', 'put', 'post', 'delete', 'trace', 'options', 'mkcol', 'patch'];
methods.forEach((method) => {
preq[method] = (url, options) => new Request(method, url, options).run();
});
module.exports = preq;
{
"name": "preq",
"version": "0.5.6",
"version": "0.5.7",
"description": "Yet another promising request wrapper",
"main": "index.js",
"scripts": {
"test": "mocha && nsp check"
"test": "mocha && nsp check",
"coverage": "istanbul cover _mocha -- -R spec",
"coveralls": "cat ./coverage/lcov.info | coveralls"
},
"author": "Gabriel Wicke <gwicke@wikimedia.org>",
"license": "MIT",
"engines": {
"node": ">=6.0.0"
},
"dependencies": {
"bluebird": "^3.1.1",
"request": "^2.85.0",
"semver": "^5.5.0"
"bluebird": "^3.5.2",
"request": "^2.88.0",
"requestretry": "^3.0.2"
},
"devDependencies": {
"mocha": "^5.0.4",
"coveralls": "^3.0.2",
"eslint-config-node-services": "^2.2.5",
"eslint-config-wikimedia": "^0.8.1",
"eslint-plugin-jsdoc": "^3.9.1",
"eslint-plugin-json": "^1.2.1",
"eslint": "^5.8.0",
"istanbul": "^0.4.5",
"mocha": "^5.2.0",
"mocha-eslint": "^4.1.0",
"mocha-lcov-reporter": "^1.3.0",
"nock": "^10.0.1",
"nsp": "^3.2.1"

@@ -19,0 +34,0 @@ },

@@ -1,50 +0,131 @@

var preq = require('../index');
var assert = require('assert');
'use strict';
// mocha defines to avoid JSHint breakage
/* global describe, it, before, beforeEach, after, afterEach */
const nock = require('nock');
const zlib = require('zlib');
const preq = require('../index');
const assert = require('assert');
require('mocha-eslint')([ '.' ]);
describe('preq', function() {
this.timeout(30000);
it('should retry', function() {
var tStart = new Date();
this.timeout(30000); // eslint-disable no-invalid-this
it('throws with undefined options', () =>
assert.throws(() => preq(), Error, 'Must throw if options not provided'));
it('should retry', () => {
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.times(5)
.reply(504, '');
const tStart = new Date();
return preq.get({
// Some unreachable port
uri: 'http://localhost:1/',
uri: 'https://en.wikipedia.org/wiki/Main_Page',
retries: 4
})
.catch(function(e) {
.catch((e) => {
assert.equal(e.status, 504);
var tDelta = new Date() - tStart;
if (tDelta < 3150) {
const tDelta = new Date() - tStart;
if (tDelta < 1000) {
throw new Error("Does not look as if this actually retried!");
}
});
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('get enwiki front page', function() {
it('should not retry 404', () => {
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(404, '');
const tStart = new Date();
return preq.get({
// Some unreachable port
uri: 'https://en.wikipedia.org/wiki/Main_Page',
retries: 4
})
.then(function(res) {
.catch((e) => {
assert.equal(e.status, 404);
const tDelta = new Date() - tStart;
if (tDelta > 1000) {
throw new Error("Looks like this was actually retried!");
}
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('should respect retry-after', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(503, '', { 'retry-after': 3 })
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY);
const tStart = new Date();
return preq.get({
// Some unreachable port
uri: 'https://en.wikipedia.org/wiki/Main_Page',
retries: 1
})
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.body, MOCK_BODY);
const tDelta = new Date() - tStart;
if (tDelta < 2500) {
throw new Error("retry-after was not respected");
}
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('should get enwiki front page', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY);
return preq.get({
uri: 'https://en.wikipedia.org/wiki/Main_Page',
})
.then((res) => {
assert.equal(res.status, 200);
assert.equal(!!res.body, true);
// Make sure content-location is not set
assert.equal(!!res.headers['content-location'], false);
});
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('get google.com, check for redirect', function() {
it('should check for redirect', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/')
.reply(301, undefined, { location: 'https://en.wikipedia.org/wiki/Main_Page' })
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY);
return preq.get({
uri: 'https://en.wikipedia.org/',
retries: 2
uri: 'https://en.wikipedia.org/'
})
.then(function(res) {
.then((res) => {
assert.equal(res.status, 200);
assert.equal(!!res.body, true);
assert.equal(res.headers['content-location'],
'https://en.wikipedia.org/wiki/Main_Page');
});
'https://en.wikipedia.org/wiki/Main_Page');
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('get google.com with query', function() {
it('should support query', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.query({ q : 'foo' })
.reply(200, MOCK_BODY);
return preq.get({
uri: 'http://google.com/',
uri: 'https://en.wikipedia.org/wiki/Main_Page',
query: {

@@ -54,18 +135,33 @@ q: 'foo'

})
.then(function(res) {
.then((res) => {
assert.equal(res.status, 200);
assert.equal(!!res.body, true);
});
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('get google.com, simple constructor style', function() {
return preq('http://google.com/')
.then(function(res) {
it('should support simple constructor style', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY);
return preq('https://en.wikipedia.org/wiki/Main_Page')
.then((res) => {
assert.equal(res.status, 200);
assert.equal(!!res.body, true);
});
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('get google.com with query, constructor style', function() {
it('should support simple constructor style with query', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.query({ q : 'foo' })
.reply(200, MOCK_BODY);
return preq({
method: 'get',
uri: 'http://google.com/',
uri: 'https://en.wikipedia.org/wiki/Main_Page',
query: {

@@ -75,31 +171,164 @@ q: 'foo'

})
.then(function(res) {
.then((res) => {
assert.equal(res.status, 200);
assert.equal(!!res.body, true);
});
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('return buffer on user-supplied encoding', function() {
return preq('http://google.com/', {encoding: null})
.then(function(res) {
it('return buffer on user-supplied encoding', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY);
return preq('https://en.wikipedia.org/wiki/Main_Page', { encoding: null })
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.body.constructor.name, 'Buffer');
});
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('return string with no encoding', function() {
return preq('http://google.com/')
.then(function(res) {
assert.equal(res.status, 200);
assert.equal(typeof res.body, 'string');
});
});
it('no content-encoding header for gzipped responses', function() {
it('no content-encoding header for gzipped responses', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(200, zlib.gzipSync(Buffer.from(MOCK_BODY)), { 'content-encoding': 'gzip' });
return preq({
uri: 'https://en.wikipedia.org/api/rest_v1/page/html/Foobar',
uri: 'https://en.wikipedia.org/wiki/Main_Page',
gzip: true
}).then(function(res) {
})
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.headers['content-encoding'], undefined);
});
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('parse json', () => {
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(200, { test: 'test' }, { 'content-type': 'application/json' });
return preq('https://en.wikipedia.org/wiki/Main_Page')
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.body.test, 'test');
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('resolve relative redirects', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org')
.get('/')
.reply(301, undefined, { 'location': '/wiki/Main_Page' })
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY, { 'content-location': '/wiki/Main_Page' });
return preq('https://en.wikipedia.org')
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.headers['content-location'],
'https://en.wikipedia.org/wiki/Main_Page');
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('remove body for 204 requests', () => {
const api = nock('https://en.wikipedia.org')
.get('/wiki/Main_Page')
.reply(204, "SOME_ERRORNEOUS_BODY");
return preq('https://en.wikipedia.org/wiki/Main_Page')
.then((res) => {
assert.equal(res.status, 204);
assert.equal(res.body, undefined);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('lowecase request headers', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const api = nock('https://en.wikipedia.org', {
reqheaders: {
'cache-control': 'no-cache',
'x-request-id': 'test_id'
}
})
.get('/wiki/Main_Page')
.reply(200, MOCK_BODY);
return preq({
uri: 'https://en.wikipedia.org/wiki/Main_Page',
headers: {
'Cache-Control': 'no-cache',
'x-request-id': 'test_id'
}
})
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('sends JSON', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const MOCK_REQ = { test: 'test' };
const api = nock('https://en.wikipedia.org')
.post('/wiki/Main_Page', JSON.stringify(MOCK_REQ))
.reply(200, MOCK_BODY);
return preq({
method: 'post',
uri: 'https://en.wikipedia.org/wiki/Main_Page',
headers: {
'content-type': 'application/json'
},
body: MOCK_REQ
})
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('sends form', () => {
const MOCK_BODY = 'Main_Wiki_Page_HTML';
const MOCK_REQ = { test: 'test' };
const api = nock('https://en.wikipedia.org')
.post('/wiki/Main_Page', 'test=test')
.reply(200, MOCK_BODY);
return preq({
method: 'post',
uri: 'https://en.wikipedia.org/wiki/Main_Page',
body: MOCK_REQ
})
.then((res) => {
assert.equal(res.status, 200);
assert.equal(res.body, MOCK_BODY);
})
.then(() => api.done())
.finally(() => nock.cleanAll());
});
it('request some real content, no nock', () => preq('https://en.wikipedia.org/wiki/Main_Page')
.then((res) => {
assert.equal(res.status, 200);
assert.equal(!!res.body, true);
}));
it('timeout with connect timeout', () => preq({
uri: 'http://localhost:12345',
connectTimeout: 1
})
.catch(e => assert.equal(e.status, 504)));
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc