Skip to content

Commit

Permalink
feat(#155): manual MIME checking for empty and single byte files
Browse files Browse the repository at this point in the history
  • Loading branch information
jannis-baum committed Aug 3, 2024
1 parent 2188f9f commit 998ac6b
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/routes/viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ router.get(/.*/, async (req: Request, res: Response) => {
body = renderDirectory(path);
} else {
const data = readFileSync(path);
const mime = pmime(path);
const mime = await pmime(path);
if (!shouldRender(mime)) {
res.setHeader('Content-Type', mime).send(data);
return;
Expand Down Expand Up @@ -99,7 +99,7 @@ router.post(/.*/, async (req: Request, res: Response) => {
let { content } = req.body;

if (reload) {
const mime = pmime(path);
const mime = await pmime(path);
if (!shouldRender(mime)) {
res.status(400).send('Reload is only permitted on rendered files');
return;
Expand Down
29 changes: 27 additions & 2 deletions src/utils/path.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,34 @@
import { execSync } from 'child_process';
import { exec } from 'child_process';
import { homedir } from 'os';
import { basename as pbasename, dirname as pdirname, parse as pparse } from 'path';
import config from '../config.js';
import { stat, readFile } from 'fs/promises';
import { promisify } from 'util';

export const pmime = (path: string) => execSync(`file --mime-type -b '${path}'`).toString().trim();
const execPromise = promisify(exec);
export const pmime = async (path: string) => {
const [{ stdout: mime }, stats] = await Promise.all([
execPromise(`file --mime-type -b '${path}'`),
stat(path),
]);
// empty files can also be `application/x-empty`
// -> we unify to `inode/x-empty`
if (stats.size == 0) return 'inode/x-empty';
// single byte files don't work well for mime recognition as they will
// always be guessed as application/octet-stream
// -> we return `text/plain` if the single byte is a printable character
if (stats.size == 1) {
const content = await readFile(path);
const char = content.at(0);
if (
char !== undefined &&
// tab line feed carriage return printable character range
(char === 0x09 || char === 0x0a || char === 0x0d || (char >= 0x20 && char <= 0x7e))
)
return 'text/plain';
}
return mime.trim();
};

export const pcomponents = (path: string) => {
const parsed = pparse(path);
Expand Down

0 comments on commit 998ac6b

Please sign in to comment.