@eik/service 5.2.7 → 5.2.9

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [5.2.9](https://github.com/eik-lib/service/compare/v5.2.8...v5.2.9) (2026-08-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * bump @eik/core to 2.1.78 ([#793](https://github.com/eik-lib/service/issues/793)) ([1d77ad8](https://github.com/eik-lib/service/commit/1d77ad8a829c8d03df7c9ab0ecafa3b8522c0230))
7
+
8
+ ## [5.2.8](https://github.com/eik-lib/service/compare/v5.2.7...v5.2.8) (2026-08-17)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * close stalled upload connections after configurable bodyTimeout ([#789](https://github.com/eik-lib/service/issues/789)) ([e765fe7](https://github.com/eik-lib/service/commit/e765fe74fae91b75467477f20110bf83f33b4e0e))
14
+
1
15
  ## [5.2.7](https://github.com/eik-lib/service/compare/v5.2.6...v5.2.7) (2026-08-17)
2
16
 
3
17
 
package/lib/main.js CHANGED
@@ -23,6 +23,7 @@ import * as utils from "./utils.js";
23
23
  * @property {number} [pkgMaxFileSize=10000000] The limit in bytes before PUT /pkg/ starts returning 413 Content Too Large
24
24
  * @property {number} [mapMaxFileSize=1000000] The limit in bytes before PUT /map/ starts returning 413 Content Too Large
25
25
  * @property {number} [imgMaxFileSize=20000000] The limit in bytes before PUT /img/ starts returning 413 Content Too Large
26
+ * @property {number} [bodyTimeout=30000] Milliseconds before an upload request whose body has not been fully received is terminated with 408
26
27
  */
27
28
 
28
29
  const EikService = class EikService {
@@ -39,7 +40,9 @@ const EikService = class EikService {
39
40
  pkgMaxFileSize = 10000000,
40
41
  mapMaxFileSize = 1000000,
41
42
  imgMaxFileSize = 20000000,
43
+ bodyTimeout = 30000,
42
44
  } = options;
45
+ this._bodyTimeout = bodyTimeout;
43
46
  this._notFoundCacheControl = notFoundCacheControl || "public, max-age=5";
44
47
 
45
48
  if (!logger) {
@@ -215,6 +218,27 @@ const EikService = class EikService {
215
218
  api() {
216
219
  /** @param {import('fastify').FastifyInstance} app */
217
220
  return async (app) => {
221
+ // Enforce a maximum idle time per connection so that slow or
222
+ // stalled uploads cannot hold busboy, the tar parser, and open
223
+ // sink write streams open indefinitely. When a socket is idle
224
+ // for longer than bodyTimeout ms the server closes the connection
225
+ // with 408 Request Timeout. The timer resets whenever data flows,
226
+ // so actively uploading connections are not affected.
227
+ if (this._bodyTimeout > 0) {
228
+ app.server.on("connection", (socket) => {
229
+ socket.setTimeout(this._bodyTimeout);
230
+ socket.on("timeout", () => {
231
+ if (!socket.destroyed) {
232
+ socket.end(
233
+ "HTTP/1.1 408 Request Timeout\r\n" +
234
+ "Content-Length: 0\r\n" +
235
+ "Connection: close\r\n\r\n",
236
+ );
237
+ }
238
+ });
239
+ });
240
+ }
241
+
218
242
  if (!app.initialConfig.routerOptions?.ignoreTrailingSlash) {
219
243
  this.logger.warn(
220
244
  'Fastify is configured with "routerOptions.ignoreTrailingSlash" set to "false". Its adviced to set "routerOptions.ignoreTrailingSlash" to "true"',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eik/service",
3
- "version": "5.2.7",
3
+ "version": "5.2.9",
4
4
  "description": "Eik REST API as a standalone HTTP service",
5
5
  "type": "module",
6
6
  "main": "./lib/main.js",
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "homepage": "https://github.com/eik-lib/service#readme",
45
45
  "dependencies": {
46
- "@eik/core": "2.1.77",
46
+ "@eik/core": "2.1.78",
47
47
  "@eik/sink": "1.2.7",
48
48
  "@eik/sink-file-system": "2.0.40",
49
49
  "@eik/sink-memory": "2.0.37",
package/types/main.d.ts CHANGED
@@ -24,6 +24,10 @@ export type EikServiceOptions = {
24
24
  * The limit in bytes before PUT /img/ starts returning 413 Content Too Large
25
25
  */
26
26
  imgMaxFileSize?: number | undefined;
27
+ /**
28
+ * Milliseconds before an upload request whose body has not been fully received is terminated with 408
29
+ */
30
+ bodyTimeout?: number | undefined;
27
31
  };
28
32
  /**
29
33
  * @typedef {object} EikServiceOptions
@@ -36,9 +40,11 @@ export type EikServiceOptions = {
36
40
  * @property {number} [pkgMaxFileSize=10000000] The limit in bytes before PUT /pkg/ starts returning 413 Content Too Large
37
41
  * @property {number} [mapMaxFileSize=1000000] The limit in bytes before PUT /map/ starts returning 413 Content Too Large
38
42
  * @property {number} [imgMaxFileSize=20000000] The limit in bytes before PUT /img/ starts returning 413 Content Too Large
43
+ * @property {number} [bodyTimeout=30000] Milliseconds before an upload request whose body has not been fully received is terminated with 408
39
44
  */
40
45
  declare const EikService: {
41
46
  new (options?: EikServiceOptions): {
47
+ _bodyTimeout: number;
42
48
  _notFoundCacheControl: string;
43
49
  _versionsGet: {
44
50
  _organizations: [string, string][];
@@ -480,7 +486,36 @@ declare const EikService: {
480
486
  };
481
487
  readonly metrics: import("@metrics/client");
482
488
  _parser(incoming: any): Promise<any>;
483
- _writeEikJson(incoming: any, capturedEikJson: Record<string, any> | null): Promise<void>;
489
+ _writeEikJson(incoming: any, capturedEikJson: Record<string, any> | null): Promise<InstanceType<new ({ pathname, version, name, type, org, }?: import("@eik/core/types/classes/asset.js").AssetOptions) => {
490
+ _mimeType: string;
491
+ _type: string;
492
+ _size: number;
493
+ _integrity: string;
494
+ _pathname: string;
495
+ _version: string;
496
+ _name: string;
497
+ _org: string;
498
+ get integrity(): string;
499
+ set integrity(value: string): any;
500
+ get pathname(): string;
501
+ get mimeType(): string;
502
+ get version(): string;
503
+ get asset(): string;
504
+ get name(): string;
505
+ get type(): string;
506
+ set type(value: string): any;
507
+ get size(): number;
508
+ set size(value: number): any;
509
+ get org(): string;
510
+ toJSON(): {
511
+ integrity: string;
512
+ pathname: string;
513
+ mimeType: string;
514
+ type: string;
515
+ size: number;
516
+ };
517
+ get [Symbol.toStringTag](): string;
518
+ }>>;
484
519
  _readVersions(incoming: any): Promise<InstanceType<new ({ versions, type, name, org }?: {
485
520
  versions?: [any, any][];
486
521
  type?: string;
@@ -578,7 +613,36 @@ declare const EikService: {
578
613
  };
579
614
  readonly metrics: import("@metrics/client");
580
615
  _parser(incoming: any): Promise<any>;
581
- _writeEikJson(incoming: any, capturedEikJson: Record<string, any> | null): Promise<void>;
616
+ _writeEikJson(incoming: any, capturedEikJson: Record<string, any> | null): Promise<InstanceType<new ({ pathname, version, name, type, org, }?: import("@eik/core/types/classes/asset.js").AssetOptions) => {
617
+ _mimeType: string;
618
+ _type: string;
619
+ _size: number;
620
+ _integrity: string;
621
+ _pathname: string;
622
+ _version: string;
623
+ _name: string;
624
+ _org: string;
625
+ get integrity(): string;
626
+ set integrity(value: string): any;
627
+ get pathname(): string;
628
+ get mimeType(): string;
629
+ get version(): string;
630
+ get asset(): string;
631
+ get name(): string;
632
+ get type(): string;
633
+ set type(value: string): any;
634
+ get size(): number;
635
+ set size(value: number): any;
636
+ get org(): string;
637
+ toJSON(): {
638
+ integrity: string;
639
+ pathname: string;
640
+ mimeType: string;
641
+ type: string;
642
+ size: number;
643
+ };
644
+ get [Symbol.toStringTag](): string;
645
+ }>>;
582
646
  _readVersions(incoming: any): Promise<InstanceType<new ({ versions, type, name, org }?: {
583
647
  versions?: [any, any][];
584
648
  type?: string;