minimalistic-server 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.mjs +102 -10
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -1160,6 +1160,7 @@ export class FileResponse extends Response {
1160
1160
  #dataPromise = null;
1161
1161
 
1162
1162
  #maxChunkSize = 16 * 1024 * 1024;
1163
+ #fragmentRequestMap = new WeakMap();
1163
1164
 
1164
1165
  constructor(filePath, code = 200, contentType = null, cookies = null) {
1165
1166
  super();
@@ -1169,18 +1170,39 @@ export class FileResponse extends Response {
1169
1170
  this.setCookies(cookies);
1170
1171
  }
1171
1172
 
1172
- async getCode() {
1173
+ async getCode(headers = null) {
1174
+ const requestedFragment = await this.#getFragmentRequest(headers);
1173
1175
  const data = await this.#retreiveData();
1176
+
1177
+ if (requestedFragment) {
1178
+ return 206;
1179
+ }
1180
+
1174
1181
  return data.code;
1175
1182
  }
1176
1183
 
1177
- async getHeaders() {
1184
+ async getHeaders(headers = null) {
1185
+ const requestedFragment = await this.#getFragmentRequest(headers);
1178
1186
  const data = await this.#retreiveData();
1187
+
1188
+ if (requestedFragment) {
1189
+ return {
1190
+ 'Content-Range': `bytes ${requestedFragment.offset}-${requestedFragment.offset + requestedFragment.size - 1}/${data.size}`,
1191
+ ...data.headers,
1192
+ };
1193
+ }
1194
+
1179
1195
  return data.headers;
1180
1196
  }
1181
1197
 
1182
- async getBody() {
1198
+ async getBody(headers = null) {
1199
+ const requestedFragment = await this.#getFragmentRequest(headers);
1183
1200
  const data = await this.#retreiveData();
1201
+
1202
+ if (requestedFragment) {
1203
+ return () => data.body(requestedFragment);
1204
+ }
1205
+
1184
1206
  return data.body;
1185
1207
  }
1186
1208
 
@@ -1190,9 +1212,69 @@ export class FileResponse extends Response {
1190
1212
 
1191
1213
  setFilePath(filePath) {
1192
1214
  this.#filePath = filePath;
1215
+ this.#dataPromise = null;
1193
1216
  }
1194
1217
 
1195
- async * #getBodyStream() {
1218
+ async #getFragmentRequest(headers) {
1219
+ let result = this.#fragmentRequestMap.get(headers);
1220
+
1221
+ if (result === undefined && headers?.['range']) {
1222
+ if (headers['range'].includes(',')) {
1223
+ this.#fragmentRequestMap.set(headers, null);
1224
+ return null;
1225
+ }
1226
+
1227
+ const numbers = headers['range']
1228
+ .trim()
1229
+ .replace('bytes=', '')
1230
+ .split(/\b\s*-\s*/gm)
1231
+ .map(x => {
1232
+ x = +x;
1233
+
1234
+ if (isNaN(x) || x < Number.MIN_SAFE_INTEGER || x > Number.MAX_SAFE_INTEGER) {
1235
+ return null;
1236
+ }
1237
+
1238
+ return Math.floor(x);
1239
+ });
1240
+
1241
+ const data = await this.#retreiveData();
1242
+
1243
+ if (data.code < 200 || data.code > 299 || typeof data.body !== 'function') {
1244
+ this.#fragmentRequestMap.set(headers, null);
1245
+ return null;
1246
+ }
1247
+
1248
+ let offset = numbers[0] ?? 0;
1249
+
1250
+ if (offset < 0) {
1251
+ offset = data.size + offset;
1252
+ }
1253
+
1254
+ if (offset >= data.size) {
1255
+ offset = data.size - 1;
1256
+ }
1257
+
1258
+ let size = (numbers[1] ?? (data.size - 1)) - offset + 1;
1259
+
1260
+ if (size < 0) {
1261
+ size = 0;
1262
+ }
1263
+
1264
+ size = Math.min(size, data.size - offset);
1265
+
1266
+ result = {
1267
+ offset,
1268
+ size,
1269
+ };
1270
+
1271
+ this.#fragmentRequestMap.set(headers, result);
1272
+ }
1273
+
1274
+ return result ?? null;
1275
+ }
1276
+
1277
+ async * #getBodyStream(fragmentRequest) {
1196
1278
  const data = await this.#retreiveData();
1197
1279
 
1198
1280
  if (typeof data.body !== 'function') {
@@ -1203,12 +1285,19 @@ export class FileResponse extends Response {
1203
1285
  let filehandle;
1204
1286
 
1205
1287
  try {
1288
+ const requestedPosition = fragmentRequest?.offset ?? 0;
1289
+ const requestedSize = fragmentRequest?.size ?? data.size;
1290
+
1206
1291
  filehandle = await fs.open(this.#filePath, 'r');
1207
1292
 
1208
- for (let offset = 0; offset < data.size; offset += this.#maxChunkSize) {
1209
- const size = Math.min(this.#maxChunkSize, data.size - offset);
1293
+ if (requestedPosition > 0) {
1294
+ await filehandle.read(Buffer.alloc(0), 0, 0, requestedPosition);
1295
+ }
1296
+
1297
+ for (let offset = 0; offset < requestedSize; offset += this.#maxChunkSize) {
1298
+ const size = Math.min(this.#maxChunkSize, requestedSize - offset);
1210
1299
  const chunk = await filehandle.read(Buffer.alloc(size), 0, size);
1211
- yield chunk;
1300
+ yield chunk.buffer;
1212
1301
  await new Promise(resolve => setTimeout(resolve, 100));
1213
1302
  }
1214
1303
  } finally {
@@ -1224,7 +1313,7 @@ export class FileResponse extends Response {
1224
1313
  try {
1225
1314
  filehandle = await fs.open(this.#filePath, 'r');
1226
1315
  const size = (await filehandle.stat()).size;
1227
- const body = size > this.#maxChunkSize ? (() => this.#getBodyStream()) : await filehandle.readFile();
1316
+ const body = size > this.#maxChunkSize ? (fragmentRequest => this.#getBodyStream(fragmentRequest)) : await filehandle.readFile();
1228
1317
 
1229
1318
  resolve({
1230
1319
  code: this.#code,
@@ -1530,12 +1619,15 @@ async function handleRequest(req, routes, staticFileDirectory) {
1530
1619
  message: 'Invalid data'
1531
1620
  }, 400);
1532
1621
 
1622
+ let requestHeaders;
1623
+
1533
1624
  try {
1534
1625
  const request = new Request(req);
1535
1626
 
1536
1627
  const method = request.getMethod();
1537
1628
  const path = request.getPath();
1538
1629
  const pathParams = {};
1630
+ requestHeaders = request.getHeaders();
1539
1631
 
1540
1632
  let routeHandler = routes;
1541
1633
  let handleOptions = false;
@@ -1584,7 +1676,7 @@ async function handleRequest(req, routes, staticFileDirectory) {
1584
1676
 
1585
1677
  if (method === 'OPTIONS' && typeof routeHandler?.[`/${method}/`] !== 'function') {
1586
1678
  handleOptions = true;
1587
- routeHandler = routeHandler?.[`/${request.getHeaders()?.['access-control-request-method']}/`.toUpperCase()];
1679
+ routeHandler = routeHandler?.[`/${requestHeaders?.['access-control-request-method']}/`.toUpperCase()];
1588
1680
  } else {
1589
1681
  routeHandler = routeHandler?.[`/${method}/`];
1590
1682
  }
@@ -1613,7 +1705,7 @@ async function handleRequest(req, routes, staticFileDirectory) {
1613
1705
  }
1614
1706
 
1615
1707
  try {
1616
- return [await response.getCode(), await response.getHeaders(), await response.getBody()];
1708
+ return [await response.getCode(requestHeaders), await response.getHeaders(requestHeaders), await response.getBody(requestHeaders)];
1617
1709
  } catch (error) {
1618
1710
  console.error(error);
1619
1711
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minimalistic-server",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "engines" : {
5
5
  "npm" : ">=8.6.0",
6
6
  "node" : ">=22.0.0"