contentTypeChecker.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. var debug = require('debug')('ylt:contentTypeChecker');
  2. var Q = require('q');
  3. var isJpg = require('is-jpg');
  4. var isPng = require('is-png');
  5. var isSvg = require('is-svg');
  6. var isGif = require('is-gif');
  7. var isWebp = require('is-webp');
  8. var isWoff = require('is-woff');
  9. var isWoff2 = require('is-woff2');
  10. var isOtf = require('is-otf');
  11. var isTtf = require('is-ttf');
  12. var isEot = require('is-eot');
  13. var isJson = require('is-json');
  14. var ContentTypeChecker = function() {
  15. function checkContentType(entry) {
  16. var deferred = Q.defer();
  17. // Setting isSomething values:
  18. switch(entry.type) {
  19. case 'html':
  20. entry.isHTML = true;
  21. break;
  22. case 'xml':
  23. entry.isXML = true;
  24. break;
  25. case 'css':
  26. entry.isCSS = true;
  27. break;
  28. case 'js':
  29. entry.isJS = true;
  30. break;
  31. case 'json':
  32. entry.isJSON = true;
  33. break;
  34. case 'image':
  35. entry.isImage = true;
  36. break;
  37. case 'webfont':
  38. entry.isWebFont = true;
  39. break;
  40. case 'video':
  41. entry.isVideo = true;
  42. break;
  43. case 'favicon':
  44. entry.isFavicon = true;
  45. break;
  46. }
  47. // Now let's check for mistakes by analysing body content. It happens more often then we think!
  48. // Ignore very small files as they are generally tracking pixels
  49. if (entry.weightCheck && entry.weightCheck.bodyBuffer && entry.weightCheck.bodySize > 100) {
  50. var foundType;
  51. try {
  52. foundType = findContentType(entry.weightCheck.bodyBuffer);
  53. // If it's an image or a font, then rewrite.
  54. if (foundType !== null && (foundType.type === 'image' || foundType.type === 'webfont' || foundType.type === 'json')) {
  55. if (foundType.type !== entry.type) {
  56. debug('Content type %s is wrong for %s. It should be %s.', entry.type, entry.ulr, foundType.type);
  57. }
  58. rewriteContentType(entry, foundType);
  59. }
  60. } catch(err) {
  61. debug('Error while analyzing the contentType of %s', entry.url);
  62. debug(err);
  63. }
  64. }
  65. deferred.resolve(entry);
  66. return deferred.promise;
  67. }
  68. function findContentType(bodyBuffer) {
  69. var bodyStr = bodyBuffer.toString();
  70. if (isJpg(bodyBuffer)) {
  71. return contentTypes.jpeg;
  72. }
  73. if (isPng(bodyBuffer)) {
  74. return contentTypes.png;
  75. }
  76. // https://github.com/sindresorhus/is-svg/issues/7
  77. if (/<svg/.test(bodyStr) && isSvg(bodyStr)) {
  78. return contentTypes.svg;
  79. }
  80. if (isGif(bodyBuffer)) {
  81. return contentTypes.gif;
  82. }
  83. if (isWebp(bodyBuffer)) {
  84. return contentTypes.webp;
  85. }
  86. if (isWoff(bodyBuffer)) {
  87. return contentTypes.woff;
  88. }
  89. if (isWoff2(bodyBuffer)) {
  90. return contentTypes.woff2;
  91. }
  92. if (isOtf(bodyBuffer)) {
  93. return contentTypes.otf;
  94. }
  95. if (isTtf(bodyBuffer)) {
  96. return contentTypes.ttf;
  97. }
  98. if (isEot(bodyBuffer)) {
  99. return contentTypes.eot;
  100. }
  101. if (isJson(bodyStr)) {
  102. return contentTypes.json;
  103. }
  104. return null;
  105. }
  106. function rewriteContentType(entry, contentTypeObj) {
  107. delete(entry.isHTML);
  108. delete(entry.isXML);
  109. delete(entry.isCSS);
  110. delete(entry.isJS);
  111. delete(entry.isJSON);
  112. delete(entry.isImage);
  113. delete(entry.isSVG);
  114. delete(entry.isVideo);
  115. delete(entry.isWebFont);
  116. delete(entry.isTTF);
  117. delete(entry.isFavicon);
  118. entry.contentType = contentTypeObj.mimes[0];
  119. contentTypeObj.updateFn(entry);
  120. }
  121. var contentTypes = {
  122. jpeg: {
  123. type: 'image',
  124. mimes: ['image/jpeg'],
  125. updateFn: function(entry) {
  126. entry.type = 'image';
  127. entry.isImage = true;
  128. }
  129. },
  130. png: {
  131. type: 'image',
  132. mimes: ['image/png'],
  133. updateFn: function(entry) {
  134. entry.type = 'image';
  135. entry.isImage = true;
  136. }
  137. },
  138. svg: {
  139. type: 'image',
  140. mimes: ['image/svg+xml'],
  141. updateFn: function(entry) {
  142. entry.type = 'image';
  143. entry.isImage = true;
  144. entry.isSVG = true;
  145. }
  146. },
  147. gif: {
  148. type: 'image',
  149. mimes: ['image/gif'],
  150. updateFn: function(entry) {
  151. entry.type = 'image';
  152. entry.isImage = true;
  153. }
  154. },
  155. webp: {
  156. type: 'image',
  157. mimes: ['image/webp'],
  158. updateFn: function(entry) {
  159. entry.type = 'image';
  160. entry.isImage = true;
  161. }
  162. },
  163. woff: {
  164. type: 'webfont',
  165. mimes: ['application/x-font-woff', 'application/font-woff', 'font/woff'],
  166. updateFn: function(entry) {
  167. entry.type = 'webfont';
  168. entry.isWebFont = true;
  169. entry.isWoff = true;
  170. }
  171. },
  172. woff2: {
  173. type: 'webfont',
  174. mimes: ['font/woff2', 'application/x-font-woff2', 'application/font-woff2'],
  175. updateFn: function(entry) {
  176. entry.type = 'webfont';
  177. entry.isWebFont = true;
  178. entry.isWoff2 = true;
  179. }
  180. },
  181. otf: {
  182. type: 'webfont',
  183. mimes: ['application/x-font-otf', 'font/otf', 'font/opentype', 'application/x-font-opentype'],
  184. updateFn: function(entry) {
  185. entry.type = 'webfont';
  186. entry.isWebFont = true;
  187. }
  188. },
  189. ttf: {
  190. type: 'webfont',
  191. mimes: ['application/x-font-ttf', 'font/ttf', 'application/x-font-truetype'],
  192. updateFn: function(entry) {
  193. entry.type = 'webfont';
  194. entry.isWebFont = true;
  195. entry.isTTF = true;
  196. }
  197. },
  198. eot: {
  199. type: 'webfont',
  200. mimes: ['application/vnd.ms-fontobject', 'font/eot'],
  201. updateFn: function(entry) {
  202. entry.type = 'webfont';
  203. entry.isWebFont = true;
  204. }
  205. },
  206. json: {
  207. type: 'json',
  208. mimes: ['application/json'],
  209. updateFn: function(entry) {
  210. entry.type = 'json';
  211. entry.isJSON = true;
  212. }
  213. },
  214. };
  215. return {
  216. checkContentType: checkContentType,
  217. findContentType: findContentType
  218. };
  219. };
  220. module.exports = new ContentTypeChecker();