index.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. 'use strict';
  2. var url = require('url');
  3. exports = module.exports = function historyApiFallback(options) {
  4. options = options || {};
  5. var logger = getLogger(options);
  6. return function(req, res, next) {
  7. var headers = req.headers;
  8. if (req.method !== 'GET') {
  9. logger(
  10. 'Not rewriting',
  11. req.method,
  12. req.url,
  13. 'because the method is not GET.'
  14. );
  15. return next();
  16. } else if (!headers || typeof headers.accept !== 'string') {
  17. logger(
  18. 'Not rewriting',
  19. req.method,
  20. req.url,
  21. 'because the client did not send an HTTP accept header.'
  22. );
  23. return next();
  24. } else if (headers.accept.indexOf('application/json') === 0) {
  25. logger(
  26. 'Not rewriting',
  27. req.method,
  28. req.url,
  29. 'because the client prefers JSON.'
  30. );
  31. return next();
  32. } else if (!acceptsHtml(headers.accept, options)) {
  33. logger(
  34. 'Not rewriting',
  35. req.method,
  36. req.url,
  37. 'because the client does not accept HTML.'
  38. );
  39. return next();
  40. }
  41. var parsedUrl = url.parse(req.url);
  42. var rewriteTarget;
  43. options.rewrites = options.rewrites || [];
  44. for (var i = 0; i < options.rewrites.length; i++) {
  45. var rewrite = options.rewrites[i];
  46. var match = parsedUrl.pathname.match(rewrite.from);
  47. if (match !== null) {
  48. rewriteTarget = evaluateRewriteRule(parsedUrl, match, rewrite.to, req);
  49. if(rewriteTarget.charAt(0) !== '/') {
  50. logger(
  51. 'We recommend using an absolute path for the rewrite target.',
  52. 'Received a non-absolute rewrite target',
  53. rewriteTarget,
  54. 'for URL',
  55. req.url
  56. );
  57. }
  58. logger('Rewriting', req.method, req.url, 'to', rewriteTarget);
  59. req.url = rewriteTarget;
  60. return next();
  61. }
  62. }
  63. var pathname = parsedUrl.pathname;
  64. if (pathname.lastIndexOf('.') > pathname.lastIndexOf('/') &&
  65. options.disableDotRule !== true) {
  66. logger(
  67. 'Not rewriting',
  68. req.method,
  69. req.url,
  70. 'because the path includes a dot (.) character.'
  71. );
  72. return next();
  73. }
  74. rewriteTarget = options.index || '/index.html';
  75. logger('Rewriting', req.method, req.url, 'to', rewriteTarget);
  76. req.url = rewriteTarget;
  77. next();
  78. };
  79. };
  80. function evaluateRewriteRule(parsedUrl, match, rule, req) {
  81. if (typeof rule === 'string') {
  82. return rule;
  83. } else if (typeof rule !== 'function') {
  84. throw new Error('Rewrite rule can only be of type string or function.');
  85. }
  86. return rule({
  87. parsedUrl: parsedUrl,
  88. match: match,
  89. request: req
  90. });
  91. }
  92. function acceptsHtml(header, options) {
  93. options.htmlAcceptHeaders = options.htmlAcceptHeaders || ['text/html', '*/*'];
  94. for (var i = 0; i < options.htmlAcceptHeaders.length; i++) {
  95. if (header.indexOf(options.htmlAcceptHeaders[i]) !== -1) {
  96. return true;
  97. }
  98. }
  99. return false;
  100. }
  101. function getLogger(options) {
  102. if (options && options.logger) {
  103. return options.logger;
  104. } else if (options && options.verbose) {
  105. return console.log.bind(console);
  106. }
  107. return function(){};
  108. }