Skip to content

Commit

Permalink
feat: support node v8.3.0+
Browse files Browse the repository at this point in the history
  • Loading branch information
niftylettuce committed Aug 22, 2020
1 parent 9f82d25 commit c7598e2
Show file tree
Hide file tree
Showing 8 changed files with 3,353 additions and 4,185 deletions.
10 changes: 0 additions & 10 deletions .babelrc

This file was deleted.

6 changes: 0 additions & 6 deletions .eslintrc

This file was deleted.

2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
language: node_js
node_js:
- '8'
- '10'
- '12'
script:
npm run test-coverage
after_success:
Expand Down
89 changes: 89 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const Url = require('url-parse');
const deprecate = require('depd')('express');

module.exports = (options) => {
options = {
defaultPath: '/',
maxRedirects: 5,
...options
};
return (request, res, next) => {
if (!request.session)
return next(new Error('Sessions required for `express-redirect-loop`'));

const { redirect, end } = res;

res.end = function (chunk, encoding) {
// instead of `!req.xhr` we need to use !accepts HTML
// because Fetch does not provide XMLHttpRequest
if (request.accepts('html')) {
request.session.prevPrevPath = request.session.prevPath;
request.session.prevPath = request.originalUrl;
request.session.prevMethod = request.method;
// if it was a redirect then store how many times
// so that we can limit the max number of redirects
if ([301, 302].includes(res.statusCode))
request.session.maxRedirects =
typeof request.session.maxRedirects === 'number'
? request.session.maxRedirects + 1
: 1;
else request.session.maxRedirects = 0;
}

end.call(res, chunk, encoding);
};

res.redirect = function (url, ...args) {
let address = url;
let status = 302;

// allow status/url
args = [url].concat(args);
if (args.length === 2) {
if (typeof args[0] === 'number') {
status = args[0];
address = args[1];
} else {
deprecate(
'res.redirect(url, status): Use res.redirect(status, url) instead'
);
status = args[1];
}
}

address = this.location(address).get('Location');

const previousPreviousPath =
request.session.prevPrevPath || options.defaultPath;
const previousPath = request.session.prevPath || options.defaultPath;
const previousMethod = request.session.prevMethod || request.method;
const maxRedirects = request.session.maxRedirects || 1;

if (
previousPath &&
address === previousPath &&
request.method === previousMethod
) {
if (
previousPreviousPath &&
address !== previousPreviousPath &&
maxRedirects <= options.maxRedirects
) {
address = previousPreviousPath;
} else {
// if the prevPrevPath w/o querystring is !== prevPrevPath
// then redirect then to prevPrevPath w/o querystring
const { pathname } = new Url(previousPreviousPath, {});
if (pathname === previousPreviousPath) address = '/';
else address = pathname;
}
} else if (maxRedirects > options.maxRedirects) {
address = options.defaultPath;
}

redirect.call(res, status, address);
};

next();
};
};
61 changes: 27 additions & 34 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,31 @@
"url-parse": "^1.4.7"
},
"devDependencies": {
"@babel/cli": "^7.7.7",
"@babel/core": "^7.7.7",
"@babel/preset-env": "^7.7.7",
"@commitlint/cli": "^8.2.0",
"@commitlint/config-conventional": "^8.2.0",
"ava": "^2.4.0",
"cabin": "^5.0.13",
"codecov": "^3.6.1",
"cross-env": "^6.0.3",
"eslint": "6.8.0",
"eslint-config-prettier": "^6.7.0",
"@commitlint/cli": "^9.1.2",
"@commitlint/config-conventional": "^9.1.2",
"ava": "^3.11.1",
"cabin": "^8.0.7",
"codecov": "^3.7.2",
"cross-env": "^7.0.2",
"eslint": "^7.7.0",
"eslint-config-xo-lass": "^1.0.3",
"eslint-plugin-node": "^10.0.0",
"eslint-plugin-prettier": "^3.1.2",
"express": "^4.17.1",
"express-session": "^1.17.0",
"fetch-cookie": "^0.7.3",
"fixpack": "^2.3.1",
"husky": "^3.1.0",
"lint-staged": "^9.5.0",
"express-session": "^1.17.1",
"fetch-cookie": "^0.10.1",
"fixpack": "^3.0.6",
"husky": "^4.2.5",
"lint-staged": "^10.2.11",
"node-fetch": "^2.6.0",
"nyc": "^14.1.1",
"prettier": "^1.19.1",
"remark-cli": "^7.0.1",
"remark-preset-github": "^0.0.16",
"rimraf": "^3.0.0",
"xo": "^0.25.3"
"nyc": "^15.1.0",
"remark-cli": "^8.0.1",
"remark-preset-github": "^3.0.0",
"rimraf": "^3.0.2",
"xo": "^0.33.0"
},
"engines": {
"node": ">=6.4"
"node": ">=8.3.0"
},
"files": "lib",
"files": "index.js",
"homepage": "https://github.com/niftylettuce/express-redirect-loop",
"husky": {
"hooks": {
Expand Down Expand Up @@ -104,7 +97,7 @@
]
}
},
"main": "lib/index.js",
"main": "index.js",
"prettier": {
"singleQuote": true,
"bracketSpacing": true,
Expand All @@ -121,20 +114,20 @@
},
"scripts": {
"ava": "cross-env NODE_ENV=test ava",
"build": "npm run build:clean && npm run build:lib",
"build:clean": "rimraf lib",
"build:lib": "babel src --out-dir lib",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
"lint": "xo && remark . -qfo && eslint lib",
"lint": "xo && remark . -qfo",
"nyc": "cross-env NODE_ENV=test nyc ava",
"test": "npm run build && npm run lint && npm run ava",
"test-coverage": "npm run build && npm run lint && npm run nyc"
"test": "npm run lint && npm run ava",
"test-coverage": "npm run lint && npm run nyc"
},
"xo": {
"prettier": true,
"space": true,
"extends": [
"xo-lass"
]
],
"rules": {
"unicorn/prevent-abbreviations": "warn"
}
}
}
84 changes: 0 additions & 84 deletions src/index.js

