hfs
Version:
HTTP File Server
175 lines (174 loc) • 6.91 kB
JavaScript
;
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsapStream = void 0;
exports.pattern2filter = pattern2filter;
exports.isLocalHost = isLocalHost;
exports.netMatches = netMatches;
exports.makeNetMatcher = makeNetMatcher;
exports.same = same;
exports.asyncGeneratorToReadable = asyncGeneratorToReadable;
exports.apiAssertTypes = apiAssertTypes;
exports.createStreamLimiter = createStreamLimiter;
const path_1 = require("path");
__exportStar(require("./util-http"), exports);
__exportStar(require("./util-files"), exports);
__exportStar(require("./fileAttr"), exports);
__exportStar(require("./cross"), exports);
__exportStar(require("./debounceAsync"), exports);
const stream_1 = require("stream");
const node_net_1 = require("node:net");
const apiMiddleware_1 = require("./apiMiddleware");
const const_1 = require("./const");
const cross_1 = require("./cross");
const net_1 = require("net");
const lodash_1 = __importDefault(require("lodash"));
function pattern2filter(pattern) {
const matcher = (0, cross_1.makeMatcher)(pattern.includes('*') ? pattern // if you specify *, we'll respect its position
: pattern.split('|').map(x => `*${x}*`).join('|'));
return (s) => !pattern || matcher((0, path_1.basename)(s || ''));
}
function isLocalHost(c) {
const ip = typeof c === 'string' ? c : c.socket.remoteAddress; // don't use Context.ip as it is subject to proxied ips, and that's no use for localhost detection
return ip && (0, cross_1.isIpLocalHost)(ip);
}
// this will memory-leak over mask, so be careful with what you use this. Object is 3x faster than _.memoize
function netMatches(ip, mask, emptyMaskReturns = false) {
var _a, _b;
const cache = (_a = netMatches).cache || (_a.cache = {});
return (cache[_b = mask + (emptyMaskReturns ? '1' : '0')] || (cache[_b] = makeNetMatcher(mask, emptyMaskReturns)))(ip); // cache the matcher
}
function makeNetMatcher(mask, emptyMaskReturns = false) {
var _a;
if (!mask)
return () => emptyMaskReturns;
mask = mask.replaceAll(' ', '');
mask = mask.replace('localhost', '::1|127.0.0.1');
try {
if (!/\/|-(?![^\[]*\])/.test(mask)) { // when no CIDR and no ranges are used, then we use standard matcher, otherwise BlockList. For "-" we must skip those inside []
if (/[^.:\da-fA-F*?|()!]/.test(mask))
throw mask;
return (0, cross_1.makeMatcher)(mask);
}
const all = mask.split('|');
const neg = ((_a = all[0]) === null || _a === void 0 ? void 0 : _a[0]) === '!';
if (neg)
all[0] = all[0].slice(1);
const bl = new node_net_1.BlockList();
for (const x of all) {
const m = /^([.:\da-f]+)(?:\/(\d+)|-([.:\da-f]+)|)$/i.exec(x); // parse cidr or range
if (!m)
throw x; // we don't support wildcards in this case
const address = (0, cross_1.try_)(() => parseAddress(m[1]), () => { throw m[1]; });
if (!address)
continue;
if (m[2])
try {
bl.addSubnet(address, Number(m[2]));
}
catch (_b) {
throw x;
}
else if (m[3])
try {
bl.addRange(address, parseAddress(m[2]));
}
catch (_c) {
throw m[2];
}
else
bl.addAddress(address);
}
return (ip) => {
try {
return neg !== bl.check(parseAddress(ip));
}
catch (_a) {
console.error("invalid address ", ip);
return false;
}
};
}
catch (e) {
throw "error in net-mask: " + e;
}
}
// can throw ERR_INVALID_ADDRESS
function parseAddress(s) {
return new node_net_1.SocketAddress({ address: s, family: (0, net_1.isIPv6)(s) ? 'ipv6' : 'ipv4' });
}
function same(a, b) {
return lodash_1.default.isEqual(a, b);
}
function asyncGeneratorToReadable(generator) {
const iterator = generator[Symbol.asyncIterator]();
return new stream_1.Readable({
objectMode: true,
destroy() {
var _a;
void ((_a = iterator.return) === null || _a === void 0 ? void 0 : _a.call(iterator));
},
read() {
iterator.next().then(it => {
if (it.done)
this.emit('ending');
return this.push(it.done ? null : it.value);
});
}
});
}
// produces as promises resolve, not sequentially
class AsapStream extends stream_1.Readable {
constructor(promises) {
super({ objectMode: true });
this.promises = promises;
this.finished = false;
}
_read() {
if (this.finished)
return;
this.finished = true;
for (const p of this.promises)
p.then(x => x !== undefined && this.push(x), e => this.emit('error', e));
Promise.allSettled(this.promises).then(() => this.push(null));
}
}
exports.AsapStream = AsapStream;
function apiAssertTypes(paramsByType) {
for (const [types, params] of Object.entries(paramsByType))
for (const [name, val] of Object.entries(params))
if (!types.split('_').some(type => type === 'array' ? Array.isArray(val) : typeof val === type))
throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad ' + name);
}
function createStreamLimiter(limit) {
let got = 0;
return new stream_1.Transform({
transform(chunk, enc, done) {
const left = limit - got;
got += chunk.length;
if (left > 0) {
this.push(chunk.length > left ? chunk.slice(0, left) : chunk);
if (got >= limit)
this.end();
}
done();
}
});
}