Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug when set agent for needle & add test cases #54

Merged
merged 1 commit into from Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions http.js
Expand Up @@ -3,7 +3,7 @@

var ProbeError = require('./lib/common').ProbeError;
var needle = require('needle');
var merge = require('deepmerge');
var lodashMerge = require('lodash.merge');
var pkg = require('./package.json');
var probeStream = require('./stream');
var URL = require('url').URL;
Expand All @@ -28,7 +28,8 @@ module.exports = function probeHttp(src, options) {
var stream, len, finalUrl = src;

try {
stream = needle.get(src, merge.all([ defaults, options ]));
var needleOptions = lodashMerge({}, defaults, options);
stream = needle.get(src, needleOptions);
} catch (err) {
reject(err);
return;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -35,7 +35,7 @@
"timeout": 5000
},
"dependencies": {
"deepmerge": "^4.0.0",
"lodash.merge": "^4.6.2",
"needle": "^2.5.2",
"stream-parser": "~0.3.1"
},
Expand Down
244 changes: 244 additions & 0 deletions test/test_http.js
Expand Up @@ -232,3 +232,247 @@ describe('probeHttp', function () {
srv.close();
});
});

describe('probeHttpWithAgent', function () {
let responder, url, srv;

let httpAgent = new http.Agent({
keepAlive : true,
maxSockets : 1
});

before(function (callback) {
srv = http.createServer(function (req, res) {
responder(req, res);
}).listen(0, 'localhost', function (err) {

url = URL.format({
protocol: 'http',
hostname: srv.address().address,
port: srv.address().port,
path: '/'
});

callback(err);
});
});

it('should process an image', async function () {
responder = function (req, res) {
res.writeHead(200);
res.write(GIF1x1);

// response never ends
};

let size = await probe(url, { agent: httpAgent });

assert.strictEqual(size.width, 1);
assert.strictEqual(size.height, 1);
assert.strictEqual(size.mime, 'image/gif');
});

it('should return content-length', async function () {
responder = function (req, res) {
res.writeHead(200, { 'Content-Length': 1234 });
res.end(GIF1x1);
};

let size = await probe(url, { agent: httpAgent });

assert.strictEqual(size.width, 1);
assert.strictEqual(size.height, 1);
assert.strictEqual(size.length, 1234);
assert.strictEqual(size.mime, 'image/gif');
});

// Check that client closes the connection after all parsers fail,
//
// NOTE: the server output should be large enough so all parsers
// that buffer data will have their first buffer filled
//
it('should abort request ASAP', async function () {
responder = function (req, res) {
res.writeHead(200);
res.write('this is not an image file,');
res.write('it\'s just a bunch of text');
// response never ends
};

await assert.rejects(
async () => probe(url, { agent: httpAgent }),
/unrecognized file format/
);
});

it('should fail on 404', async function () {
responder = function (req, res) {
res.writeHead(404);
res.write('not found');
// response never ends
};

await assert.rejects(
async () => probe(url, { agent: httpAgent }),
{ statusCode: 404 }
);
});

it('should fail on status 201-299 codes', async function () {
responder = function (req, res) {
res.writeHead(201);
res.end(GIF1x1);
};

await assert.rejects(
async () => probe(url, { agent: httpAgent }),
{ statusCode: 201 }
);
});

it('should return error if url is not a string', async function () {
// for coverage
await assert.rejects(
async () => probe(123),
/URL must be a string/
);
});

it('should return error if url is invalid', async function () {
await assert.rejects(
async () => probe('badurl'),
// search error text for both `request` / `got` / Github Actions
/(ENOTFOUND badurl)|(Invalid URI)|(getaddrinfo EAI_AGAIN)/
);
});

it('should return error if connection fails', async function () {
responder = function (req, res) {
res.destroy();
};

await assert.rejects(
async () => probe(url, { retries: 0 }),
{ code: 'ECONNRESET' }
);
});

it('should add User-Agent to http requests', async function () {
let userAgent;

responder = function (req, res) {
userAgent = req.headers['user-agent'];

res.writeHead(200);
res.end(GIF1x1);
};

await probe(url, { agent: httpAgent });

assert(/^probe/.test(userAgent));
});

it('should allow customize request options', async function () {
let userAgent;

responder = function (req, res) {
userAgent = req.headers['user-agent'];

res.writeHead(200);
res.end(GIF1x1);
};

await probe(url, { headers: { 'User-Agent': 'foobar ' } });

assert(/^foo/.test(userAgent));
});

it('should return url when following redirect', async function () {
responder = function (req, res) {
if (req.url === '/redirect.gif') {
res.writeHead(302, { Location: url + '/empty.gif' });
res.end();
return;
}

res.writeHead(200);
res.end(GIF1x1);
};

let size = await probe(url + '/redirect.gif');

assert.strictEqual(size.url, url + '/empty.gif');
assert.strictEqual(size.width, 1);
assert.strictEqual(size.height, 1);
assert.strictEqual(size.mime, 'image/gif');
});

it('should follow relative redirect', async function () {
responder = function (req, res) {
if (req.url === '/path/to/step1.gif') {
res.writeHead(302, { Location: '../step2.gif' });
res.end();
return;
}

if (req.url === '/path/step2.gif') {
res.writeHead(302, { Location: 'step3.gif' });
res.end();
return;
}

res.writeHead(200);
res.end(GIF1x1);
};

let size = await probe(url + '/path/to/step1.gif');

assert.strictEqual(size.url, url + '/path/step3.gif');
assert.strictEqual(size.width, 1);
assert.strictEqual(size.height, 1);
assert.strictEqual(size.mime, 'image/gif');
});

it('should accept gzip-encoded output when not requested by client', async function () {
let encoding;

responder = function (req, res) {
encoding = req.headers['accept-encoding'] || 'identity';
res.setHeader('content-encoding', 'gzip');
res.writeHead(200);
res.end(zlib.gzipSync(GIF1x1));
};

let size = await probe(url + '/empty.gif');

// make sure client requested no compression
assert.strictEqual(encoding, 'identity');

assert.strictEqual(size.width, 1);
assert.strictEqual(size.height, 1);
assert.strictEqual(size.mime, 'image/gif');
});

it('does not duplicate listeners on .end', async function () {
await probe(url + '/path/to/step1.gif', function (err) {
if (err) throw err;

// lets go through all sockets and inspect all socket objects
for (let hostTarget in httpAgent.sockets) {
// 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.
if (hostTarget) {
httpAgent.sockets[hostTarget].forEach(function (socket) {
socket.listeners('end').length.should.be.below(5, "too many listeners on the socket object's end event");
});
}
}
});
});

after(function () {
httpAgent.destroy();
srv.close();
});
});