This file was deleted.

48 changes: 24 additions & 24 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const redirectLoop = require('..');

const cabin = new Cabin();

test.beforeEach(t => {
test.beforeEach((t) => {
const app = express();
app.use(
session({
Expand All @@ -19,30 +19,30 @@ test.beforeEach(t => {
);
app.use(cabin.middleware);
app.use(redirectLoop());
app.get('/', (req, res) => res.sendStatus(200));
app.get('/bar', (req, res) => res.redirect('/foo'));
app.get('/foo', (req, res) => res.redirect('/foo'));
app.get('/baz', (req, res) => res.redirect('/bar'));
app.get('/beep', (req, res) => res.sendStatus(200));
app.get('/boop', (req, res) => res.redirect('/boop'));
app.get('/1', (req, res) => res.redirect('/2')); // 1
app.get('/2', (req, res) => res.redirect('/3')); // 2
app.get('/3', (req, res) => res.redirect('/4')); // 3
app.get('/4', (req, res) => res.redirect('/5')); // 4
app.get('/5', (req, res) => res.redirect('/6')); // 5
app.get('/6', (req, res) => res.redirect('/7')); // 6 <-- redirects to /
app.get('/7', (req, res) => res.redirect('/8'));
app.get('/form', (req, res) => res.sendStatus(200));
app.post('/form', (req, res) => res.redirect('/form'));
app.use((err, req, res, next) => {
app.get('/', (request, res) => res.sendStatus(200));
app.get('/bar', (request, res) => res.redirect('/foo'));
app.get('/foo', (request, res) => res.redirect('/foo'));
app.get('/baz', (request, res) => res.redirect('/bar'));
app.get('/beep', (request, res) => res.sendStatus(200));
app.get('/boop', (request, res) => res.redirect('/boop'));
app.get('/1', (request, res) => res.redirect('/2')); // 1
app.get('/2', (request, res) => res.redirect('/3')); // 2
app.get('/3', (request, res) => res.redirect('/4')); // 3
app.get('/4', (request, res) => res.redirect('/5')); // 4
app.get('/5', (request, res) => res.redirect('/6')); // 5
app.get('/6', (request, res) => res.redirect('/7')); // 6 <-- redirects to /
app.get('/7', (request, res) => res.redirect('/8'));
app.get('/form', (request, res) => res.sendStatus(200));
app.post('/form', (request, res) => res.redirect('/form'));
app.use((err, request, res, next) => {
console.log('err', err);
next(err, req, res, next);
next(err, request, res, next);
});
const server = app.listen();
t.context.url = `http://localhost:${server.address().port}/`;
});

test('caps at max of 5 redirects', async t => {
test('caps at max of 5 redirects', async (t) => {
const res = await fetch(`${t.context.url}1`, {
credentials: 'include'
});
Expand All @@ -51,7 +51,7 @@ test('caps at max of 5 redirects', async t => {
t.pass();
});

test('/beep => 200 => /boop => /beep', async t => {
test('/beep => 200 => /boop => /beep', async (t) => {
let res = await fetch(`${t.context.url}beep`, { credentials: 'include' });
t.is(res.status, 200);
t.is(res.url, `${t.context.url}beep`);
Expand All @@ -61,28 +61,28 @@ test('/beep => 200 => /boop => /beep', async t => {
t.pass();
});

test('/bar => /foo => /', async t => {
test('/bar => /foo => /', async (t) => {
const res = await fetch(`${t.context.url}bar`, { credentials: 'include' });
t.is(res.status, 200);
t.is(res.url, t.context.url);
t.pass();
});

test('/foo => /', async t => {
test('/foo => /', async (t) => {
const res = await fetch(`${t.context.url}foo`, { credentials: 'include' });
t.is(res.status, 200);
t.is(res.url, t.context.url);
t.pass();
});

test('/baz => /bar => /foo => /', async t => {
test('/baz => /bar => /foo => /', async (t) => {
const res = await fetch(`${t.context.url}baz`, { credentials: 'include' });
t.is(res.status, 200);
t.is(res.url, t.context.url);
t.pass();
});

test('prevents incorrect redirect to earlier path', async t => {
test('prevents incorrect redirect to earlier path', async (t) => {
// GET / -> GET /form -> POST /form -> GET /form
let res = await fetch(t.context.url, { credentials: 'include' });
t.is(res.status, 200);
Expand Down
Loading

0 comments on commit c7598e2

Please sign in to comment.