UNPKG

flaks

Version:

reverse http/https proxy optimized for edge/outgoing traffic

395 lines (330 loc) 10.8 kB
const should = require ('should'); const _ = require ('lodash'); const async = require ('async'); const request = require ('supertest'); const express = require ('express'); const bodyParser = require ('body-parser'); function _get_me_an_app() { const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(bodyParser.text()); app.all ('/st/:st', (req, res) => { app.__hits++; app.__req = req; res.status (+(req.params.st)).send ('status'); }); app.all ('/hole', (req, res) => { app.__hits++; app.__req = req; setTimeout (() => res.socket.destroy (), 500); }); app.all ('/never-respond', (req, res) => { app.__hits++; app.__req = req; }); app.all ('/half-response', (req, res) => { app.__hits++; app.__req = req; res.writeHead(200, { 'Content-Length': 666, 'Content-Type': 'text/plain', connection: 'keep-alive' }); setTimeout (() => res.socket.destroy (), 500); }); app.all('*', (req, res) => { app.__hits++; app.__req = req; res.send({ q: req.query, h: req.headers, u: req.url, b: req.body }); }); app.__hits = 0; return app; } const config = { agents: {}, vhosts: { default: { http: { // wirelog: true, routes: { '/a/(.*)': { target: ['http://localhost:28090/st/$1', 'http://localhost:28090/st/510'], lb: 'seq' }, '/b/(.*)': { target: ['http://localhost:19999/st/$1', 'http://localhost:28090/st/$1', 'http://localhost:28090/st/510'], lb: 'seq' }, '/c/(.*)': { target: ['http://localhoster:28090/st/$1', 'http://localhost:28090/st/$1', 'http://localhost:28090/st/510'], lb: 'seq' }, '/d/(.*)': { target: ['http://1.1.1.1:28090/st/$1', 'http://localhost:28090/st/$1', 'http://localhost:28090/st/510'], lb: 'seq' }, '/e/(.*)': { target: ['http://localhost:28090/hole', 'http://localhost:28090/st/510'], lb: 'seq' }, '/f/(.*)': { target: ['http://localhost:28090/half-response', 'http://localhost:28090/st/510'], lb: 'seq' }, '/g/(.*)': { target: ['http://localhost:28090/never-respond', 'http://localhost:28090/st/510'], lb: 'seq' }, '/h/(.*)': { target: [ {url: 'http://localhost:28090/ein', w: 9}, {url: 'http://localhost:28090/zwei', w: 1}, {url: 'http://localhost:28090/drei', w: 90}, ], lb: 'seq' }, '/i/(.*)': { target: [ {url: 'http://localhost:28090/ein', w: 9}, {url: 'http://localhost:28090/zwei', w: 1}, {url: 'http://localhost:28090/drei', w: 90}, ], lb: 'rand' }, } }, net: { incoming_timeout: 3000, outgoing_timeout: 2000, connect_timeout: 1000 } } } }; describe('LoadBalancing', () => { let flaks; before (done => async.series ([ // cb => require ('winston-log-space').init (cb), cb => {flaks = require('../uber-app'); cb (); } ], done)); ['post', 'put', 'patch', 'delete'].forEach (verb => [200, 207, 404, 444, 500, 517].forEach (st => { // ['post'].forEach (verb => // [200].forEach (st => { it(`proxies ${verb} gets HTTP ${st} on first option, ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/a/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(st) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies ${verb} gets HTTP ${st} on second option (after connection refused), ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/b/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(st) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies ${verb} gets HTTP ${st} on second option (after resolution error), ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/c/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(st) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies ${verb} gets HTTP ${st} on second option (after connection timeout), ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/d/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(st) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies ${verb} gets HTTP 502 on first option upon socket close, ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/e/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(502) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies ${verb} gets HTTP 502 on first option upon socket close on half-received response, ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/f/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(502) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies ${verb} gets HTTP 502 on first option upon upstream response timeout, ignores the rest `, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); request(context.app) [verb](`/g/${st}`) .query({ a: 1, b: '666' }) .send('ddfgdgdgdgdf') .set({ 'x-request-id': 'qwertyuiop' }) .type('text') .expect(502) .end((err, res) => { if (err) return done(err); target.__hits.should.equal (1); target.__req.body.should.equal ('ddfgdgdgdgdf'); tserv.close(); context.shutdown(false, done); }); }); }); })); it(`proxies correctly using lb seq`, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); async.timesLimit ( 1000, 17, (n, cb) => request(context.app).get ('/h/h').end ((err, res) => { if (err) return cb (err); cb (null, res.body.u); }), (err, res) => { if (err) return done(err); const cuml = res.reduce ((acc, v) => { if (!acc[v]) acc[v] = 0; acc[v]++; return acc; }, {}); cuml.should.eql ({ '/ein': 1000 }); tserv.close(); context.shutdown(false, done); }); }); }); it(`proxies correctly using lb rand`, done => { flaks(config, (err, context) => { if (err) return done(err); let target = _get_me_an_app(); let tserv = target.listen(28090); async.timesLimit ( 10000, 17, (n, cb) => request(context.app).get ('/i/i').end ((err, res) => { if (err) return cb (err); cb (null, res.body.u); }), (err, res) => { if (err) return done(err); const cuml = res.reduce ((acc, v) => { if (!acc[v]) acc[v] = 0; acc[v]++; return acc; }, {}); _.size (cuml).should.equal (3); cuml['/ein'].should.be.approximately (900, 100); cuml['/zwei'].should.be.approximately (100, 100); cuml['/drei'].should.be.approximately (9000, 100); tserv.close(); context.shutdown(false, done); }); }); }); });