|
5 | 5 | const path = require('path');
|
6 | 6 | const fs = require('fs');
|
7 | 7 | const url = require('url');
|
| 8 | +const { Readable } = require('stream'); |
| 9 | +const buffer = require('buffer'); |
8 | 10 | const mime = require('mime');
|
9 | 11 | const urlJoin = require('url-join');
|
10 | 12 | const showDir = require('./show-dir');
|
11 | 13 | const version = require('../../package.json').version;
|
12 | 14 | const status = require('./status-handlers');
|
13 | 15 | const generateEtag = require('./etag');
|
14 | 16 | const optsParser = require('./opts');
|
| 17 | +const htmlEncodingSniffer = require('html-encoding-sniffer'); |
15 | 18 |
|
16 | 19 | let httpServerCore = null;
|
17 | 20 |
|
@@ -234,8 +237,17 @@ module.exports = function createMiddleware(_dir, _options) {
|
234 | 237 | let cacheControl = cache;
|
235 | 238 | let stream = null;
|
236 | 239 | if (contentType && isTextFile(contentType)) {
|
237 |
| - // Assume text types are utf8 |
238 |
| - contentType += '; charset=UTF-8'; |
| 240 | + if (stat.size < buffer.constants.MAX_LENGTH) { |
| 241 | + const bytes = fs.readFileSync(file); |
| 242 | + const sniffedEncoding = htmlEncodingSniffer(bytes, { |
| 243 | + defaultEncoding: 'UTF-8' |
| 244 | + }); |
| 245 | + contentType += `; charset=${sniffedEncoding}`; |
| 246 | + stream = Readable.from(bytes) |
| 247 | + } else { |
| 248 | + // Assume text types are utf8 |
| 249 | + contentType += '; charset=UTF-8'; |
| 250 | + } |
239 | 251 | }
|
240 | 252 |
|
241 | 253 | if (file === gzippedFile) { // is .gz picked up
|
@@ -317,7 +329,10 @@ module.exports = function createMiddleware(_dir, _options) {
|
317 | 329 | return;
|
318 | 330 | }
|
319 | 331 |
|
320 |
| - stream = fs.createReadStream(file); |
| 332 | + // stream may already have been assigned during encoding sniffing. |
| 333 | + if (stream === null) { |
| 334 | + stream = fs.createReadStream(file); |
| 335 | + } |
321 | 336 |
|
322 | 337 | stream.pipe(res);
|
323 | 338 | stream.on('error', (err) => {
|
|
0 commit comments