Skip to content

Commit

Permalink
Add support for socketTimeout & connectTimeout
Browse files Browse the repository at this point in the history
Drop support for node < 6
  • Loading branch information
martinj committed May 6, 2019
1 parent baf4b00 commit 2c47f13
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 156 deletions.
14 changes: 14 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"plugins": [
"mocha"
],
"extends": "@aptoma/eslint-config",
"parserOptions": {
"ecmaVersion": 9
},
"env": {
"node": true,
"mocha": true,
"es6": true
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
coverage
.nyc_output
43 changes: 0 additions & 43 deletions .jshintrc

This file was deleted.

10 changes: 3 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
language: node_js
node_js:
- 0.10
- 0.12
- 4
- 5

script:
- npm run ci
- 6
- 10
- 12
128 changes: 81 additions & 47 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
'use strict';
var request = require('request');
var streamify = require('streamify');
var path = require('path');
var fs = require('fs');
var Readable = require('stream').Readable;
var Promise = require('bluebird');
const request = require('request');
const streamify = require('streamify');
const path = require('path');
const fs = require('fs');
const Readable = require('stream').Readable;
const Promise = require('bluebird');

/**
* Is status code 2xx
Expand All @@ -21,7 +21,7 @@ function isOk(statusCode) {
* @return {Promise} resolves with response or rejects with ResponseError or ConnectionError
*/
function requestProm(opts) {
return new Promise(function (resolve, reject) {
return new Promise((resolve, reject) => {
createRequest(opts, resolve, reject);
});
}
Expand All @@ -32,7 +32,7 @@ function requestProm(opts) {
* @param {Object} [opts] options to request
* @return {Promise} resolves with response
*/
['GET', 'POST', 'PATCH', 'DELETE', 'HEAD', 'PUT'].forEach(function (method) {
['GET', 'POST', 'PATCH', 'DELETE', 'HEAD', 'PUT'].forEach((method) => {
// create short hand functions
requestProm[method.toLowerCase()] = function (url, opts) {
opts = opts || {};
Expand All @@ -44,27 +44,26 @@ function requestProm(opts) {

/**
* Make a request that returns a stream thats not sensitive to use after a process.nextTick()
* @param {String} url
* @param {Object} opts options to request
* @return {Stream} from streamify
*/
requestProm.stream = function (opts) {
var stream = streamify();
requestProm.stream = function (opts) {
const stream = streamify();
opts = opts || {};

var req = request(opts);
req.on('error', function (err) {
const req = request(opts);
req.on('error', (err) => {
if (err.code && (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT')) {
return stream.emit('error', new ConnectionError(
'Connect timeout occurred when requesting url: ' + (opts.url || opts.uri),
err.code
err.code
));
}

return stream.emit('error', new ConnectionError(err.message, err.code));
});

req.on('response', function (res) {
req.on('response', (res) => {
if (!isOk(res.statusCode)) {
stream.emit('error', new ResponseError(
'Server responded with ' + res.statusCode + ', unable get data from url: ' + (opts.url || opts.uri),
Expand All @@ -84,19 +83,19 @@ function requestProm(opts) {
* @param {Object} [opts] options to request
* @return {Promise} resolves with response
*/
requestProm.postFile = function (url, file, opts) {
requestProm.postFile = function (url, file, opts) {
opts = opts || {};
opts.url = url;
opts.method = 'POST';

return new Promise(function (resolve, reject) {
var req = createRequest(opts, resolve, reject),
form = req.form();
return new Promise((resolve, reject) => {
const req = createRequest(opts, resolve, reject);
const form = req.form();

if (file instanceof Readable) {
form.append('file', file);
} else {
form.append('file', fs.createReadStream(file), { filename: path.basename(file) });
form.append('file', fs.createReadStream(file), {filename: path.basename(file)});
}
});
};
Expand All @@ -109,38 +108,76 @@ function requestProm(opts) {
* @return {Request}
*/
function createRequest(opts, resolve, reject) {
return request(opts,
function (err, res, body) {
if (err) {
if (err.code && (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT')) {
return reject(new ConnectionError(
'Connect timeout occurred when requesting url: ' + (opts.url || opts.uri),
err.code
));
}

return reject(new ConnectionError(err.message, err.code));
if (opts.timeout && (opts.socketTimeout || opts.connectTimeout)) {
reject(new Error('Can\'t use socketTimeout/connectTimeout in conjuction with timeout'));
}

const req = request(opts, (err, res, body) => { // eslint-disable-line complexity
if (err) {
if (err.code && (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT')) {
return reject(new ConnectionError(
`Connect timeout occurred when requesting url: ${opts.url || opts.uri}`,
err.code
));
}

if (!isOk(res.statusCode)) {
return reject(new ResponseError('Request to ' + (opts.url || opts.uri) + ' failed. code: ' + res.statusCode, res));
}
return reject(new ConnectionError(err.message, err.code));
}

if (opts.json && typeof(body) !== 'object') {
return reject(new ResponseError('Unable to parse json from url: ' + (opts.url || opts.uri), res));
}
if (!isOk(res.statusCode)) {
return reject(new ResponseError(`Request to ${opts.url || opts.uri} failed. code: ${res.statusCode}`, res));
}

return resolve(res);
if (opts.json && typeof (body) !== 'object') {
return reject(new ResponseError(`Unable to parse json from url: ${opts.url || opts.uri}`, res));
}
);

return resolve(res);
});

if (opts.socketTimeout) {
req.on('socket', (socket) => {
if (!socket.connecting) {
socket.setTimeout(opts.socketTimeout, sockTimeout);
return;
}

socket.on('connect', () => {
socket.setTimeout(opts.socketTimeout, sockTimeout);
});
});
}

if (opts.connectTimeout) {
const connectTimeoutId = setTimeout(() => {
if (req.req) {
req.abort();
const e = new Error('ESOCKETTIMEDOUT');
e.code = 'ESOCKETTIMEDOUT';
e.connect = true;
req.emit('error', e);
}
}, opts.connectTimeout);

req.on('connect', () => {
clearTimeout(connectTimeoutId);
});
}

return req;

function sockTimeout() {
req.abort();
const e = new Error('ESOCKETTIMEDOUT');
e.code = 'ESOCKETTIMEDOUT';
e.connect = false;
req.emit('error', e);
}
}

/**
* ResponseError
*/
function ResponseError(message, response) {
this.message = message;
this.name = "ResponseError";
this.name = 'ResponseError';
this.response = response;
this.statusCode = response.statusCode;
Error.captureStackTrace(this, ResponseError);
Expand All @@ -150,13 +187,10 @@ ResponseError.prototype.constructor = ResponseError;

requestProm.ResponseError = ResponseError;

/**
* ConnectionError
*/
function ConnectionError(message, code) {
this.message = message;
this.code = code;
this.name = "ConnectionError";
this.name = 'ConnectionError';
Error.captureStackTrace(this, ConnectionError);
}
ConnectionError.prototype = Object.create(Error.prototype);
Expand Down
21 changes: 12 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
"description": "Bluebird promise wrapper for request.",
"main": "index.js",
"scripts": {
"test": "jshint test/ index.js && istanbul test --preload-sources _mocha -- -u exports -R spec test/*.test.js test/**/*.test.js",
"ci": "npm test --coverage && istanbul report cobertura",
"lint": "eslint --ext '.js' .",
"test": "npm run lint && NODE_ENV=test nyc --reporter=text-summary --reporter=lcov mocha --exit 'test/*.test.js' 'test/**/*.test.js'",
"watch": "NODE_ENV=test BLUEBIRD_DEBUG=1 mocha --watch 'test/**/*.js' 'index.js' --timeout 1000",
"release": "npm test && release-it -n -i patch",
"release:minor": "npm test && release-it -n -i minor",
"release:major": "npm test && release-it -n -i major"
Expand All @@ -27,16 +28,18 @@
},
"homepage": "https://github.com/martinj/node-request-prom",
"dependencies": {
"bluebird": "^3.4.6",
"request": "^2.75.0",
"bluebird": "^3.5.4",
"request": "^2.88.0",
"streamify": "^0.2.6"
},
"devDependencies": {
"istanbul": "^0.4.5",
"jshint": "^2.9.3",
"mocha": "^3.1.2",
"nock": "^8.1.0",
"@aptoma/eslint-config": "^7.0.1",
"eslint": "^5.16.0",
"eslint-plugin-mocha": "^5.3.0",
"mocha": "^6.1.4",
"nock": "^10.0.6",
"nyc": "^14.1.0",
"release-it": "^2.4.3",
"should": "^11.1.1"
"should": "^13.2.3"
}
}
Loading

0 comments on commit 2c47f13

Please sign in to comment.