mirror of
https://github.com/musix-org/musix-oss
synced 2025-06-17 04:26:00 +00:00
opus
This commit is contained in:
196
node_modules/needle/test/basic_auth_spec.js
generated
vendored
Normal file
196
node_modules/needle/test/basic_auth_spec.js
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
var helpers = require('./helpers'),
|
||||
should = require('should'),
|
||||
needle = require('./../'),
|
||||
server;
|
||||
|
||||
var port = 7707;
|
||||
|
||||
describe('Basic Auth', function() {
|
||||
|
||||
before(function(done) {
|
||||
server = helpers.server({ port: port }, done);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
///////////////// helpers
|
||||
|
||||
var get_auth = function(header) {
|
||||
var token = header.split(/\s+/).pop();
|
||||
return token && Buffer.from(token, 'base64').toString().split(':');
|
||||
}
|
||||
|
||||
describe('when neither username or password are passed', function() {
|
||||
|
||||
it('doesnt send any Authorization headers', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.not.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username is an empty string, and password is a valid string', function() {
|
||||
|
||||
var opts = { username: '', password: 'foobar', parse: true };
|
||||
|
||||
it('doesnt send any Authorization headers', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.not.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('when username is a valid string, but no username is passed', function() {
|
||||
|
||||
var opts = { username: 'foobar', parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes username, without colon', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth.should.have.lengthOf(1);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username is a valid string, and password is null', function() {
|
||||
|
||||
var opts = { username: 'foobar', password: null, parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both username and password', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username is a valid string, and password is an empty string', function() {
|
||||
|
||||
var opts = { username: 'foobar', password: '', parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both username and password', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('');
|
||||
auth.should.have.lengthOf(2);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username AND password are non empty strings', function() {
|
||||
|
||||
var opts = { username: 'foobar', password: 'jakub', parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both user and password', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('jakub');
|
||||
auth.should.have.lengthOf(2);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('URL with @ but not username/pass', function() {
|
||||
it('doesnt send Authorization header', function(done) {
|
||||
var url = 'localhost:' + port + '/abc/@def/xyz.zip';
|
||||
|
||||
needle.get(url, {}, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.not.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('sends user:pass headers if passed via options', function(done) {
|
||||
var url = 'localhost:' + port + '/abc/@def/xyz.zip';
|
||||
|
||||
needle.get(url, { username: 'foo' }, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
sent_headers['authorization'].should.eql('Basic Zm9v')
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when username/password are included in URL', function() {
|
||||
var opts = { parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('foobar:jakub@localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both user and password', function(done) {
|
||||
needle.get('foobar:jakub@localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('jakub');
|
||||
auth.should.have.lengthOf(2);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
112
node_modules/needle/test/compression_spec.js
generated
vendored
Normal file
112
node_modules/needle/test/compression_spec.js
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
zlib = require('zlib'),
|
||||
stream = require('stream'),
|
||||
port = 11123,
|
||||
server;
|
||||
|
||||
describe('compression', function(){
|
||||
|
||||
require.bind(null, 'zlib').should.not.throw()
|
||||
|
||||
var jsonData = '{"foo":"bar"}';
|
||||
|
||||
describe('when server supports compression', function(){
|
||||
|
||||
before(function(){
|
||||
server = http.createServer(function(req, res) {
|
||||
var raw = new stream.PassThrough();
|
||||
|
||||
var acceptEncoding = req.headers['accept-encoding'];
|
||||
if (!acceptEncoding) {
|
||||
acceptEncoding = '';
|
||||
}
|
||||
|
||||
if (acceptEncoding.match(/\bdeflate\b/)) {
|
||||
res.setHeader('Content-Encoding', 'deflate');
|
||||
raw.pipe(zlib.createDeflate()).pipe(res);
|
||||
} else if (acceptEncoding.match(/\bgzip\b/)) {
|
||||
res.setHeader('Content-Encoding', 'gzip');
|
||||
raw.pipe(zlib.createGzip()).pipe(res);
|
||||
} else if (acceptEncoding.match(/\bbr\b/)) {
|
||||
res.setHeader('Content-Encoding', 'br');
|
||||
raw.pipe(zlib.createBrotliCompress()).pipe(res);
|
||||
} else {
|
||||
raw.pipe(res);
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
if (req.headers['with-bad']) {
|
||||
res.end('foo'); // end, no deflate data
|
||||
} else {
|
||||
raw.end(jsonData)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
server.listen(port);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and client requests no compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
needle.get('localhost:' + port, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('and client requests gzip compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'gzip'}}, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.not.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('and client requests deflate compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate'}}, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.not.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should rethrow errors from decompressors', function(done){
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate', 'With-Bad': 'true'}}, function(err, response, body) {
|
||||
should.exist(err);
|
||||
err.message.should.equal("incorrect header check");
|
||||
err.code.should.equal("Z_DATA_ERROR")
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('and client requests brotli compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
// Skip this test if Brotli is not supported
|
||||
if (typeof zlib.BrotliDecompress !== 'function') {
|
||||
return done();
|
||||
}
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'br'}}, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.not.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
305
node_modules/needle/test/cookies_spec.js
generated
vendored
Normal file
305
node_modules/needle/test/cookies_spec.js
generated
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
var needle = require('../'),
|
||||
cookies = require('../lib/cookies'),
|
||||
sinon = require('sinon'),
|
||||
http = require('http'),
|
||||
should = require('should'),
|
||||
assert = require('assert');
|
||||
|
||||
var WEIRD_COOKIE_NAME = 'wc',
|
||||
BASE64_COOKIE_NAME = 'bc',
|
||||
FORBIDDEN_COOKIE_NAME = 'fc',
|
||||
NUMBER_COOKIE_NAME = 'nc';
|
||||
|
||||
var WEIRD_COOKIE_VALUE = '!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~',
|
||||
BASE64_COOKIE_VALUE = 'Y29va2llCg==',
|
||||
FORBIDDEN_COOKIE_VALUE = ' ;"\\,',
|
||||
NUMBER_COOKIE_VALUE = 12354342;
|
||||
|
||||
var TEST_HOST = 'localhost',
|
||||
NO_COOKIES_TEST_PORT = 11112,
|
||||
ALL_COOKIES_TEST_PORT = 11113;
|
||||
|
||||
describe('cookies', function() {
|
||||
|
||||
var setCookieHeader, headers, server, opts;
|
||||
|
||||
function decode(str) {
|
||||
return decodeURIComponent(str);
|
||||
}
|
||||
|
||||
function encode(str) {
|
||||
str = str.toString().replace(/[\x00-\x1F\x7F]/g, encodeURIComponent);
|
||||
return str.replace(/[\s\"\,;\\%]/g, encodeURIComponent);
|
||||
}
|
||||
|
||||
before(function() {
|
||||
setCookieHeader = [
|
||||
WEIRD_COOKIE_NAME + '=' + encode(WEIRD_COOKIE_VALUE) + ';',
|
||||
BASE64_COOKIE_NAME + '=' + encode(BASE64_COOKIE_VALUE) + ';',
|
||||
FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE) + ';',
|
||||
NUMBER_COOKIE_NAME + '=' + encode(NUMBER_COOKIE_VALUE) + ';'
|
||||
];
|
||||
});
|
||||
|
||||
before(function(done) {
|
||||
serverAllCookies = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Set-Cookie', setCookieHeader);
|
||||
res.end('200');
|
||||
}).listen(ALL_COOKIES_TEST_PORT, TEST_HOST, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
serverAllCookies.close(done);
|
||||
});
|
||||
|
||||
describe('with default options', function() {
|
||||
it('no cookie header is set on request', function(done) {
|
||||
needle.get(
|
||||
TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, function(err, response) {
|
||||
assert(!response.req._headers.cookie);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if response does not contain cookies', function() {
|
||||
before(function(done) {
|
||||
serverNoCookies = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('200');
|
||||
}).listen(NO_COOKIES_TEST_PORT, TEST_HOST, done);
|
||||
});
|
||||
|
||||
it('response.cookies is undefined', function(done) {
|
||||
needle.get(
|
||||
TEST_HOST + ':' + NO_COOKIES_TEST_PORT, function(error, response) {
|
||||
assert(!response.cookies);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
serverNoCookies.close(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if response contains cookies', function() {
|
||||
|
||||
it('puts them on resp.cookies', function(done) {
|
||||
needle.get(
|
||||
TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
response.should.have.property('cookies');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('parses them as a object', function(done) {
|
||||
needle.get(
|
||||
TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
response.cookies.should.be.an.instanceOf(Object)
|
||||
.and.have.property(WEIRD_COOKIE_NAME);
|
||||
response.cookies.should.have.property(BASE64_COOKIE_NAME);
|
||||
response.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
|
||||
response.cookies.should.have.property(NUMBER_COOKIE_NAME);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('must decode it', function(done) {
|
||||
needle.get(
|
||||
TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
response.cookies.wc.should.be.eql(WEIRD_COOKIE_VALUE);
|
||||
response.cookies.bc.should.be.eql(BASE64_COOKIE_VALUE);
|
||||
response.cookies.fc.should.be.eql(FORBIDDEN_COOKIE_VALUE);
|
||||
response.cookies.nc.should.be.eql(NUMBER_COOKIE_VALUE.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a cookie value is invalid', function() {
|
||||
|
||||
before(function() {
|
||||
setCookieHeader = [
|
||||
'geo_city=%D1%E0%ED%EA%F2-%CF%E5%F2%E5%F0%E1%F3%F0%E3'
|
||||
];
|
||||
})
|
||||
|
||||
it('doesnt blow up', function(done) {
|
||||
needle.get(TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
should.not.exist(error)
|
||||
var whatever = 'efbfbdefbfbdefbfbdefbfbdefbfbd2defbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbd';
|
||||
Buffer.from(response.cookies.geo_city).toString('hex').should.eql(whatever)
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and response is a redirect', function() {
|
||||
|
||||
var redirectServer, testPort = 22222;
|
||||
|
||||
var responseCookies = [
|
||||
[ // first req
|
||||
WEIRD_COOKIE_NAME + '=' + encode(WEIRD_COOKIE_VALUE) + ';',
|
||||
BASE64_COOKIE_NAME + '=' + encode(BASE64_COOKIE_VALUE) + ';',
|
||||
'FOO=123;'
|
||||
], [ // second req
|
||||
FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE) + ';',
|
||||
NUMBER_COOKIE_NAME + '=' + encode(NUMBER_COOKIE_VALUE) + ';'
|
||||
], [ // third red
|
||||
'FOO=BAR;'
|
||||
]
|
||||
]
|
||||
|
||||
before(function() {
|
||||
redirectServer = http.createServer(function(req, res) {
|
||||
var number = parseInt(req.url.replace('/', ''));
|
||||
var nextUrl = 'http://' + TEST_HOST + ':' + testPort + '/' + (number + 1);
|
||||
|
||||
if (responseCookies[number]) { // got cookies
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Set-Cookie', responseCookies[number]);
|
||||
res.setHeader('Location', nextUrl);
|
||||
} else if (number == 3) {
|
||||
res.statusCode = 302; // redirect but without cookies
|
||||
res.setHeader('Location', nextUrl);
|
||||
}
|
||||
|
||||
res.end('OK');
|
||||
}).listen(22222, TEST_HOST);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
redirectServer.close(done);
|
||||
})
|
||||
|
||||
describe('and follow_set_cookies is false', function() {
|
||||
|
||||
var opts = {
|
||||
follow_set_cookies: false,
|
||||
follow_max: 4
|
||||
};
|
||||
|
||||
it('no cookie header set on redirection request', function(done) {
|
||||
var spy = sinon.spy(cookies, 'write');
|
||||
|
||||
needle.get(TEST_HOST + ':' + testPort + '/0', opts, function(err, resp) {
|
||||
spy.callCount.should.eql(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and follow_set_cookies is true', function() {
|
||||
var opts = {
|
||||
follow_set_cookies: true,
|
||||
follow_max: 4
|
||||
};
|
||||
|
||||
it('should have all the cookies', function(done) {
|
||||
needle.get(TEST_HOST + ':' + testPort + '/0', opts, function(err, resp) {
|
||||
resp.cookies.should.have.property(WEIRD_COOKIE_NAME);
|
||||
resp.cookies.should.have.property(BASE64_COOKIE_NAME);
|
||||
resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
|
||||
resp.cookies.should.have.property(NUMBER_COOKIE_NAME);
|
||||
resp.cookies.should.have.property('FOO');
|
||||
resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with parse_cookies = false', function() {
|
||||
it('does not parse them', function(done) {
|
||||
needle.get(
|
||||
TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, { parse_cookies: false }, function(error, response) {
|
||||
assert(!response.cookies);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if request contains cookie header', function() {
|
||||
var opts = {
|
||||
cookies: {}
|
||||
};
|
||||
|
||||
before(function() {
|
||||
opts.cookies[WEIRD_COOKIE_NAME] = WEIRD_COOKIE_VALUE;
|
||||
opts.cookies[BASE64_COOKIE_NAME] = BASE64_COOKIE_VALUE;
|
||||
opts.cookies[FORBIDDEN_COOKIE_NAME] = FORBIDDEN_COOKIE_VALUE;
|
||||
opts.cookies[NUMBER_COOKIE_NAME] = NUMBER_COOKIE_VALUE;
|
||||
});
|
||||
|
||||
it('must be a valid cookie string', function(done) {
|
||||
var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/;
|
||||
|
||||
var full_header = [
|
||||
WEIRD_COOKIE_NAME + '=' + WEIRD_COOKIE_VALUE,
|
||||
BASE64_COOKIE_NAME + '=' + BASE64_COOKIE_VALUE,
|
||||
FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE),
|
||||
NUMBER_COOKIE_NAME + '=' + NUMBER_COOKIE_VALUE
|
||||
].join('; ')
|
||||
|
||||
needle.get(TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
|
||||
var cookieString = response.req._headers.cookie;
|
||||
cookieString.should.be.type('string');
|
||||
|
||||
cookieString.split(/\s*;\s*/).forEach(function(pair) {
|
||||
COOKIE_PAIR.test(pair).should.be.exactly(true);
|
||||
});
|
||||
|
||||
cookieString.should.be.exactly(full_header);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('dont have to encode allowed characters', function(done) {
|
||||
var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/,
|
||||
KEY_INDEX = 1,
|
||||
VALUE_INEX = 3;
|
||||
|
||||
needle.get(TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
|
||||
var cookieObj = {},
|
||||
cookieString = response.req._headers.cookie;
|
||||
|
||||
cookieString.split(/\s*;\s*/).forEach(function(str) {
|
||||
var pair = COOKIE_PAIR.exec(str);
|
||||
cookieObj[pair[KEY_INDEX]] = pair[VALUE_INEX];
|
||||
});
|
||||
|
||||
cookieObj[WEIRD_COOKIE_NAME].should.be.exactly(WEIRD_COOKIE_VALUE);
|
||||
cookieObj[BASE64_COOKIE_NAME].should.be.exactly(BASE64_COOKIE_VALUE);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('must encode forbidden characters', function(done) {
|
||||
var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/,
|
||||
KEY_INDEX = 1,
|
||||
VALUE_INEX = 3;
|
||||
|
||||
needle.get(TEST_HOST + ':' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
|
||||
var cookieObj = {},
|
||||
cookieString = response.req._headers.cookie;
|
||||
|
||||
cookieString.split(/\s*;\s*/).forEach(function(str) {
|
||||
var pair = COOKIE_PAIR.exec(str);
|
||||
cookieObj[pair[KEY_INDEX]] = pair[VALUE_INEX];
|
||||
});
|
||||
|
||||
cookieObj[FORBIDDEN_COOKIE_NAME].should.not.be.eql(
|
||||
FORBIDDEN_COOKIE_VALUE);
|
||||
cookieObj[FORBIDDEN_COOKIE_NAME].should.be.exactly(
|
||||
encode(FORBIDDEN_COOKIE_VALUE));
|
||||
cookieObj[FORBIDDEN_COOKIE_NAME].should.be.exactly(
|
||||
encodeURIComponent(FORBIDDEN_COOKIE_VALUE));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
86
node_modules/needle/test/decoder_spec.js
generated
vendored
Normal file
86
node_modules/needle/test/decoder_spec.js
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
Q = require('q'),
|
||||
chardet = require('jschardet');
|
||||
|
||||
describe('character encoding', function() {
|
||||
|
||||
var url;
|
||||
this.timeout(5000);
|
||||
|
||||
describe('test A', function() {
|
||||
|
||||
before(function() {
|
||||
url = 'http://www.nina.jp/server/slackware/webapp/tomcat_charset.html';
|
||||
})
|
||||
|
||||
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('windows-1252');
|
||||
resp.body.indexOf('EUCを使う').should.eql(-1);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with decode = true', function() {
|
||||
|
||||
it('decodes', function(done) {
|
||||
|
||||
needle.get(url, { decode: true }, function(err, resp) {
|
||||
resp.body.should.be.a.String;
|
||||
chardet.detect(resp.body).encoding.should.eql('ascii');
|
||||
resp.body.indexOf('EUCを使う').should.not.eql(-1);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('test B', function() {
|
||||
|
||||
it('encodes to UTF-8', function(done) {
|
||||
|
||||
// Our Needle wrapper that requests a chinese website.
|
||||
var task = Q.nbind(needle.get, needle, 'http://www.chinesetop100.com/');
|
||||
|
||||
// Different instantiations of this task
|
||||
var tasks = [Q.fcall(task, {decode: true}),
|
||||
Q.fcall(task, {decode: false})];
|
||||
|
||||
var results = tasks.map(function(task) {
|
||||
return task.then(function(obj) {
|
||||
return obj[0].body;
|
||||
});
|
||||
});
|
||||
|
||||
// Execute all requests concurrently
|
||||
Q.all(results).done(function(bodies) {
|
||||
|
||||
var charsets = [
|
||||
chardet.detect(bodies[0]).encoding,
|
||||
chardet.detect(bodies[1]).encoding,
|
||||
]
|
||||
|
||||
// We wanted to decode our first stream.
|
||||
charsets[0].should.equal('ascii');
|
||||
bodies[0].indexOf('全球中文网站前二十强').should.not.equal(-1);
|
||||
|
||||
// But not our second stream.
|
||||
charsets[1].should.equal('windows-1252');
|
||||
bodies[1].indexOf('全球中文网站前二十强').should.equal(-1);
|
||||
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
})
|
286
node_modules/needle/test/errors_spec.js
generated
vendored
Normal file
286
node_modules/needle/test/errors_spec.js
generated
vendored
Normal file
@ -0,0 +1,286 @@
|
||||
var needle = require('../'),
|
||||
sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
http = require('http'),
|
||||
Emitter = require('events').EventEmitter,
|
||||
helpers = require('./helpers');
|
||||
|
||||
var get_catch = function(url, opts) {
|
||||
var err;
|
||||
try {
|
||||
needle.get(url, opts);
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
describe('errors', function() {
|
||||
|
||||
after(function(done) {
|
||||
setTimeout(done, 100)
|
||||
})
|
||||
|
||||
describe('when host does not exist', function() {
|
||||
|
||||
var url = 'http://unexistinghost/foo';
|
||||
|
||||
describe('with callback', function() {
|
||||
|
||||
it('does not throw', function() {
|
||||
var ex = get_catch(url);
|
||||
should.not.exist(ex);
|
||||
})
|
||||
|
||||
it('callbacks an error', function(done) {
|
||||
needle.get(url, function(err) {
|
||||
err.should.be.a.Error;
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('error should be ENOTFOUND or EADDRINFO or EAI_AGAIN', function(done) {
|
||||
needle.get(url, function(err) {
|
||||
err.code.should.match(/ENOTFOUND|EADDRINFO|EAI_AGAIN/)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not callback a response', function(done) {
|
||||
needle.get(url, function(err, resp) {
|
||||
should.not.exist(resp);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false;
|
||||
var req = needle.get(url, function(err, resp) { })
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('without callback', function() {
|
||||
|
||||
it('does not throw', function() {
|
||||
var ex = get_catch(url);
|
||||
should.not.exist(ex);
|
||||
})
|
||||
|
||||
it('emits end event once, with error', function(done) {
|
||||
var callcount = 0,
|
||||
stream = needle.get(url);
|
||||
|
||||
stream.on('done', function(err) {
|
||||
callcount++;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
callcount.should.equal(1);
|
||||
done();
|
||||
}, 200)
|
||||
})
|
||||
|
||||
it('error should be ENOTFOUND or EADDRINFO or EAI_AGAIN', function(done) {
|
||||
var errorific,
|
||||
stream = needle.get(url);
|
||||
|
||||
stream.on('done', function(err) {
|
||||
errorific = err;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
should.exist(errorific);
|
||||
errorific.code.should.match(/ENOTFOUND|EADDRINFO|EAI_AGAIN/)
|
||||
done();
|
||||
}, 200)
|
||||
})
|
||||
|
||||
it('does not emit a readable event', function(done) {
|
||||
var called = false,
|
||||
stream = needle.get(url);
|
||||
|
||||
stream.on('readable', function() {
|
||||
called = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
called.should.be.false;
|
||||
done();
|
||||
}, 50)
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false,
|
||||
req = needle.get(url);
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when request times out waiting for response', function() {
|
||||
|
||||
var server,
|
||||
url = 'http://localhost:3333/foo';
|
||||
|
||||
var send_request = function(cb) {
|
||||
return needle.get(url, { response_timeout: 200 }, cb);
|
||||
}
|
||||
|
||||
before(function() {
|
||||
server = helpers.server({ port: 3333, wait: 1000 });
|
||||
})
|
||||
|
||||
after(function() {
|
||||
server.close();
|
||||
})
|
||||
|
||||
describe('with callback', function() {
|
||||
|
||||
it('aborts the request', function(done) {
|
||||
|
||||
var time = new Date();
|
||||
|
||||
send_request(function(err) {
|
||||
var timediff = (new Date() - time);
|
||||
timediff.should.be.within(200, 300);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it('callbacks an error', function(done) {
|
||||
send_request(function(err) {
|
||||
err.should.be.a.Error;
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('error should be ECONNRESET', function(done) {
|
||||
send_request(function(err) {
|
||||
err.code.should.equal('ECONNRESET')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not callback a response', function(done) {
|
||||
send_request(function(err, resp) {
|
||||
should.not.exist(resp);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false;
|
||||
|
||||
var req = send_request(function(err, resp) {
|
||||
should.not.exist(resp);
|
||||
})
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 350);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('without callback', function() {
|
||||
|
||||
it('emits done event once, with error', function(done) {
|
||||
var called = 0,
|
||||
stream = send_request();
|
||||
|
||||
stream.on('done', function(err) {
|
||||
called++;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
called.should.equal(1);
|
||||
done();
|
||||
}, 250)
|
||||
})
|
||||
|
||||
it('aborts the request', function(done) {
|
||||
|
||||
var time = new Date();
|
||||
var stream = send_request();
|
||||
|
||||
stream.on('done', function(err) {
|
||||
var timediff = (new Date() - time);
|
||||
timediff.should.be.within(200, 300);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it('error should be ECONNRESET', function(done) {
|
||||
var error,
|
||||
stream = send_request();
|
||||
|
||||
stream.on('done', function(err) {
|
||||
error = err;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
error.code.should.equal('ECONNRESET')
|
||||
done();
|
||||
}, 250)
|
||||
})
|
||||
|
||||
it('does not emit a readable event', function(done) {
|
||||
var called = false,
|
||||
stream = send_request();
|
||||
|
||||
stream.on('readable', function() {
|
||||
called = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
called.should.be.false;
|
||||
done();
|
||||
}, 250)
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false;
|
||||
var req = send_request();
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
198
node_modules/needle/test/headers_spec.js
generated
vendored
Normal file
198
node_modules/needle/test/headers_spec.js
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
var http = require('http'),
|
||||
helpers = require('./helpers'),
|
||||
should = require('should');
|
||||
|
||||
var port = 54321;
|
||||
|
||||
describe('request headers', function() {
|
||||
|
||||
var needle,
|
||||
server,
|
||||
existing_sockets,
|
||||
original_defaultMaxSockets;
|
||||
|
||||
before(function(done) {
|
||||
setTimeout(function() {
|
||||
existing_sockets = get_active_sockets().length;
|
||||
server = helpers.server({ port: port }, done);
|
||||
}, 100);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done)
|
||||
})
|
||||
|
||||
function send_request(opts, cb) {
|
||||
needle.get('http://localhost:' + port, opts, cb);
|
||||
}
|
||||
|
||||
function get_active_sockets() {
|
||||
var handles = process._getActiveHandles();
|
||||
|
||||
// only return the ones that have a .end() function (like a socket)
|
||||
return handles.filter(function(el) {
|
||||
if (el.constructor.name.toString() == 'Socket') {
|
||||
return el.destroyed !== true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('old node versions (<0.11.4) with persistent keep-alive connections', function() {
|
||||
|
||||
before(function() {
|
||||
delete require.cache[require.resolve('..')] // in case it was already loaded
|
||||
original_defaultMaxSockets = http.Agent.defaultMaxSockets;
|
||||
http.Agent.defaultMaxSockets = 5;
|
||||
needle = require('..');
|
||||
})
|
||||
|
||||
after(function() {
|
||||
http.Agent.defaultMaxSockets = original_defaultMaxSockets;
|
||||
delete require.cache[require.resolve('..')]
|
||||
})
|
||||
|
||||
describe('default options', function() {
|
||||
|
||||
it('sends a Connection: close header', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('close');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('no open sockets remain after request', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: close', function() {
|
||||
|
||||
it('sends a Connection: close header', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('close');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('no open sockets remain after request', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: keep-alive', function() {
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.headers.connection)', function(done) {
|
||||
send_request({ headers: { connection: 'keep-alive' }}, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.connection)', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('one open socket remain after request', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets + 1);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('new node versions with smarter connection disposing', function() {
|
||||
|
||||
before(function() {
|
||||
delete require.cache[require.resolve('..')]
|
||||
original_defaultMaxSockets = http.Agent.defaultMaxSockets;
|
||||
http.Agent.defaultMaxSockets = Infinity;
|
||||
needle = require('..');
|
||||
})
|
||||
|
||||
after(function() {
|
||||
http.Agent.defaultMaxSockets = original_defaultMaxSockets;
|
||||
delete require.cache[require.resolve('..')]
|
||||
})
|
||||
|
||||
describe('default options', function() {
|
||||
|
||||
// TODO:
|
||||
// this is weird. by default, new node versions set a 'close' header
|
||||
// while older versions set a keep-alive header
|
||||
|
||||
it.skip('sets a Connection header', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
// should.not.exist(resp.body.headers['connection']);
|
||||
// done();
|
||||
})
|
||||
})
|
||||
|
||||
it.skip('one open sockets remain after request', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
// get_active_sockets().length.should.eql(1);
|
||||
// done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: close', function() {
|
||||
|
||||
it('sends a Connection: close header', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('close');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('no open sockets remain after request', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: keep-alive', function() {
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.headers.connection)', function(done) {
|
||||
send_request({ headers: { connection: 'keep-alive' }}, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.connection)', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('one open socket remain after request', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets + 1);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
72
node_modules/needle/test/helpers.js
generated
vendored
Normal file
72
node_modules/needle/test/helpers.js
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
var fs = require('fs');
|
||||
|
||||
var protocols = {
|
||||
http : require('http'),
|
||||
https : require('https')
|
||||
}
|
||||
|
||||
var keys = {
|
||||
cert : fs.readFileSync(__dirname + '/keys/ssl.cert'),
|
||||
key : fs.readFileSync(__dirname + '/keys/ssl.key')
|
||||
}
|
||||
|
||||
var helpers = {};
|
||||
|
||||
helpers.server = function(opts, cb) {
|
||||
|
||||
var defaults = {
|
||||
code : 200,
|
||||
headers : {'Content-Type': 'application/json'}
|
||||
}
|
||||
|
||||
var mirror_response = function(req) {
|
||||
return JSON.stringify({
|
||||
headers: req.headers,
|
||||
body: req.body
|
||||
})
|
||||
}
|
||||
|
||||
var get = function(what) {
|
||||
if (!opts[what])
|
||||
return defaults[what];
|
||||
|
||||
if (typeof opts[what] == 'function')
|
||||
return opts[what](); // set them at runtime
|
||||
else
|
||||
return opts[what];
|
||||
}
|
||||
|
||||
var finish = function(req, res) {
|
||||
res.writeHead(get('code'), get('headers'));
|
||||
res.end(opts.response || mirror_response(req));
|
||||
}
|
||||
|
||||
var handler = function(req, res) {
|
||||
|
||||
req.setEncoding('utf8'); // get as string
|
||||
req.body = '';
|
||||
req.on('data', function(str) { req.body += str })
|
||||
req.socket.on('error', function(e) {
|
||||
// res.writeHead(500, {'Content-Type': 'text/plain'});
|
||||
// res.end('Error: ' + e.message);
|
||||
})
|
||||
|
||||
setTimeout(function(){
|
||||
finish(req, res);
|
||||
}, opts.wait || 0);
|
||||
|
||||
};
|
||||
|
||||
var protocol = opts.protocol || 'http';
|
||||
var server;
|
||||
|
||||
if (protocol == 'https')
|
||||
server = protocols[protocol].createServer(keys, handler);
|
||||
else
|
||||
server = protocols[protocol].createServer(handler);
|
||||
|
||||
server.listen(opts.port, cb);
|
||||
return server;
|
||||
}
|
||||
|
||||
module.exports = helpers;
|
34
node_modules/needle/test/long_string_spec.js
generated
vendored
Normal file
34
node_modules/needle/test/long_string_spec.js
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
var needle = require('../'),
|
||||
should = require('should');
|
||||
|
||||
describe('when posting a very long string', function() {
|
||||
|
||||
this.timeout(20000);
|
||||
|
||||
function get_string(length) {
|
||||
var str = '';
|
||||
for (var i = 0; i < length; i++) {
|
||||
str += 'x';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
it("shouldn't throw an EPIPE error out of nowhere", function(done) {
|
||||
var error;
|
||||
|
||||
function finished() {
|
||||
setTimeout(function() {
|
||||
should.not.exist(error);
|
||||
done();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
try {
|
||||
needle.post('https://google.com', { data: get_string(Math.pow(2, 20)) }, finished)
|
||||
} catch(e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
254
node_modules/needle/test/output_spec.js
generated
vendored
Normal file
254
node_modules/needle/test/output_spec.js
generated
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
sinon = require('sinon'),
|
||||
stream = require('stream'),
|
||||
fs = require('fs'),
|
||||
port = 11111,
|
||||
server;
|
||||
|
||||
describe('with output option', function() {
|
||||
|
||||
var server, handler, file = '/tmp/foobar.out';
|
||||
|
||||
function send_request_cb(where, cb) {
|
||||
var url = 'http://localhost:' + port + '/whatever.file';
|
||||
return needle.get(url, { output: where }, cb);
|
||||
}
|
||||
|
||||
function send_request_stream(where, cb) {
|
||||
var url = 'http://localhost:' + port + '/whatever.file';
|
||||
var stream = needle.get(url, { output: where });
|
||||
stream.on('end', cb);
|
||||
}
|
||||
|
||||
// this will only work in UNICES
|
||||
function get_open_file_descriptors() {
|
||||
var list = fs.readdirSync('/proc/self/fd');
|
||||
return list.length;
|
||||
}
|
||||
|
||||
var send_request = send_request_cb;
|
||||
|
||||
before(function(){
|
||||
server = http.createServer(function(req, res) {
|
||||
handler(req, res);
|
||||
}).listen(port);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server.close();
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
try { fs.unlinkSync(file) } catch(e) { };
|
||||
})
|
||||
|
||||
describe('and a 404 response', function() {
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(404, {'Content-Type': 'text/plain' });
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
it('doesnt attempt to write a file', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(404);
|
||||
spy.called.should.eql(false);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('doesnt actually write a file', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(404);
|
||||
fs.existsSync(file).should.eql(false);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and a 200 response', function() {
|
||||
|
||||
describe('for an empty response', function() {
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
it('uses a writableStream', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(200);
|
||||
spy.called.should.eql(true);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('writes a file', function(done) {
|
||||
fs.existsSync(file).should.eql(false);
|
||||
send_request(file, function(err, resp) {
|
||||
fs.existsSync(file).should.eql(true);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file is zero bytes in length', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
fs.statSync(file).size.should.equal(0);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
if (process.platform != 'win32') {
|
||||
it('closes the file descriptor', function(done) {
|
||||
var open_descriptors = get_open_file_descriptors();
|
||||
send_request(file + Math.random(), function(err, resp) {
|
||||
var current_descriptors = get_open_file_descriptors();
|
||||
open_descriptors.should.eql(current_descriptors);
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
describe('for a JSON response', function() {
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
||||
res.end(JSON.stringify({foo: 'bar'}));
|
||||
}
|
||||
})
|
||||
|
||||
it('uses a writableStream', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(200);
|
||||
spy.called.should.eql(true);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('writes a file', function(done) {
|
||||
fs.existsSync(file).should.eql(false);
|
||||
send_request(file, function(err, resp) {
|
||||
fs.existsSync(file).should.eql(true);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file size equals response length', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
fs.statSync(file).size.should.equal(resp.bytes);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('response pipeline is honoured (JSON is decoded by default)', function(done) {
|
||||
send_request_stream(file, function(err, resp) {
|
||||
// we need to wait a bit since writing to config.output
|
||||
// happens independently of needle's callback logic.
|
||||
setTimeout(function() {
|
||||
fs.readFileSync(file).toString().should.eql('{\"foo\":\"bar\"}');
|
||||
done();
|
||||
}, 20);
|
||||
})
|
||||
})
|
||||
|
||||
it('closes the file descriptor', function(done) {
|
||||
var open_descriptors = get_open_file_descriptors();
|
||||
send_request(file + Math.random(), function(err, resp) {
|
||||
var current_descriptors = get_open_file_descriptors();
|
||||
open_descriptors.should.eql(current_descriptors);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('for a binary file', function() {
|
||||
|
||||
var pixel = Buffer.from("base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs", "base64");
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Transfer-Encoding': 'chunked' });
|
||||
res.write(pixel.slice(0, 10));
|
||||
res.write(pixel.slice(10, 20));
|
||||
res.write(pixel.slice(20, 30));
|
||||
res.write(pixel.slice(30));
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
it('uses a writableStream', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(200);
|
||||
spy.called.should.eql(true);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('writes a file', function(done) {
|
||||
fs.existsSync(file).should.eql(false);
|
||||
send_request(file, function(err, resp) {
|
||||
fs.existsSync(file).should.eql(true);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file size equals response length', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
fs.statSync(file).size.should.equal(resp.bytes);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file is equal to original buffer', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
// we need to wait a bit since writing to config.output
|
||||
// happens independently of needle's callback logic.
|
||||
setTimeout(function() {
|
||||
fs.readFileSync(file).should.eql(pixel);
|
||||
done();
|
||||
}, 20);
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the data in resp.body too', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
resp.body.should.eql(pixel);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
if (process.platform != 'win32') {
|
||||
it('closes the file descriptor', function(done) {
|
||||
var open_descriptors = get_open_file_descriptors();
|
||||
send_request(file + Math.random(), function(err, resp) {
|
||||
var current_descriptors = get_open_file_descriptors();
|
||||
open_descriptors.should.eql(current_descriptors);
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
494
node_modules/needle/test/parsing_spec.js
generated
vendored
Normal file
494
node_modules/needle/test/parsing_spec.js
generated
vendored
Normal file
@ -0,0 +1,494 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
port = 11111,
|
||||
server;
|
||||
|
||||
describe('parsing', function(){
|
||||
|
||||
describe('when response is an JSON string', function(){
|
||||
|
||||
var json_string = '{"foo":"bar"}';
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/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.have.property('foo', 'bar');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and default parse_response is set to false', function() {
|
||||
|
||||
it('does NOT return object when disabled using .defaults', function(done){
|
||||
needle.defaults({ parse_response: false })
|
||||
|
||||
needle.get('localhost:' + port, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('{"foo":"bar"}');
|
||||
|
||||
needle.defaults({ parse_response: 'all' });
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is true', function() {
|
||||
|
||||
describe('and JSON is valid', function() {
|
||||
|
||||
it('should return object', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.should.have.property('foo', 'bar')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should have a .parser = json property', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.parser.should.eql('json');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('and response is empty', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('should return an empty string', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.body.should.equal('');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and JSON is invalid', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "this is not going to work";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('does not throw', function(done) {
|
||||
(function(){
|
||||
needle.get('localhost:' + port, { parse: true }, done);
|
||||
}).should.not.throw();
|
||||
});
|
||||
|
||||
it('does NOT return object', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.a.String;
|
||||
body.toString().should.eql('this is not going to work');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is false', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: false }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('{"foo":"bar"}');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should NOT have a .parser = json property', function(done) {
|
||||
needle.get('localhost:' + port, { parse: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is "xml"', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: 'xml' }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('{"foo":"bar"}');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should NOT have a .parser = json property', function(done) {
|
||||
needle.get('localhost:' + port, { parse: 'xml' }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('when response is JSON \'false\'', function(){
|
||||
|
||||
var json_string = 'false';
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(json_string);
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse option is not passed', function() {
|
||||
|
||||
it('should return object', function(done){
|
||||
needle.get('localhost:' + port, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.equal(false);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is true', function() {
|
||||
|
||||
describe('and JSON is valid', function() {
|
||||
|
||||
it('should return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.should.equal(false)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('and response is empty', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('should return an empty string', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.body.should.equal('');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and JSON is invalid', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "this is not going to work";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('does not throw', function(done) {
|
||||
(function(){
|
||||
needle.get('localhost:' + port, { parse: true }, done);
|
||||
}).should.not.throw();
|
||||
});
|
||||
|
||||
it('does NOT return object', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.a.String;
|
||||
body.toString().should.eql('this is not going to work');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is false', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: false }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('false');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is "xml"', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: 'xml' }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('false');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('when response is an invalid XML string', function(){
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHeader(200, {'Content-Type': 'application/xml'})
|
||||
res.end("<post><body there11post>")
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse_response is true', function(){
|
||||
|
||||
it('should return original string', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.eql('<post><body there11post>')
|
||||
should.not.exist(body.name);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser = xml property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse response is false', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.toString().should.eql('<post><body there11post>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when response is a valid XML string', function(){
|
||||
|
||||
before(function(done) {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHeader(200, {'Content-Type': 'application/xml'})
|
||||
res.end("<post><p>hello</p><p>world</p></post>")
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse_response is true', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.name.should.eql('post')
|
||||
body.children[0].name.should.eql('p')
|
||||
body.children[0].value.should.eql('hello')
|
||||
|
||||
body.children[1].name.should.eql('p')
|
||||
body.children[1].value.should.eql('world')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should have a .parser = xml property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.parser.should.eql('xml');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse response is false', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.toString().should.eql('<post><p>hello</p><p>world</p></post>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
describe('valid XML, using xml2js', function() {
|
||||
|
||||
var parsers, origParser;
|
||||
|
||||
before(function(done) {
|
||||
var xml2js = require('xml2js')
|
||||
parsers = require('../lib/parsers');
|
||||
origParser = parsers['application/xml'];
|
||||
|
||||
var customParser = require('xml2js').parseString;
|
||||
parsers.use('xml2js', ['application/xml'], function(buff, cb) {
|
||||
var opts = { explicitRoot: true, explicitArray: false };
|
||||
customParser(buff, opts, cb);
|
||||
})
|
||||
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHeader(200, {'Content-Type': 'application/xml'})
|
||||
res.end("<post><p>hello</p><p>world</p></post>")
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
parsers['application/xml'] = origParser;
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse_response is true', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.eql({ post: { p: ['hello', 'world' ]}})
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should have a .parser = xml property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.parser.should.eql('xml2js');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse response is false', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.toString().should.eql('<post><p>hello</p><p>world</p></post>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
1021
node_modules/needle/test/post_data_spec.js
generated
vendored
Normal file
1021
node_modules/needle/test/post_data_spec.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
202
node_modules/needle/test/proxy_spec.js
generated
vendored
Normal file
202
node_modules/needle/test/proxy_spec.js
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
var helpers = require('./helpers'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
http = require('http'),
|
||||
needle = require('./../');
|
||||
|
||||
var port = 7707;
|
||||
var url = 'localhost:' + port;
|
||||
var nonexisting_host = 'awepfokawepofawe.com';
|
||||
|
||||
describe('proxy option', function() {
|
||||
|
||||
var spy, opts;
|
||||
|
||||
function send_request(opts, done) {
|
||||
if (spy) spy.restore();
|
||||
spy = sinon.spy(http, 'request');
|
||||
needle.get(url, opts, done);
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// proxy opts helpers
|
||||
|
||||
function not_proxied(done) {
|
||||
return function(err, resp) {
|
||||
var path = spy.args[0][0].path;
|
||||
path.should.eql('/'); // not the full original URI
|
||||
spy.restore();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function proxied(host, port, done) {
|
||||
return function(err, resp) {
|
||||
var path = spy.args[0][0].path;
|
||||
path.should.eql('http://' + url); // the full original URI
|
||||
|
||||
var http_host = spy.args[0][0].host;
|
||||
if (http_host) http_host.should.eql(host);
|
||||
|
||||
var http_port = spy.args[0][0].port;
|
||||
if (http_port) http_port.should.eql(port);
|
||||
|
||||
spy.restore();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// auth helpers
|
||||
|
||||
function get_auth(header) {
|
||||
var token = header.split(/\s+/).pop();
|
||||
return token && Buffer.from(token, 'base64').toString().split(':');
|
||||
}
|
||||
|
||||
function no_proxy_auth(done) {
|
||||
return function(err, resp) {
|
||||
var headers = spy.args[0][0].headers;
|
||||
Object.keys(headers).should.not.containEql('proxy-authorization');
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function header_set(name, user, pass, done) {
|
||||
return function(err, resp) {
|
||||
var headers = spy.args[0][0].headers;
|
||||
var auth = get_auth(headers[name]);
|
||||
auth[0].should.eql(user);
|
||||
auth[1].should.eql(pass);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function proxy_auth_set(user, pass, done) {
|
||||
return header_set('proxy-authorization', user, pass, done);
|
||||
}
|
||||
|
||||
function basic_auth_set(user, pass, done) {
|
||||
return header_set('authorization', user, pass, done);
|
||||
}
|
||||
|
||||
after(function() {
|
||||
spy.restore();
|
||||
})
|
||||
|
||||
describe('when null proxy is passed', function() {
|
||||
|
||||
it('does not proxy', function(done) {
|
||||
send_request({ proxy: null }, not_proxied(done))
|
||||
})
|
||||
|
||||
describe('but defaults has been set', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults({ proxy: 'foobar' });
|
||||
})
|
||||
|
||||
after(function() {
|
||||
needle.defaults({ proxy: null });
|
||||
})
|
||||
|
||||
it('tries to proxy anyway', function(done) {
|
||||
send_request({}, proxied('foobar', 80, done))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when weird string is passed', function() {
|
||||
|
||||
it('tries to proxy anyway', function(done) {
|
||||
send_request({ proxy: 'alfalfa' }, proxied('alfalfa', 80, done))
|
||||
})
|
||||
})
|
||||
|
||||
describe('when valid url is passed', function() {
|
||||
|
||||
it('proxies request', function(done) {
|
||||
send_request({ proxy: nonexisting_host + ':123/done' }, proxied(nonexisting_host, '123', done))
|
||||
})
|
||||
|
||||
it('does not set a Proxy-Authorization header', function(done) {
|
||||
send_request({ proxy: nonexisting_host + ':123/done' }, no_proxy_auth(done));
|
||||
})
|
||||
|
||||
describe('and proxy url contains user:pass', function() {
|
||||
|
||||
before(function() {
|
||||
opts = {
|
||||
proxy: 'http://mj:x@' + nonexisting_host + ':123/done'
|
||||
}
|
||||
})
|
||||
|
||||
it('proxies request', function(done) {
|
||||
send_request(opts, proxied(nonexisting_host, '123', done))
|
||||
})
|
||||
|
||||
it('sets Proxy-Authorization header', function(done) {
|
||||
send_request(opts, proxy_auth_set('mj', 'x', done));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and a proxy_user is passed', function() {
|
||||
|
||||
before(function() {
|
||||
opts = {
|
||||
proxy: nonexisting_host + ':123',
|
||||
proxy_user: 'someone',
|
||||
proxy_pass: 'else'
|
||||
}
|
||||
})
|
||||
|
||||
it('proxies request', function(done) {
|
||||
send_request(opts, proxied(nonexisting_host, '123', done))
|
||||
})
|
||||
|
||||
it('sets Proxy-Authorization header', function(done) {
|
||||
send_request(opts, proxy_auth_set('someone', 'else', done));
|
||||
})
|
||||
|
||||
describe('and url also contains user:pass', function() {
|
||||
|
||||
it('url user:pass wins', function(done) {
|
||||
var opts = {
|
||||
proxy: 'http://xxx:yyy@' + nonexisting_host + ':123',
|
||||
proxy_user: 'someone',
|
||||
proxy_pass: 'else'
|
||||
}
|
||||
|
||||
send_request(opts, proxy_auth_set('xxx', 'yyy', done));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and options.username is also present', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { proxy_user: 'foobar', username: 'someone' };
|
||||
})
|
||||
|
||||
it('a separate Authorization header is set', function(done) {
|
||||
var opts = {
|
||||
proxy: nonexisting_host + ':123',
|
||||
proxy_user: 'someone',
|
||||
proxy_pass: 'else',
|
||||
username: 'test',
|
||||
password: 'X'
|
||||
}
|
||||
|
||||
send_request(opts, basic_auth_set('test', 'X', done));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
128
node_modules/needle/test/querystring_spec.js
generated
vendored
Normal file
128
node_modules/needle/test/querystring_spec.js
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
var should = require('should'),
|
||||
stringify = require('../lib/querystring').build;
|
||||
|
||||
describe('stringify', function() {
|
||||
|
||||
describe('with null', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify(null);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with a number', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify(100);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with a string', function() {
|
||||
|
||||
describe('that is empty', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify('');
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('that doesnt contain an equal sign', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify('boomshagalaga');
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('that contains an equal sign', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify('hello=123');
|
||||
res.should.eql('hello=123');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with an array', function() {
|
||||
|
||||
describe('with key val objects', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify([ {foo: 'bar'} ]);
|
||||
res.should.eql('foo=bar');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('where all elements are strings with an equal sign', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify([ 'bar=123', 'quux=' ]);
|
||||
res.should.eql('bar=123&quux=');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with random words', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify(['hello', 'there']);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with integers', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify([123, 432]);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with an object', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify({ test: 100 });
|
||||
res.should.eql('test=100');
|
||||
})
|
||||
|
||||
describe('with object where val is an array', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify({ foo: ['bar', 'baz'] });
|
||||
res.should.eql('foo[]=bar&foo[]=baz');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with object where val is an array of key val objects', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify({ foo: [{'1': 'bar'}, {'2': 'baz'}] });
|
||||
res.should.eql('foo[][1]=bar&foo[][2]=baz');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
392
node_modules/needle/test/redirect_spec.js
generated
vendored
Normal file
392
node_modules/needle/test/redirect_spec.js
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
var helpers = require('./helpers'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
needle = require('./../');
|
||||
|
||||
var ports = {
|
||||
http : 8888,
|
||||
https : 9999
|
||||
}
|
||||
|
||||
var protocols = {
|
||||
http : require('http'),
|
||||
https : require('https')
|
||||
}
|
||||
|
||||
var code = 301;
|
||||
var location; // var to set the response location
|
||||
|
||||
function response_code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
function response_headers() {
|
||||
return { 'Content-Type': 'text/plain', 'Location': location }
|
||||
}
|
||||
|
||||
describe('redirects', function() {
|
||||
|
||||
var spies = {},
|
||||
servers = {};
|
||||
|
||||
var current_protocol;
|
||||
var hostname = require('os').hostname();
|
||||
|
||||
// open two servers, one that responds to a redirect
|
||||
before(function(done) {
|
||||
|
||||
var conf = {
|
||||
port : ports.http,
|
||||
code : response_code,
|
||||
headers : response_headers
|
||||
}
|
||||
|
||||
servers.http = helpers.server(conf, function() {
|
||||
conf.port = ports.https;
|
||||
conf.protocol = 'https';
|
||||
servers.https = helpers.server(conf, done);
|
||||
});
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
servers.http.close(function() {
|
||||
servers.https.close(done);
|
||||
});
|
||||
})
|
||||
|
||||
var prots = {'http': 'https'};
|
||||
Object.keys(prots).forEach(function(protocol) {
|
||||
|
||||
current_protocol = protocol;
|
||||
var other_protocol = protocol == 'http' ? 'https' : 'http';
|
||||
|
||||
var opts, // each test will modify this
|
||||
host = '127.0.0.1',
|
||||
url = protocol + '://' + host + ':' + ports[protocol] + '/hello';
|
||||
|
||||
function send_request(opts, cb) {
|
||||
opts.rejectUnauthorized = false;
|
||||
// console.log(' -- sending request ' + url + ' -- redirect to ' + location);
|
||||
needle.post(url, { foo: 'bar' }, opts, cb);
|
||||
}
|
||||
|
||||
function not_followed(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
resp.statusCode.should.eql(301);
|
||||
if (current_protocol == 'http') {
|
||||
spies.http.callCount.should.eql(1); // only original request
|
||||
spies.https.callCount.should.eql(0);
|
||||
} else {
|
||||
spies.http.callCount.should.eql(0);
|
||||
spies.https.callCount.should.eql(1); // only original request
|
||||
}
|
||||
done();
|
||||
})
|
||||
}
|
||||
|
||||
function followed_same_protocol(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
// the original request plus the redirect one
|
||||
spies[current_protocol].callCount.should.eql(2);
|
||||
done();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function followed_other_protocol(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
// on new node versions, https.request calls http.request internally,
|
||||
// so we need to amount for that additional call.
|
||||
|
||||
var http_calls = protocols.http.Agent.defaultMaxSockets == Infinity ? 2 : 1;
|
||||
|
||||
spies.http.callCount.should.eql(http_calls); // the one(s) from http.request
|
||||
spies.https.callCount.should.eql(1); // the one from https.request (redirect)
|
||||
done();
|
||||
})
|
||||
}
|
||||
|
||||
// set a spy on [protocol].request
|
||||
// so we can see how many times a request was made
|
||||
before(function() {
|
||||
spies.http = sinon.spy(protocols.http, 'request');
|
||||
spies.https = sinon.spy(protocols.https, 'request');
|
||||
})
|
||||
|
||||
// and make sure it is restored after each test
|
||||
afterEach(function() {
|
||||
spies.http.reset();
|
||||
spies.https.reset();
|
||||
})
|
||||
|
||||
after(function() {
|
||||
spies.http.restore();
|
||||
spies.https.restore();
|
||||
})
|
||||
|
||||
describe('when overriding defaults', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults({ follow_max: 10 });
|
||||
opts = {};
|
||||
})
|
||||
|
||||
after(function() {
|
||||
// reset values to previous
|
||||
needle.defaults({ follow_max: 0 });
|
||||
})
|
||||
|
||||
describe('and redirected to the same path on same host and protocol', function() {
|
||||
before(function() {
|
||||
location = url;
|
||||
})
|
||||
it('does not follow redirect', not_followed);
|
||||
})
|
||||
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to a different path on same host, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace('/hello', '/goodbye');
|
||||
})
|
||||
it('follows redirect', followed_same_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to a different path on same host, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace('/hello', '/goodbye').replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to same path on another host, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname);
|
||||
})
|
||||
it('follows redirect', followed_same_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to same path on another host, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// false and null have the same result
|
||||
var values = [false, null];
|
||||
values.forEach(function(value) {
|
||||
|
||||
describe('when follow is ' + value, function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow: value };
|
||||
})
|
||||
|
||||
|
||||
|
||||
describe('and redirected to the same path on same host and protocol', function() {
|
||||
before(function() {
|
||||
location = url;
|
||||
})
|
||||
|
||||
it('throws an error', function() {
|
||||
(function() {
|
||||
send_request(opts, function() { });
|
||||
}).should.throw;
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when follow is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow: true };
|
||||
})
|
||||
|
||||
describe('and redirected to the same path on same host and protocol', function() {
|
||||
before(function() { location = url })
|
||||
|
||||
it('throws an error', function() {
|
||||
(function() {
|
||||
send_request(opts, function() { });
|
||||
}).should.throw;
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when follow is > 0', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults({ follow: 10 });
|
||||
})
|
||||
|
||||
after(function() {
|
||||
needle.defaults({ follow: 0 });
|
||||
})
|
||||
|
||||
describe('when keep_method is false', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_keep_method: false };
|
||||
})
|
||||
|
||||
// defaults to follow host and protocol
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
|
||||
it('sends a GET request with no data', function(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
spies.http.args[0][0].method.should.eql('GET');
|
||||
// spy.args[0][3].should.eql(null);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and set_referer is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_set_referer: true };
|
||||
})
|
||||
|
||||
// defaults to follow host and protocol
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
|
||||
it('sets Referer header when following redirect', function(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
spies.http.args[0][0].headers['referer'].should.eql("http://" + host + ":8888/hello");
|
||||
// spies.http.args[0][3].should.eql({ foo: 'bar'});
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and keep_method is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_keep_method: true };
|
||||
})
|
||||
|
||||
// defaults to follow host and protocol
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
|
||||
it('sends a POST request with the original data', function(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
spies.http.args[0][0].method.should.eql('post');
|
||||
// spies.http.args[0][3].should.eql({ foo: 'bar'});
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_host is false', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_host: false };
|
||||
})
|
||||
|
||||
// by default it will follow other protocols
|
||||
describe('and redirected to same path on another domain, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname);
|
||||
})
|
||||
it('follows redirect', followed_same_protocol);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_host is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_host: true };
|
||||
})
|
||||
|
||||
// by default it will follow other protocols
|
||||
describe('and redirected to same path on another domain, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname);
|
||||
})
|
||||
|
||||
it('does not follow redirect', not_followed);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_protocol is false', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_protocol: false };
|
||||
})
|
||||
|
||||
// by default it will follow other hosts
|
||||
describe('and redirected to same path on another domain, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_protocol is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_protocol: true };
|
||||
})
|
||||
|
||||
// by default it will follow other hosts
|
||||
describe('and redirected to same path on another domain, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('does not follow redirect', not_followed);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
});
|
45
node_modules/needle/test/redirect_with_timeout.js
generated
vendored
Normal file
45
node_modules/needle/test/redirect_with_timeout.js
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
var should = require('should')
|
||||
var needle = require('./../')
|
||||
|
||||
describe('follow redirects when read_timeout is set', function () {
|
||||
|
||||
it('clear timeout before following redirect', function (done) {
|
||||
var opts = {
|
||||
open_timeout: 1000,
|
||||
read_timeout: 3000,
|
||||
follow: 5,
|
||||
user_agent: 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
|
||||
}
|
||||
|
||||
var timedOut = 0
|
||||
var redirects = 0
|
||||
|
||||
var timer = setTimeout(function () {
|
||||
var hasRedirects = redirects > 0
|
||||
hasRedirects.should.equal(true)
|
||||
done()
|
||||
}, opts.read_timeout || 3000)
|
||||
|
||||
var resp = needle.get('http://google.com/', opts, function (err, resp, body) {
|
||||
var noErr = err === null
|
||||
var hasBody = body.length > 0
|
||||
noErr.should.equal(true);
|
||||
hasBody.should.equal(true);
|
||||
});
|
||||
|
||||
resp.on('redirect', function (location) {
|
||||
redirects++
|
||||
// console.info(' Redirected to ', location)
|
||||
})
|
||||
|
||||
resp.on('timeout', function (type) {
|
||||
timedOut++
|
||||
timedOut.should.equal(0)
|
||||
// console.error(' ', type, 'timeout')
|
||||
clearTimeout(timer)
|
||||
done()
|
||||
})
|
||||
|
||||
}).timeout(30000)
|
||||
|
||||
})
|
202
node_modules/needle/test/request_stream_spec.js
generated
vendored
Normal file
202
node_modules/needle/test/request_stream_spec.js
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
var fs = require('fs'),
|
||||
needle = require('..'),
|
||||
stream = require('stream'),
|
||||
http = require('http'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon');
|
||||
|
||||
var port = 2233;
|
||||
|
||||
describe('request stream length', function() {
|
||||
|
||||
var server, writable;
|
||||
|
||||
function createServer() {
|
||||
return http.createServer(function(req, res) {
|
||||
|
||||
req.on('data', function(chunk) {
|
||||
// console.log(chunk.length);
|
||||
})
|
||||
|
||||
req.on('end', function() {
|
||||
res.writeHeader(200, { 'Content-Type': 'application/json'})
|
||||
res.end(JSON.stringify({ headers: req.headers }))
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
before(function(done) {
|
||||
server = createServer();
|
||||
server.listen(port, done)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
writable = new stream.Readable();
|
||||
writable._read = function() {
|
||||
this.push('hello world');
|
||||
this.push(null);
|
||||
}
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done)
|
||||
})
|
||||
|
||||
function send_request(opts, cb) {
|
||||
needle.post('http://localhost:' + port, writable, opts, function(err, resp) {
|
||||
cb(err, resp)
|
||||
})
|
||||
}
|
||||
|
||||
describe('no stream_length set', function() {
|
||||
|
||||
it('doesnt set Content-Length header', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
should.not.exist(resp.body.headers['content-length']);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('doesnt work if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('stream_length set to invalid value', function() {
|
||||
|
||||
it('sets Content-Length header to that value', function(done) {
|
||||
send_request({ stream_length: 5 }, function(err, resp) {
|
||||
should.exist(err);
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('doesnt work if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 5, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('doesnt work if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 5 }, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('stream_length is set to valid value', function() {
|
||||
|
||||
it('sets Content-Length header to that value', function(done) {
|
||||
send_request({ stream_length: 11 }, function(err, resp) {
|
||||
resp.body.headers['content-length'].should.eql('11');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 11, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 11 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
describe('stream_length set to 0', function() {
|
||||
|
||||
describe('stream with path', function() {
|
||||
|
||||
var stub;
|
||||
|
||||
beforeEach(function() {
|
||||
writable.path = '/foo/bar';
|
||||
stub = sinon.stub(fs, 'stat', function(path, cb) {
|
||||
cb(null, { size: 11 })
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function() {
|
||||
stub.restore();
|
||||
})
|
||||
|
||||
it('sets Content-Length header to streams length', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
resp.body.headers['content-length'].should.eql('11');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 0, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('stream without path', function() {
|
||||
it('does not set Content-Length header', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
should.not.exist(resp.body.headers['content-length']);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('doesnt work if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 0, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
})
|
139
node_modules/needle/test/response_stream_spec.js
generated
vendored
Normal file
139
node_modules/needle/test/response_stream_spec.js
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
stream = require('stream'),
|
||||
fs = require('fs'),
|
||||
port = 11111,
|
||||
server;
|
||||
|
||||
describe('response streams', function() {
|
||||
|
||||
describe('when the server sends back json', function(){
|
||||
|
||||
before(function() {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end('{"foo":"bar"}')
|
||||
}).listen(port);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server.close();
|
||||
})
|
||||
|
||||
describe('and the client uses streams', function(){
|
||||
|
||||
it('should create a proper streams2 stream', function(done) {
|
||||
var stream = needle.get('localhost:' + port)
|
||||
|
||||
// newer node versions set this to null instead of false
|
||||
var bool = !!stream._readableState.flowing;
|
||||
should.equal(false, bool);
|
||||
|
||||
var readableCalled = false;
|
||||
stream.on('readable', function() {
|
||||
readableCalled = true;
|
||||
})
|
||||
|
||||
stream.on('done', function() {
|
||||
readableCalled.should.be.true;
|
||||
done();
|
||||
});
|
||||
|
||||
stream.resume()
|
||||
|
||||
})
|
||||
|
||||
it('emits a single data item which is our JSON object', function(done) {
|
||||
var stream = needle.get('localhost:' + port)
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
chunk.should.be.an.Object;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('done', function () {
|
||||
chunks.should.have.length(1)
|
||||
chunks[0].should.have.property('foo', 'bar');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('emits a raw buffer if we do not want to parse JSON', function(done) {
|
||||
var stream = needle.get('localhost:' + port, { parse: false })
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
Buffer.isBuffer(chunk).should.be.true;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('done', function() {
|
||||
var body = Buffer.concat(chunks).toString();
|
||||
body.should.equal('{"foo":"bar"}')
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the server sends back what was posted to it', function () {
|
||||
var file = 'asdf.txt';
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/octet')
|
||||
req.pipe(res);
|
||||
}).listen(port);
|
||||
|
||||
fs.writeFile(file, 'contents of stream', done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close();
|
||||
fs.unlink(file, done);
|
||||
})
|
||||
|
||||
it('can PUT a stream', function (done) {
|
||||
var stream = needle.put('localhost:' + port, fs.createReadStream(file), { stream: true });
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
Buffer.isBuffer(chunk).should.be.true;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('end', function () {
|
||||
var body = Buffer.concat(chunks).toString();
|
||||
body.should.equal('contents of stream')
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can PATCH a stream', function (done) {
|
||||
var stream = needle.patch('localhost:' + port, fs.createReadStream(file), { stream: true });
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
Buffer.isBuffer(chunk).should.be.true;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('end', function () {
|
||||
var body = Buffer.concat(chunks).toString();
|
||||
body.should.equal('contents of stream')
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
66
node_modules/needle/test/socket_pool_spec.js
generated
vendored
Normal file
66
node_modules/needle/test/socket_pool_spec.js
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
var needle = require('../'),
|
||||
should = require('should'),
|
||||
http = require('http');
|
||||
|
||||
var server, port = 11112;
|
||||
|
||||
describe('socket reuse', function() {
|
||||
|
||||
before(function() {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
setTimeout(function() {
|
||||
res.end('{"foo":"bar"}');
|
||||
}, 50);
|
||||
}).listen(port);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server.close();
|
||||
});
|
||||
|
||||
describe('when sockets are reused', function() {
|
||||
|
||||
var httpAgent = new http.Agent({
|
||||
keepAlive : true,
|
||||
maxSockets : 1
|
||||
});
|
||||
|
||||
it('does not duplicate listeners on .end', function(done) {
|
||||
|
||||
var last_error;
|
||||
var count = 10;
|
||||
|
||||
function completed(err) {
|
||||
--count || done(last_error);
|
||||
}
|
||||
|
||||
function send() {
|
||||
needle.get('localhost:' + port, { agent: httpAgent }, function(err, resp) {
|
||||
if (err)
|
||||
throw new Error("Unexpected error: " + err);
|
||||
|
||||
// lets go through all sockets and inspect all socket objects
|
||||
for (hostTarget in httpAgent.sockets) {
|
||||
httpAgent.sockets[hostTarget].forEach(function(socket) {
|
||||
// normally, there are 2 internal listeners and 1 needle sets up,
|
||||
// but to be sure the test does not fail even if newer node versions
|
||||
// introduce additional listeners, we use a higher limit.
|
||||
try {
|
||||
socket.listeners('end').length.should.be.below(5, "too many listeners on the socket object's end event");
|
||||
} catch (e) {
|
||||
last_error = e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
completed();
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
send();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
155
node_modules/needle/test/url_spec.js
generated
vendored
Normal file
155
node_modules/needle/test/url_spec.js
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
var needle = require('../'),
|
||||
sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
http = require('http'),
|
||||
helpers = require('./helpers');
|
||||
|
||||
var port = 3456;
|
||||
|
||||
describe('urls', function() {
|
||||
|
||||
var server, url;
|
||||
|
||||
function send_request(cb) {
|
||||
return needle.get(url, cb);
|
||||
}
|
||||
|
||||
before(function(done){
|
||||
server = helpers.server({ port: port }, done);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('null URL', function(){
|
||||
|
||||
it('throws', function(){
|
||||
(function() {
|
||||
send_request()
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('invalid protocol', function(){
|
||||
|
||||
before(function() {
|
||||
url = 'foo://google.com/what'
|
||||
})
|
||||
|
||||
it('does not throw', function(done) {
|
||||
(function() {
|
||||
send_request(function(err) {
|
||||
done();
|
||||
})
|
||||
}).should.not.throw()
|
||||
})
|
||||
|
||||
it('returns an error', function(done) {
|
||||
send_request(function(err) {
|
||||
err.should.be.an.Error;
|
||||
err.code.should.match(/ENOTFOUND|EADDRINFO|EAI_AGAIN/)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('invalid host', function(){
|
||||
|
||||
before(function() {
|
||||
url = 'http://s1\\\2.com/'
|
||||
})
|
||||
|
||||
it('fails', function(done) {
|
||||
(function() {
|
||||
send_request(function(){ })
|
||||
}.should.throw(TypeError))
|
||||
done()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
describe('invalid path', function(){
|
||||
|
||||
before(function() {
|
||||
url = 'http://www.google.com\\\/x\\\ %^&*() /x2.com/'
|
||||
})
|
||||
|
||||
it('fails', function(done) {
|
||||
send_request(function(err) {
|
||||
err.should.be.an.Error;
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
*/
|
||||
|
||||
describe('valid protocol and path', function() {
|
||||
|
||||
before(function() {
|
||||
url = 'http://localhost:' + port + '/foo';
|
||||
})
|
||||
|
||||
it('works', function(done) {
|
||||
send_request(function(err){
|
||||
should.not.exist(err);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('no protocol but with slashes and valid path', function() {
|
||||
|
||||
before(function() {
|
||||
url = '//localhost:' + port + '/foo';
|
||||
})
|
||||
|
||||
it('works', function(done) {
|
||||
send_request(function(err){
|
||||
should.not.exist(err);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('no protocol nor slashes and valid path', function() {
|
||||
|
||||
before(function() {
|
||||
url = 'localhost:' + port + '/foo';
|
||||
})
|
||||
|
||||
it('works', function(done) {
|
||||
send_request(function(err){
|
||||
should.not.exist(err);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('double encoding', function() {
|
||||
|
||||
var path = '/foo?email=' + encodeURIComponent('what-ever@Example.Com');
|
||||
|
||||
before(function() {
|
||||
url = 'localhost:' + port + path
|
||||
});
|
||||
|
||||
it('should not occur', function(done) {
|
||||
send_request(function(err, res) {
|
||||
should.not.exist(err);
|
||||
should(res.req.path).be.exactly(path);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
})
|
17
node_modules/needle/test/utils/formidable.js
generated
vendored
Normal file
17
node_modules/needle/test/utils/formidable.js
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
var formidable = require('formidable'),
|
||||
http = require('http'),
|
||||
util = require('util');
|
||||
|
||||
var port = process.argv[2] || 8888;
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
var form = new formidable.IncomingForm();
|
||||
form.parse(req, function(err, fields, files) {
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.write('received upload:\n\n');
|
||||
console.log(util.inspect({fields: fields, files: files}))
|
||||
res.end(util.inspect({fields: fields, files: files}));
|
||||
});
|
||||
}).listen(port);
|
||||
|
||||
console.log('HTTP server listening on port ' + port);
|
62
node_modules/needle/test/utils/proxy.js
generated
vendored
Normal file
62
node_modules/needle/test/utils/proxy.js
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
var http = require('http'),
|
||||
https = require('https'),
|
||||
url = require('url');
|
||||
|
||||
var port = 1234,
|
||||
log = true,
|
||||
request_auth = false;
|
||||
|
||||
http.createServer(function(request, response) {
|
||||
|
||||
console.log(request.headers);
|
||||
console.log("Got request: " + request.url);
|
||||
console.log("Forwarding request to " + request.headers['host']);
|
||||
|
||||
if (request_auth) {
|
||||
if (!request.headers['proxy-authorization']) {
|
||||
response.writeHead(407, {'Proxy-Authenticate': 'Basic realm="proxy.com"'})
|
||||
return response.end('Hello.');
|
||||
}
|
||||
}
|
||||
|
||||
var remote = url.parse(request.url);
|
||||
var protocol = remote.protocol == 'https:' ? https : http;
|
||||
|
||||
var opts = {
|
||||
host: request.headers['host'],
|
||||
port: remote.port || (remote.protocol == 'https:' ? 443 : 80),
|
||||
method: request.method,
|
||||
path: remote.pathname,
|
||||
headers: request.headers
|
||||
}
|
||||
|
||||
var proxy_request = protocol.request(opts, function(proxy_response){
|
||||
|
||||
proxy_response.on('data', function(chunk) {
|
||||
if (log) console.log(chunk.toString());
|
||||
response.write(chunk, 'binary');
|
||||
});
|
||||
proxy_response.on('end', function() {
|
||||
response.end();
|
||||
});
|
||||
|
||||
response.writeHead(proxy_response.statusCode, proxy_response.headers);
|
||||
});
|
||||
|
||||
request.on('data', function(chunk) {
|
||||
if (log) console.log(chunk.toString());
|
||||
proxy_request.write(chunk, 'binary');
|
||||
});
|
||||
|
||||
request.on('end', function() {
|
||||
proxy_request.end();
|
||||
});
|
||||
|
||||
}).listen(port);
|
||||
|
||||
process.on('uncaughtException', function(err){
|
||||
console.log('Uncaught exception!');
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
console.log("Proxy server listening on port " + port);
|
104
node_modules/needle/test/utils/test.js
generated
vendored
Normal file
104
node_modules/needle/test/utils/test.js
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
// TODO: write specs. :)
|
||||
|
||||
var fs = require('fs'),
|
||||
client = require('./../../');
|
||||
|
||||
process.env.DEBUG = true;
|
||||
|
||||
var response_callback = function(err, resp, body){
|
||||
console.log(err);
|
||||
if(resp) console.log("Got status code " + resp.statusCode)
|
||||
console.log(body);
|
||||
}
|
||||
|
||||
function simple_head(){
|
||||
client.head('http://www.amazon.com', response_callback);
|
||||
}
|
||||
|
||||
function simple_get(){
|
||||
client.get('http://www.nodejs.org', response_callback);
|
||||
}
|
||||
|
||||
function proxy_get(){
|
||||
client.get('https://www.google.com/search?q=nodejs', {proxy: 'http://localhost:1234'}, response_callback);
|
||||
}
|
||||
|
||||
function auth_get(){
|
||||
client.get('https://www.twitter.com', {username: 'asd', password: '123'}, response_callback);
|
||||
}
|
||||
|
||||
function simple_post(url){
|
||||
|
||||
var data = {
|
||||
foo: 'bar',
|
||||
baz: {
|
||||
nested: 'attribute'
|
||||
}
|
||||
}
|
||||
|
||||
client.post(url, data, response_callback);
|
||||
|
||||
}
|
||||
|
||||
function multipart_post(url){
|
||||
|
||||
var filename = 'test_file.txt';
|
||||
var data = 'Plain text data.\nLorem ipsum dolor sit amet.\nBla bla bla.\n';
|
||||
fs.writeFileSync(filename, data);
|
||||
|
||||
var black_pixel = Buffer.from("data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=".replace(/^data:image\/\w+;base64,/, ""), "base64");
|
||||
|
||||
var data = {
|
||||
foo: 'bar',
|
||||
bar: 'baz',
|
||||
nested: {
|
||||
my_document: { file: filename, content_type: 'text/plain' },
|
||||
even: {
|
||||
more: 'nesting'
|
||||
}
|
||||
},
|
||||
pixel: { filename: 'black_pixel.gif', buffer: black_pixel, content_type: 'image/gif' },
|
||||
field2: {value: JSON.stringify({"json":[ {"one":1}, {"two":2} ]}), content_type: 'application/json' }
|
||||
}
|
||||
|
||||
client.post(url, data, {multipart: true}, function(err, resp, body){
|
||||
|
||||
console.log(err);
|
||||
console.log("Got status code " + resp.statusCode)
|
||||
console.log(body);
|
||||
fs.unlink(filename);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
switch(process.argv[2]){
|
||||
case 'head':
|
||||
simple_head();
|
||||
break;
|
||||
case 'get':
|
||||
simple_get();
|
||||
break;
|
||||
case 'auth':
|
||||
auth_get();
|
||||
break;
|
||||
case 'proxy':
|
||||
proxy_get();
|
||||
break;
|
||||
case 'post':
|
||||
simple_post(process.argv[3] || 'http://posttestserver.com/post.php');
|
||||
break;
|
||||
case 'multipart':
|
||||
multipart_post(process.argv[3] || 'http://posttestserver.com/post.php?dir=example');
|
||||
break;
|
||||
case 'all':
|
||||
simple_head();
|
||||
simple_get();
|
||||
auth_get();
|
||||
proxy_get();
|
||||
simple_post(process.argv[3] || 'http://posttestserver.com/post.php');
|
||||
multipart_post(process.argv[3] || 'http://posttestserver.com/post.php?dir=example');
|
||||
break;
|
||||
default:
|
||||
console.log("Usage: ./test.js [head|get|auth|proxy|multipart]")
|
||||
}
|
Reference in New Issue
Block a user