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

needle

Package Overview
Dependencies
Maintainers
1
Versions
112
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

needle - npm Package Compare versions

Comparing version 3.2.0 to 3.3.0

examples/stream-multiple/app.js

4

lib/decoder.js

@@ -24,2 +24,3 @@ var iconv,

var matches = regex.exec(chunk.toString());
if (matches) {

@@ -32,3 +33,4 @@ var found = matches[1].toLowerCase().replace('utf8', 'utf-8'); // canonicalize;

if (this.charset == 'utf-8') { // no need to decode, just pass through
// if charset is already utf-8 or given encoding isn't supported, just pass through
if (this.charset == 'utf-8' || !iconv.encodingExists(this.charset)) {
this.push(chunk);

@@ -35,0 +37,0 @@ return done();

//////////////////////////////////////////
// Needle -- HTTP Client for Node.js
// Written by Tomás Pollak <tomas@forkhq.com>
// (c) 2012-2020 - Fork Ltd.
// (c) 2012-2023 - Fork Ltd.
// MIT Licensed

@@ -13,3 +13,3 @@ //////////////////////////////////////////

stream = require('stream'),
debug = require('debug')('needle'),
debug = require('util').debuglog('needle'),
stringify = require('./querystring').build,

@@ -101,2 +101,5 @@ multipart = require('./multipart'),

// abort signal
signal : null,
// booleans

@@ -111,3 +114,4 @@ compressed : false,

follow_if_same_protocol : false,
follow_if_same_location : false
follow_if_same_location : false,
use_proxy_from_env_var : true
}

@@ -194,3 +198,4 @@

localAddress: get_option('localAddress', undefined),
lookup: get_option('lookup', undefined)
lookup: get_option('lookup', undefined),
signal: get_option('signal', defaults.signal)
}, // passed later to http.request() directly

@@ -212,2 +217,5 @@ headers : {},

if (config.http_opts.signal && !(config.http_opts.signal instanceof AbortSignal))
throw new TypeError(typeof config.http_opts.signal + ' received for signal, but expected an AbortSignal');
// populate http_opts with given TLS options

@@ -263,8 +271,10 @@ tls_options.split(' ').forEach(function(key) {

var env_proxy = utils.get_env_var(['HTTP_PROXY', 'HTTPS_PROXY'], true);
if (!config.proxy && env_proxy) config.proxy = env_proxy;
if (config.use_proxy_from_env_var) {
var env_proxy = utils.get_env_var(['HTTP_PROXY', 'HTTPS_PROXY'], true);
if (!config.proxy && env_proxy) config.proxy = env_proxy;
}
// if proxy is present, set auth header from either url or proxy_user option.
if (config.proxy) {
if (utils.should_proxy_to(uri)) {
if (!config.use_proxy_from_env_var || utils.should_proxy_to(uri)) {
if (config.proxy.indexOf('http') === -1)

@@ -452,2 +462,3 @@ config.proxy = 'http://' + config.proxy;

protocol = request_opts.protocol == 'https:' ? https : http;
signal = request_opts.signal;

@@ -488,2 +499,7 @@ function done(err, resp) {

function abort_handler() {
out.emit('err', new Error('Aborted by signal.'));
request.destroy();
}
function set_timeout(type, milisecs) {

@@ -495,9 +511,10 @@ if (timer) clearTimeout(timer);

out.emit('timeout', type);
request.abort();
request.destroy();
// also invoke done() to terminate job on read_timeout
if (type == 'read') done(new Error(type + ' timeout'));
signal && signal.removeEventListener('abort', abort_handler);
}, milisecs);
}
debug('Making request #' + count, request_opts);

@@ -775,3 +792,11 @@ request = protocol.request(request_opts, function(resp) {

out.abort = function() { request.abort() }; // easier access
if (signal) { // abort signal given, so handle it
if (signal.aborted === true) {
abort_handler();
} else {
signal.addEventListener('abort', abort_handler, { once: true });
}
}
out.abort = function() { request.destroy() }; // easier access
out.request = request;

@@ -811,4 +836,4 @@ return out;

if (defaults.hasOwnProperty(target_key) && typeof obj[key] != 'undefined') {
if (target_key != 'parse_response' && target_key != 'proxy' && target_key != 'agent') {
// ensure type matches the original, except for proxy/parse_response that can be null/bool or string
if (target_key != 'parse_response' && target_key != 'proxy' && target_key != 'agent' && target_key != 'signal') {
// ensure type matches the original, except for proxy/parse_response that can be null/bool or string, and signal that can be null/AbortSignal
var valid_type = defaults[target_key].constructor.name;

@@ -818,2 +843,4 @@

throw new TypeError('Invalid type for ' + key + ', should be ' + valid_type);
} else if (target_key === 'signal' && obj[key] !== null && !(obj[key] instanceof AbortSignal)) {
throw new TypeError('Invalid type for ' + key + ', should be AbortSignal');
}

@@ -820,0 +847,0 @@ defaults[target_key] = obj[key];

@@ -100,2 +100,3 @@ //////////////////////////////////////////

'application/json',
'application/hal+json',
'text/javascript',

@@ -102,0 +103,0 @@ 'application/vnd.api+json'

{
"name": "needle",
"version": "3.2.0",
"version": "3.3.0",
"description": "The leanest and most handsome HTTP client in the Nodelands.",

@@ -43,3 +43,2 @@ "keywords": [

"dependencies": {
"debug": "^3.2.6",
"iconv-lite": "^0.6.3",

@@ -46,0 +45,0 @@ "sax": "^1.2.4"

@@ -61,2 +61,3 @@ Needle

- Streaming non-UTF-8 charset decoding, via `iconv-lite`
- Aborting any or all Needle requests using `AbortSignal` objects

@@ -321,2 +322,3 @@ And yes, Mr. Wayne, it does come in black.

- `uri_modifier`: Anonymous function taking request (or redirect location if following redirects) URI as an argument and modifying it given logic. It has to return a valid URI string for successful request.
- `signal` : An `AbortSignal` object that can be used to abort any or all Needle requests.

@@ -614,7 +616,15 @@ Response options

Then you should be able to run `npm test` once you have the dependencies in place.
To run the tests with debug logs, set the environment variable `NODE_DEBUG` to `needle` (for example, by running `NODE_DEBUG=needle npm test`).
> Note: Tests currently only work on linux-based environments that have `/proc/self/fd`. They *do not* work on MacOS environments.
> You can use Docker to run tests by creating a container and mounting the needle project directory on `/app`
> `docker create --name Needle -v /app -w /app -v /app/node_modules -i node:argon`
docker create --name Needle -v $(pwd) -w /app -v $(pwd)/node_modules -i node:argon
Or alternatively:
docker run -it -w /app --name Needle \
--mount type=bind,source="$(pwd)",target=/app \
node:fermium bash
Credits

@@ -621,0 +631,0 @@ -------

@@ -14,32 +14,27 @@ var should = require('should'),

function staticServerFor(file, content_type) {
return http.createServer(function(req, res) {
req.on('data', function(chunk) {})
req.on('end', function() {
// We used to pull from a particular site that is no longer up.
// This is a local mirror pulled from archive.org
// https://web.archive.org/web/20181003202907/http://www.nina.jp/server/slackware/webapp/tomcat_charset.html
fs.readFile(file, function(err, data) {
if (err) {
res.writeHead(404);
res.end(JSON.stringify(err));
return;
}
res.writeHeader(200, { 'Content-Type': content_type })
res.end(data);
});
})
})
}
describe('Given content-type: "text/html; charset=EUC-JP"', function() {
var server, port = 2233;
var port = 2233;
var server;
function createServer() {
return http.createServer(function(req, res) {
req.on('data', function(chunk) {})
req.on('end', function() {
// We used to pull from a particular site that is no longer up.
// This is a local mirror pulled from archive.org
// https://web.archive.org/web/20181003202907/http://www.nina.jp/server/slackware/webapp/tomcat_charset.html
fs.readFile('test/tomcat_charset.html', function(err, data) {
if (err) {
res.writeHead(404);
res.end(JSON.stringify(err));
return;
}
res.writeHeader(200, { 'Content-Type': 'text/html; charset=EUC-JP' })
res.end(data);
});
})
})
}
before(function(done) {
server = createServer();
server = staticServerFor('test/files/tomcat_charset.html', 'text/html; charset=EUC-JP')
server.listen(port, done)

@@ -54,5 +49,3 @@ url = 'http://localhost:' + port;

describe('with decode = false', function() {
it('does not decode', function(done) {
needle.get(url, { decode: false }, function(err, resp) {

@@ -64,11 +57,7 @@ resp.body.should.be.a.String;

})
})
})
describe('with decode = true', function() {
it('decodes', function(done) {
needle.get(url, { decode: true }, function(err, resp) {

@@ -80,7 +69,4 @@ resp.body.should.be.a.String;

})
})
})
})

@@ -126,2 +112,39 @@

describe('Given content-type: text/html; charset=maccentraleurope', function() {
var server, port = 2233;
// from 'https://wayback.archive-it.org/3259/20160921140616/https://www.arc.gov/research/MapsofAppalachia.asp?MAP_ID=11';
before(function(done) {
server = staticServerFor('test/files/Appalachia.html', 'text/html; charset=maccentraleurope')
server.listen(port, done)
url = 'http://localhost:' + port;
})
after(function(done) {
server.close(done)
})
describe('with decode = false', function() {
it('does not decode', function(done) {
needle.get(url, { decode: false }, function(err, resp) {
resp.body.should.be.a.String;
chardet.detect(resp.body).encoding.should.eql('ascii');
done();
})
})
})
describe('with decode = true', function() {
it('does not explode', function(done) {
(function() {
needle.get(url, { decode: true }, function(err, resp) {
resp.body.should.be.a.String;
chardet.detect(resp.body).encoding.should.eql('ascii');
done();
})
}).should.not.throw();
})
})
})
describe('Given content-type: "text/html"', function () {

@@ -158,3 +181,2 @@

})
})

@@ -161,0 +183,0 @@

@@ -272,2 +272,136 @@ var needle = require('../'),

var node_major_ver = process.version.split('.')[0].replace('v', '');
if (node_major_ver >= 16) {
describe('when request is aborted by signal', function() {
var server,
url = 'http://localhost:3333/foo';
before(function() {
server = helpers.server({ port: 3333, wait: 600 });
})
after(function() {
server.close();
})
afterEach(function() {
// reset signal to default
needle.defaults({signal: null});
})
it('works if passing an already aborted signal aborts the request', function(done) {
var abortedSignal = AbortSignal.abort();
var start = new Date();
abortedSignal.aborted.should.equal(true);
needle.get(url, { signal: abortedSignal, response_timeout: 10000 }, function(err, res) {
var timediff = (new Date() - start);
should.not.exist(res);
err.code.should.equal('ABORT_ERR');
timediff.should.be.within(0, 50);
done();
});
})
it('works if request aborts before timing out', function(done) {
var cancel = new AbortController();
var start = new Date();
needle.get(url, { signal: cancel.signal, response_timeout: 500, open_timeout: 500, read_timeout: 500 }, function(err, res) {
var timediff = (new Date() - start);
should.not.exist(res);
if (node_major_ver <= 16)
err.code.should.equal('ECONNRESET');
if (node_major_ver > 16)
err.code.should.equal('ABORT_ERR');
cancel.signal.aborted.should.equal(true);
timediff.should.be.within(200, 250);
done();
});
function abort() {
cancel.abort();
}
setTimeout(abort, 200);
})
it('works if request times out before being aborted', function(done) {
var cancel = new AbortController();
var start = new Date();
needle.get(url, { signal: cancel.signal, response_timeout: 200, open_timeout: 200, read_timeout: 200 }, function(err, res) {
var timediff = (new Date() - start);
should.not.exist(res);
err.code.should.equal('ECONNRESET');
timediff.should.be.within(200, 250);
});
function abort() {
cancel.signal.aborted.should.equal(false);
done();
}
setTimeout(abort, 500);
})
it('works if setting default signal aborts all requests', function(done) {
var cancel = new AbortController();
needle.defaults({signal: cancel.signal});
var start = new Date();
var count = 0;
function cb(err, res) {
var timediff = (new Date() - start);
should.not.exist(res);
if (node_major_ver <= 16)
err.code.should.equal('ECONNRESET');
if (node_major_ver > 16)
err.code.should.equal('ABORT_ERR');
cancel.signal.aborted.should.equal(true);
timediff.should.be.within(200, 250);
if ( count++ === 2 ) done();
}
needle.get(url, { timeout: 300 }, cb);
needle.get(url, { timeout: 350 }, cb);
needle.get(url, { timeout: 400 }, cb);
function abort() {
cancel.abort();
}
setTimeout(abort, 200);
})
it('does not work if invalid signal passed', function(done) {
try {
needle.get(url, { signal: 'invalid signal' }, function(err, res) {
done(new Error('A bad option error expected to be thrown'));
});
} catch(e) {
e.should.be.a.TypeError;
done();
}
})
it('does not work if invalid signal set by default', function(done) {
try {
needle.defaults({signal: new Error(), timeout: 1200});
done(new Error('A bad option error expected to be thrown'));
} catch(e) {
e.should.be.a.TypeError;
done();
}
})
})
}
})

@@ -547,2 +547,43 @@ var should = require('should'),

describe('when response is a HAL JSON content-type', function () {
var json_string = '{"name": "Tomás", "_links": {"href": "https://github.com/tomas/needle.git"}}';
before(function(done){
server = http.createServer(function(req, res) {
res.setHeader('Content-Type', 'application/hal+json');
res.end(json_string);
}).listen(port, done);
});
after(function(done){
server.close(done);
});
describe('and parse option is not passed', function() {
describe('with default parse_response', function() {
before(function() {
needle.defaults().parse_response.should.eql('all')
})
it('should return object', function(done){
needle.get('localhost:' + port, function(err, response, body){
should.ifError(err);
body.should.deepEqual({
'name': 'Tomás',
'_links': {
'href': 'https://github.com/tomas/needle.git'
}});
done();
});
});
});
})
});
})

@@ -231,2 +231,39 @@ var helpers = require('./helpers'),

describe('when environment variable is set', function() {
describe('and default is unchanged', function() {
before(function() {
process.env.HTTP_PROXY = 'foobar';
})
after(function() {
delete process.env.HTTP_PROXY;
})
it('tries to proxy', function(done) {
send_request({}, proxied('foobar', 80, done))
})
})
describe('and functionality is disabled', function() {
before(function() {
process.env.HTTP_PROXY = 'foobar';
})
after(function() {
delete process.env.HTTP_PROXY;
})
it('ignores proxy', function(done) {
send_request({
use_proxy_from_env_var: false
}, not_proxied(done))
})
})
})
})
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