'use strict'; var url = require('url'); exports = module.exports = function historyApiFallback(options) { options = options || {}; var logger = getLogger(options); return function(req, res, next) { var headers = req.headers; if (req.method !== 'GET') { logger( 'Not rewriting', req.method, req.url, 'because the method is not GET.' ); return next(); } else if (!headers || typeof headers.accept !== 'string') { logger( 'Not rewriting', req.method, req.url, 'because the client did not send an HTTP accept header.' ); return next(); } else if (headers.accept.indexOf('application/json') === 0) { logger( 'Not rewriting', req.method, req.url, 'because the client prefers JSON.' ); return next(); } else if (!acceptsHtml(headers.accept, options)) { logger( 'Not rewriting', req.method, req.url, 'because the client does not accept HTML.' ); return next(); } var parsedUrl = url.parse(req.url); var rewriteTarget; options.rewrites = options.rewrites || []; for (var i = 0; i < options.rewrites.length; i++) { var rewrite = options.rewrites[i]; var match = parsedUrl.pathname.match(rewrite.from); if (match !== null) { rewriteTarget = evaluateRewriteRule(parsedUrl, match, rewrite.to, req); logger('Rewriting', req.method, req.url, 'to', rewriteTarget); req.url = rewriteTarget; return next(); } } var pathname = parsedUrl.pathname; if (pathname.lastIndexOf('.') > pathname.lastIndexOf('/') && options.disableDotRule !== true) { logger( 'Not rewriting', req.method, req.url, 'because the path includes a dot (.) character.' ); return next(); } rewriteTarget = options.index || '/index.html'; logger('Rewriting', req.method, req.url, 'to', rewriteTarget); req.url = rewriteTarget; next(); }; }; function evaluateRewriteRule(parsedUrl, match, rule, req) { if (typeof rule === 'string') { return rule; } else if (typeof rule !== 'function') { throw new Error('Rewrite rule can only be of type string of function.'); } return rule({ parsedUrl: parsedUrl, match: match, request: req }); } function acceptsHtml(header, options) { options.htmlAcceptHeaders = options.htmlAcceptHeaders || ['text/html', '*/*']; for (var i = 0; i < options.htmlAcceptHeaders.length; i++) { if (header.indexOf(options.htmlAcceptHeaders[i]) !== -1) { return true; } } return false; } function getLogger(options) { if (options && options.logger) { return options.logger; } else if (options && options.verbose) { return console.log.bind(console); } return function(){}; }