@hono/node-server 1.15.0 → 1.17.0

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.
@@ -23,9 +23,9 @@ __export(serve_static_exports, {
23
23
  serveStatic: () => serveStatic
24
24
  });
25
25
  module.exports = __toCommonJS(serve_static_exports);
26
- var import_filepath = require("hono/utils/filepath");
27
26
  var import_mime = require("hono/utils/mime");
28
27
  var import_node_fs = require("fs");
28
+ var import_node_path = require("path");
29
29
  var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
30
30
  var ENCODINGS = {
31
31
  br: ".br",
@@ -49,9 +49,6 @@ var createStreamBody = (stream) => {
49
49
  });
50
50
  return body;
51
51
  };
52
- var addCurrentDirPrefix = (path) => {
53
- return `./${path}`;
54
- };
55
52
  var getStats = (path) => {
56
53
  let stats;
57
54
  try {
@@ -61,36 +58,35 @@ var getStats = (path) => {
61
58
  return stats;
62
59
  };
63
60
  var serveStatic = (options = { root: "" }) => {
61
+ const root = (0, import_node_path.resolve)(options.root || ".");
62
+ const optionPath = options.path;
64
63
  return async (c, next) => {
65
64
  if (c.finalized) {
66
65
  return next();
67
66
  }
68
67
  let filename;
69
68
  try {
70
- filename = options.path ?? decodeURIComponent(c.req.path);
69
+ const rawPath = optionPath ?? c.req.path;
70
+ if (!optionPath) {
71
+ const decodedPath = decodeURIComponent(rawPath);
72
+ if (decodedPath.includes("..")) {
73
+ await options.onNotFound?.(rawPath, c);
74
+ return next();
75
+ }
76
+ }
77
+ filename = optionPath ?? decodeURIComponent(c.req.path);
71
78
  } catch {
72
79
  await options.onNotFound?.(c.req.path, c);
73
80
  return next();
74
81
  }
75
- let path = (0, import_filepath.getFilePathWithoutDefaultDocument)({
76
- filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename,
77
- root: options.root
78
- });
79
- if (path) {
80
- path = addCurrentDirPrefix(path);
81
- } else {
82
- return next();
83
- }
82
+ const requestPath = options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename;
83
+ let path = optionPath ? options.root ? (0, import_node_path.resolve)((0, import_node_path.join)(root, optionPath)) : optionPath : (0, import_node_path.resolve)((0, import_node_path.join)(root, requestPath));
84
84
  let stats = getStats(path);
85
85
  if (stats && stats.isDirectory()) {
86
- path = (0, import_filepath.getFilePath)({
87
- filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename,
88
- root: options.root,
89
- defaultDocument: options.index ?? "index.html"
90
- });
91
- if (path) {
92
- path = addCurrentDirPrefix(path);
93
- } else {
86
+ const indexFile = options.index ?? "index.html";
87
+ path = (0, import_node_path.resolve)((0, import_node_path.join)(path, indexFile));
88
+ if (!optionPath && !path.startsWith(root)) {
89
+ await options.onNotFound?.(path, c);
94
90
  return next();
95
91
  }
96
92
  stats = getStats(path);
@@ -1,7 +1,7 @@
1
1
  // src/serve-static.ts
2
- import { getFilePath, getFilePathWithoutDefaultDocument } from "hono/utils/filepath";
3
2
  import { getMimeType } from "hono/utils/mime";
4
3
  import { createReadStream, lstatSync } from "fs";
4
+ import { join, resolve } from "path";
5
5
  var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
6
6
  var ENCODINGS = {
7
7
  br: ".br",
@@ -25,9 +25,6 @@ var createStreamBody = (stream) => {
25
25
  });
26
26
  return body;
27
27
  };
28
- var addCurrentDirPrefix = (path) => {
29
- return `./${path}`;
30
- };
31
28
  var getStats = (path) => {
32
29
  let stats;
33
30
  try {
@@ -37,36 +34,35 @@ var getStats = (path) => {
37
34
  return stats;
38
35
  };
39
36
  var serveStatic = (options = { root: "" }) => {
37
+ const root = resolve(options.root || ".");
38
+ const optionPath = options.path;
40
39
  return async (c, next) => {
41
40
  if (c.finalized) {
42
41
  return next();
43
42
  }
44
43
  let filename;
45
44
  try {
46
- filename = options.path ?? decodeURIComponent(c.req.path);
45
+ const rawPath = optionPath ?? c.req.path;
46
+ if (!optionPath) {
47
+ const decodedPath = decodeURIComponent(rawPath);
48
+ if (decodedPath.includes("..")) {
49
+ await options.onNotFound?.(rawPath, c);
50
+ return next();
51
+ }
52
+ }
53
+ filename = optionPath ?? decodeURIComponent(c.req.path);
47
54
  } catch {
48
55
  await options.onNotFound?.(c.req.path, c);
49
56
  return next();
50
57
  }
51
- let path = getFilePathWithoutDefaultDocument({
52
- filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename,
53
- root: options.root
54
- });
55
- if (path) {
56
- path = addCurrentDirPrefix(path);
57
- } else {
58
- return next();
59
- }
58
+ const requestPath = options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename;
59
+ let path = optionPath ? options.root ? resolve(join(root, optionPath)) : optionPath : resolve(join(root, requestPath));
60
60
  let stats = getStats(path);
61
61
  if (stats && stats.isDirectory()) {
62
- path = getFilePath({
63
- filename: options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename,
64
- root: options.root,
65
- defaultDocument: options.index ?? "index.html"
66
- });
67
- if (path) {
68
- path = addCurrentDirPrefix(path);
69
- } else {
62
+ const indexFile = options.index ?? "index.html";
63
+ path = resolve(join(path, indexFile));
64
+ if (!optionPath && !path.startsWith(root)) {
65
+ await options.onNotFound?.(path, c);
70
66
  return next();
71
67
  }
72
68
  stats = getStats(path);
package/dist/server.js CHANGED
@@ -36,6 +36,9 @@ __export(server_exports, {
36
36
  module.exports = __toCommonJS(server_exports);
37
37
  var import_node_http = require("http");
38
38
 
39
+ // src/listener.ts
40
+ var import_node_http22 = require("http2");
41
+
39
42
  // src/request.ts
40
43
  var import_node_http2 = require("http2");
41
44
  var import_node_stream = require("stream");
@@ -64,6 +67,7 @@ var Request = class extends GlobalRequest {
64
67
  super(input, options);
65
68
  }
66
69
  };
70
+ var wrapBodyStream = Symbol("wrapBodyStream");
67
71
  var newRequestFromIncoming = (method, url, incoming, abortController) => {
68
72
  const headerRecord = [];
69
73
  const rawHeaders = incoming.rawHeaders;
@@ -97,6 +101,23 @@ var newRequestFromIncoming = (method, url, incoming, abortController) => {
97
101
  controller.close();
98
102
  }
99
103
  });
104
+ } else if (incoming[wrapBodyStream]) {
105
+ let reader;
106
+ init.body = new ReadableStream({
107
+ async pull(controller) {
108
+ try {
109
+ reader ||= import_node_stream.Readable.toWeb(incoming).getReader();
110
+ const { done, value } = await reader.read();
111
+ if (done) {
112
+ controller.close();
113
+ } else {
114
+ controller.enqueue(value);
115
+ }
116
+ } catch (error) {
117
+ controller.error(error);
118
+ }
119
+ }
120
+ });
100
121
  } else {
101
122
  init.body = import_node_stream.Readable.toWeb(incoming);
102
123
  }
@@ -345,6 +366,7 @@ global.fetch = (info, init) => {
345
366
  };
346
367
 
347
368
  // src/listener.ts
369
+ var outgoingEnded = Symbol("outgoingEnded");
348
370
  var regBuffer = /^no$/i;
349
371
  var regContentType = /^(application\/json\b|text\/(?!event-stream\b))/i;
350
372
  var handleRequestError = () => new Response(null, {
@@ -390,10 +412,12 @@ var responseViaCache = async (res, outgoing) => {
390
412
  outgoing.end(new Uint8Array(await body.arrayBuffer()));
391
413
  } else {
392
414
  flushHeaders(outgoing);
393
- return writeFromReadableStream(body, outgoing)?.catch(
415
+ await writeFromReadableStream(body, outgoing)?.catch(
394
416
  (e) => handleResponseError(e, outgoing)
395
417
  );
396
418
  }
419
+ ;
420
+ outgoing[outgoingEnded]?.();
397
421
  };
398
422
  var responseViaResponseObject = async (res, outgoing, options = {}) => {
399
423
  if (res instanceof Promise) {
@@ -439,8 +463,11 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
439
463
  outgoing.writeHead(res.status, resHeaderRecord);
440
464
  outgoing.end();
441
465
  }
466
+ ;
467
+ outgoing[outgoingEnded]?.();
442
468
  };
443
469
  var getRequestListener = (fetchCallback, options = {}) => {
470
+ const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
444
471
  if (options.overrideGlobalObjects !== false && global.Request !== Request) {
445
472
  Object.defineProperty(global, "Request", {
446
473
  value: Request
@@ -453,15 +480,46 @@ var getRequestListener = (fetchCallback, options = {}) => {
453
480
  let res, req;
454
481
  try {
455
482
  req = newRequest(incoming, options.hostname);
483
+ let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
484
+ if (!incomingEnded) {
485
+ ;
486
+ incoming[wrapBodyStream] = true;
487
+ incoming.on("end", () => {
488
+ incomingEnded = true;
489
+ });
490
+ if (incoming instanceof import_node_http22.Http2ServerRequest) {
491
+ ;
492
+ outgoing[outgoingEnded] = () => {
493
+ if (!incomingEnded) {
494
+ setTimeout(() => {
495
+ if (!incomingEnded) {
496
+ setTimeout(() => {
497
+ incoming.destroy();
498
+ outgoing.destroy();
499
+ });
500
+ }
501
+ });
502
+ }
503
+ };
504
+ }
505
+ }
456
506
  outgoing.on("close", () => {
457
507
  const abortController = req[abortControllerKey];
458
- if (!abortController) {
459
- return;
508
+ if (abortController) {
509
+ if (incoming.errored) {
510
+ req[abortControllerKey].abort(incoming.errored.toString());
511
+ } else if (!outgoing.writableFinished) {
512
+ req[abortControllerKey].abort("Client connection prematurely closed.");
513
+ }
460
514
  }
461
- if (incoming.errored) {
462
- req[abortControllerKey].abort(incoming.errored.toString());
463
- } else if (!outgoing.writableFinished) {
464
- req[abortControllerKey].abort("Client connection prematurely closed.");
515
+ if (!incomingEnded) {
516
+ setTimeout(() => {
517
+ if (!incomingEnded) {
518
+ setTimeout(() => {
519
+ incoming.destroy();
520
+ });
521
+ }
522
+ });
465
523
  }
466
524
  });
467
525
  res = fetchCallback(req, { incoming, outgoing });
@@ -497,7 +555,8 @@ var createAdaptorServer = (options) => {
497
555
  const fetchCallback = options.fetch;
498
556
  const requestListener = getRequestListener(fetchCallback, {
499
557
  hostname: options.hostname,
500
- overrideGlobalObjects: options.overrideGlobalObjects
558
+ overrideGlobalObjects: options.overrideGlobalObjects,
559
+ autoCleanupIncoming: options.autoCleanupIncoming
501
560
  });
502
561
  const createServer = options.createServer || import_node_http.createServer;
503
562
  const server = createServer(options.serverOptions || {}, requestListener);
package/dist/server.mjs CHANGED
@@ -1,6 +1,9 @@
1
1
  // src/server.ts
2
2
  import { createServer as createServerHTTP } from "http";
3
3
 
4
+ // src/listener.ts
5
+ import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
6
+
4
7
  // src/request.ts
5
8
  import { Http2ServerRequest } from "http2";
6
9
  import { Readable } from "stream";
@@ -29,6 +32,7 @@ var Request = class extends GlobalRequest {
29
32
  super(input, options);
30
33
  }
31
34
  };
35
+ var wrapBodyStream = Symbol("wrapBodyStream");
32
36
  var newRequestFromIncoming = (method, url, incoming, abortController) => {
33
37
  const headerRecord = [];
34
38
  const rawHeaders = incoming.rawHeaders;
@@ -62,6 +66,23 @@ var newRequestFromIncoming = (method, url, incoming, abortController) => {
62
66
  controller.close();
63
67
  }
64
68
  });
69
+ } else if (incoming[wrapBodyStream]) {
70
+ let reader;
71
+ init.body = new ReadableStream({
72
+ async pull(controller) {
73
+ try {
74
+ reader ||= Readable.toWeb(incoming).getReader();
75
+ const { done, value } = await reader.read();
76
+ if (done) {
77
+ controller.close();
78
+ } else {
79
+ controller.enqueue(value);
80
+ }
81
+ } catch (error) {
82
+ controller.error(error);
83
+ }
84
+ }
85
+ });
65
86
  } else {
66
87
  init.body = Readable.toWeb(incoming);
67
88
  }
@@ -310,6 +331,7 @@ global.fetch = (info, init) => {
310
331
  };
311
332
 
312
333
  // src/listener.ts
334
+ var outgoingEnded = Symbol("outgoingEnded");
313
335
  var regBuffer = /^no$/i;
314
336
  var regContentType = /^(application\/json\b|text\/(?!event-stream\b))/i;
315
337
  var handleRequestError = () => new Response(null, {
@@ -355,10 +377,12 @@ var responseViaCache = async (res, outgoing) => {
355
377
  outgoing.end(new Uint8Array(await body.arrayBuffer()));
356
378
  } else {
357
379
  flushHeaders(outgoing);
358
- return writeFromReadableStream(body, outgoing)?.catch(
380
+ await writeFromReadableStream(body, outgoing)?.catch(
359
381
  (e) => handleResponseError(e, outgoing)
360
382
  );
361
383
  }
384
+ ;
385
+ outgoing[outgoingEnded]?.();
362
386
  };
363
387
  var responseViaResponseObject = async (res, outgoing, options = {}) => {
364
388
  if (res instanceof Promise) {
@@ -404,8 +428,11 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
404
428
  outgoing.writeHead(res.status, resHeaderRecord);
405
429
  outgoing.end();
406
430
  }
431
+ ;
432
+ outgoing[outgoingEnded]?.();
407
433
  };
408
434
  var getRequestListener = (fetchCallback, options = {}) => {
435
+ const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
409
436
  if (options.overrideGlobalObjects !== false && global.Request !== Request) {
410
437
  Object.defineProperty(global, "Request", {
411
438
  value: Request
@@ -418,15 +445,46 @@ var getRequestListener = (fetchCallback, options = {}) => {
418
445
  let res, req;
419
446
  try {
420
447
  req = newRequest(incoming, options.hostname);
448
+ let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
449
+ if (!incomingEnded) {
450
+ ;
451
+ incoming[wrapBodyStream] = true;
452
+ incoming.on("end", () => {
453
+ incomingEnded = true;
454
+ });
455
+ if (incoming instanceof Http2ServerRequest2) {
456
+ ;
457
+ outgoing[outgoingEnded] = () => {
458
+ if (!incomingEnded) {
459
+ setTimeout(() => {
460
+ if (!incomingEnded) {
461
+ setTimeout(() => {
462
+ incoming.destroy();
463
+ outgoing.destroy();
464
+ });
465
+ }
466
+ });
467
+ }
468
+ };
469
+ }
470
+ }
421
471
  outgoing.on("close", () => {
422
472
  const abortController = req[abortControllerKey];
423
- if (!abortController) {
424
- return;
473
+ if (abortController) {
474
+ if (incoming.errored) {
475
+ req[abortControllerKey].abort(incoming.errored.toString());
476
+ } else if (!outgoing.writableFinished) {
477
+ req[abortControllerKey].abort("Client connection prematurely closed.");
478
+ }
425
479
  }
426
- if (incoming.errored) {
427
- req[abortControllerKey].abort(incoming.errored.toString());
428
- } else if (!outgoing.writableFinished) {
429
- req[abortControllerKey].abort("Client connection prematurely closed.");
480
+ if (!incomingEnded) {
481
+ setTimeout(() => {
482
+ if (!incomingEnded) {
483
+ setTimeout(() => {
484
+ incoming.destroy();
485
+ });
486
+ }
487
+ });
430
488
  }
431
489
  });
432
490
  res = fetchCallback(req, { incoming, outgoing });
@@ -462,7 +520,8 @@ var createAdaptorServer = (options) => {
462
520
  const fetchCallback = options.fetch;
463
521
  const requestListener = getRequestListener(fetchCallback, {
464
522
  hostname: options.hostname,
465
- overrideGlobalObjects: options.overrideGlobalObjects
523
+ overrideGlobalObjects: options.overrideGlobalObjects,
524
+ autoCleanupIncoming: options.autoCleanupIncoming
466
525
  });
467
526
  const createServer = options.createServer || createServerHTTP;
468
527
  const server = createServer(options.serverOptions || {}, requestListener);
package/dist/types.d.mts CHANGED
@@ -35,6 +35,7 @@ type ServerOptions = createHttpOptions | createHttpsOptions | createHttp2Options
35
35
  type Options = {
36
36
  fetch: FetchCallback;
37
37
  overrideGlobalObjects?: boolean;
38
+ autoCleanupIncoming?: boolean;
38
39
  port?: number;
39
40
  hostname?: string;
40
41
  } & ServerOptions;
package/dist/types.d.ts CHANGED
@@ -35,6 +35,7 @@ type ServerOptions = createHttpOptions | createHttpsOptions | createHttp2Options
35
35
  type Options = {
36
36
  fetch: FetchCallback;
37
37
  overrideGlobalObjects?: boolean;
38
+ autoCleanupIncoming?: boolean;
38
39
  port?: number;
39
40
  hostname?: string;
40
41
  } & ServerOptions;
package/dist/vercel.js CHANGED
@@ -34,6 +34,9 @@ __export(vercel_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(vercel_exports);
36
36
 
37
+ // src/listener.ts
38
+ var import_node_http22 = require("http2");
39
+
37
40
  // src/request.ts
38
41
  var import_node_http2 = require("http2");
39
42
  var import_node_stream = require("stream");
@@ -62,6 +65,7 @@ var Request = class extends GlobalRequest {
62
65
  super(input, options);
63
66
  }
64
67
  };
68
+ var wrapBodyStream = Symbol("wrapBodyStream");
65
69
  var newRequestFromIncoming = (method, url, incoming, abortController) => {
66
70
  const headerRecord = [];
67
71
  const rawHeaders = incoming.rawHeaders;
@@ -95,6 +99,23 @@ var newRequestFromIncoming = (method, url, incoming, abortController) => {
95
99
  controller.close();
96
100
  }
97
101
  });
102
+ } else if (incoming[wrapBodyStream]) {
103
+ let reader;
104
+ init.body = new ReadableStream({
105
+ async pull(controller) {
106
+ try {
107
+ reader ||= import_node_stream.Readable.toWeb(incoming).getReader();
108
+ const { done, value } = await reader.read();
109
+ if (done) {
110
+ controller.close();
111
+ } else {
112
+ controller.enqueue(value);
113
+ }
114
+ } catch (error) {
115
+ controller.error(error);
116
+ }
117
+ }
118
+ });
98
119
  } else {
99
120
  init.body = import_node_stream.Readable.toWeb(incoming);
100
121
  }
@@ -343,6 +364,7 @@ global.fetch = (info, init) => {
343
364
  };
344
365
 
345
366
  // src/listener.ts
367
+ var outgoingEnded = Symbol("outgoingEnded");
346
368
  var regBuffer = /^no$/i;
347
369
  var regContentType = /^(application\/json\b|text\/(?!event-stream\b))/i;
348
370
  var handleRequestError = () => new Response(null, {
@@ -388,10 +410,12 @@ var responseViaCache = async (res, outgoing) => {
388
410
  outgoing.end(new Uint8Array(await body.arrayBuffer()));
389
411
  } else {
390
412
  flushHeaders(outgoing);
391
- return writeFromReadableStream(body, outgoing)?.catch(
413
+ await writeFromReadableStream(body, outgoing)?.catch(
392
414
  (e) => handleResponseError(e, outgoing)
393
415
  );
394
416
  }
417
+ ;
418
+ outgoing[outgoingEnded]?.();
395
419
  };
396
420
  var responseViaResponseObject = async (res, outgoing, options = {}) => {
397
421
  if (res instanceof Promise) {
@@ -437,8 +461,11 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
437
461
  outgoing.writeHead(res.status, resHeaderRecord);
438
462
  outgoing.end();
439
463
  }
464
+ ;
465
+ outgoing[outgoingEnded]?.();
440
466
  };
441
467
  var getRequestListener = (fetchCallback, options = {}) => {
468
+ const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
442
469
  if (options.overrideGlobalObjects !== false && global.Request !== Request) {
443
470
  Object.defineProperty(global, "Request", {
444
471
  value: Request
@@ -451,15 +478,46 @@ var getRequestListener = (fetchCallback, options = {}) => {
451
478
  let res, req;
452
479
  try {
453
480
  req = newRequest(incoming, options.hostname);
481
+ let incomingEnded = !autoCleanupIncoming || incoming.method === "GET" || incoming.method === "HEAD";
482
+ if (!incomingEnded) {
483
+ ;
484
+ incoming[wrapBodyStream] = true;
485
+ incoming.on("end", () => {
486
+ incomingEnded = true;
487
+ });
488
+ if (incoming instanceof import_node_http22.Http2ServerRequest) {
489
+ ;
490
+ outgoing[outgoingEnded] = () => {
491
+ if (!incomingEnded) {
492
+ setTimeout(() => {
493
+ if (!incomingEnded) {
494
+ setTimeout(() => {
495
+ incoming.destroy();
496
+ outgoing.destroy();
497
+ });
498
+ }
499
+ });
500
+ }
501
+ };
502
+ }
503
+ }
454
504
  outgoing.on("close", () => {
455
505
  const abortController = req[abortControllerKey];
456
- if (!abortController) {
457
- return;
506
+ if (abortController) {
507
+ if (incoming.errored) {
508
+ req[abortControllerKey].abort(incoming.errored.toString());
509
+ } else if (!outgoing.writableFinished) {
510
+ req[abortControllerKey].abort("Client connection prematurely closed.");
511
+ }
458
512
  }
459
- if (incoming.errored) {
460
- req[abortControllerKey].abort(incoming.errored.toString());
461
- } else if (!outgoing.writableFinished) {
462
- req[abortControllerKey].abort("Client connection prematurely closed.");
513
+ if (!incomingEnded) {
514
+ setTimeout(() => {
515
+ if (!incomingEnded) {
516
+ setTimeout(() => {
517
+ incoming.destroy();
518
+ });
519
+ }
520
+ });
463
521
  }
464
522
  });
465
523
  res = fetchCallback(req, { incoming, outgoing });