More granular control over query handling

This commit is contained in:
Michael Jackson 2019-08-08 10:00:34 -07:00
parent a718d90549
commit dfe2f29fe0
7 changed files with 82 additions and 54 deletions

View File

@ -1,20 +0,0 @@
import request from 'supertest';
import createServer from '../createServer';
describe('Invalid query params', () => {
let server;
beforeEach(() => {
server = createServer();
});
it('redirect to the same path w/out those params', done => {
request(server)
.get('/d3?module&invalid-param')
.end((err, res) => {
expect(res.statusCode).toBe(302);
expect(res.headers.location).toBe('/d3?module');
done();
});
});
});

View File

@ -29,4 +29,16 @@ describe('A request for metadata', () => {
});
});
});
describe('when the URL includes invalid query parameters', () => {
it('removes them from the URL', done => {
request(server)
.get('/react@16.8.0/?meta&invalid')
.end((err, res) => {
expect(res.statusCode).toBe(302);
expect(res.headers.location).toBe('/react@16.8.0/?meta');
done();
});
});
});
});

View File

@ -0,0 +1,24 @@
import request from 'supertest';
import createServer from '../createServer.js';
describe('A request for a module', () => {
let server;
beforeEach(() => {
server = createServer();
});
// TODO: More tests here
describe('when the URL includes invalid query parameters', () => {
it('removes them from the URL', done => {
request(server)
.get('/react@16.8.0/?module&invalid')
.end((err, res) => {
expect(res.statusCode).toBe(302);
expect(res.headers.location).toBe('/react@16.8.0/?module');
done();
});
});
});
});

View File

@ -35,4 +35,16 @@ describe('A request for a JavaScript file', () => {
});
});
});
describe('when the URL has invalid query params', () => {
it('removes them from the URL', done => {
request(server)
.get('/react@16.8.0/index.js?invalid')
.end((err, res) => {
expect(res.statusCode).toBe(302);
expect(res.headers.location).toBe('/react@16.8.0/index.js');
done();
});
});
});
});

View File

@ -11,6 +11,7 @@ import serveMainPage from './actions/serveMainPage.js';
import serveModule from './actions/serveModule.js';
import serveStats from './actions/serveStats.js';
import allowQuery from './middleware/allowQuery.js';
import findEntry from './middleware/findEntry.js';
import noQuery from './middleware/noQuery.js';
import redirectLegacyURLs from './middleware/redirectLegacyURLs.js';
@ -19,7 +20,6 @@ import staticFiles from './middleware/staticFiles.js';
import validateFilename from './middleware/validateFilename.js';
import validatePackageURL from './middleware/validatePackageURL.js';
import validatePackageName from './middleware/validatePackageName.js';
import validateQuery from './middleware/validateQuery.js';
import validateVersion from './middleware/validateVersion.js';
function createApp(callback) {
@ -86,9 +86,9 @@ export default function createServer() {
app.get(
'*/',
allowQuery('meta'),
validatePackageURL,
validatePackageName,
validateQuery,
validateVersion,
validateFilename,
serveDirectoryMetadata
@ -96,9 +96,9 @@ export default function createServer() {
app.get(
'*',
allowQuery('meta'),
validatePackageURL,
validatePackageName,
validateQuery,
validateVersion,
validateFilename,
serveFileMetadata
@ -113,14 +113,16 @@ export default function createServer() {
}
});
// We need to route in this weird way because Express
// doesn't have a way to route based on query params.
const moduleApp = createApp(app => {
app.enable('strict routing');
app.get(
'*',
allowQuery('module'),
validatePackageURL,
validatePackageName,
validateQuery,
validateVersion,
validateFilename,
findEntry,
@ -143,9 +145,9 @@ export default function createServer() {
app.get(
'*',
noQuery(),
validatePackageURL,
validatePackageName,
validateQuery,
validateVersion,
validateFilename,
findEntry,

View File

@ -0,0 +1,27 @@
import createSearch from '../utils/createSearch.js';
/**
* Reject URLs with invalid query parameters to increase cache hit rates.
*/
export default function allowQuery(validKeys = []) {
if (!Array.isArray(validKeys)) {
validKeys = [validKeys];
}
return (req, res, next) => {
const keys = Object.keys(req.query);
if (!keys.every(key => validKeys.includes(key))) {
const newQuery = keys
.filter(key => validKeys.includes(key))
.reduce((query, key) => {
query[key] = req.query[key];
return query;
}, {});
return res.redirect(302, req.baseUrl + req.path + createSearch(newQuery));
}
next();
};
}

View File

@ -1,29 +0,0 @@
import createSearch from '../utils/createSearch.js';
const knownQueryParams = {
main: true, // Deprecated, see #63
meta: true,
module: true
};
function isKnownQueryParam(param) {
return !!knownQueryParams[param];
}
/**
* Reject URLs with invalid query parameters to increase cache hit rates.
*/
export default function validateQuery(req, res, next) {
const keys = Object.keys(req.query);
if (!keys.every(isKnownQueryParam)) {
const newQuery = keys.filter(isKnownQueryParam).reduce((query, key) => {
query[key] = req.query[key];
return query;
}, {});
return res.redirect(302, req.path + createSearch(newQuery));
}
next();
}