@hono/node-server 1.6.0 → 1.8.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.
package/dist/response.js CHANGED
@@ -22,7 +22,8 @@ var response_exports = {};
22
22
  __export(response_exports, {
23
23
  GlobalResponse: () => GlobalResponse,
24
24
  Response: () => Response,
25
- cacheKey: () => cacheKey
25
+ cacheKey: () => cacheKey,
26
+ getInternalBody: () => getInternalBody
26
27
  });
27
28
  module.exports = __toCommonJS(response_exports);
28
29
 
@@ -40,18 +41,19 @@ var buildOutgoingHttpHeaders = (headers) => {
40
41
  if (cookies.length > 0) {
41
42
  res["set-cookie"] = cookies;
42
43
  }
43
- res["content-type"] ??= "text/plain;charset=UTF-8";
44
+ res["content-type"] ??= "text/plain; charset=UTF-8";
44
45
  return res;
45
46
  };
46
47
 
47
48
  // src/response.ts
48
49
  var responseCache = Symbol("responseCache");
50
+ var getResponseCache = Symbol("getResponseCache");
49
51
  var cacheKey = Symbol("cache");
50
52
  var GlobalResponse = global.Response;
51
53
  var Response = class _Response {
52
54
  #body;
53
55
  #init;
54
- get cache() {
56
+ [getResponseCache]() {
55
57
  delete this[cacheKey];
56
58
  return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
57
59
  }
@@ -61,7 +63,7 @@ var Response = class _Response {
61
63
  const cachedGlobalResponse = init[responseCache];
62
64
  if (cachedGlobalResponse) {
63
65
  this.#init = cachedGlobalResponse;
64
- this.cache;
66
+ this[getResponseCache]();
65
67
  return;
66
68
  } else {
67
69
  this.#init = init.#init;
@@ -70,7 +72,7 @@ var Response = class _Response {
70
72
  this.#init = init;
71
73
  }
72
74
  if (typeof body === "string" || body instanceof ReadableStream) {
73
- let headers = init?.headers || { "content-type": "text/plain;charset=UTF-8" };
75
+ let headers = init?.headers || { "content-type": "text/plain; charset=UTF-8" };
74
76
  if (headers instanceof Headers) {
75
77
  headers = buildOutgoingHttpHeaders(headers);
76
78
  }
@@ -93,14 +95,14 @@ var Response = class _Response {
93
95
  ].forEach((k) => {
94
96
  Object.defineProperty(Response.prototype, k, {
95
97
  get() {
96
- return this.cache[k];
98
+ return this[getResponseCache]()[k];
97
99
  }
98
100
  });
99
101
  });
100
102
  ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
101
103
  Object.defineProperty(Response.prototype, k, {
102
104
  value: function() {
103
- return this.cache[k]();
105
+ return this[getResponseCache]()[k]();
104
106
  }
105
107
  });
106
108
  });
@@ -109,9 +111,26 @@ Object.setPrototypeOf(Response.prototype, GlobalResponse.prototype);
109
111
  Object.defineProperty(global, "Response", {
110
112
  value: Response
111
113
  });
114
+ var stateKey = Reflect.ownKeys(new GlobalResponse()).find(
115
+ (k) => typeof k === "symbol" && k.toString() === "Symbol(state)"
116
+ );
117
+ if (!stateKey) {
118
+ console.warn("Failed to find Response internal state key");
119
+ }
120
+ function getInternalBody(response) {
121
+ if (!stateKey) {
122
+ return;
123
+ }
124
+ if (response instanceof Response) {
125
+ response = response[getResponseCache]();
126
+ }
127
+ const state = response[stateKey];
128
+ return state && state.body || void 0;
129
+ }
112
130
  // Annotate the CommonJS export names for ESM import in node:
113
131
  0 && (module.exports = {
114
132
  GlobalResponse,
115
133
  Response,
116
- cacheKey
134
+ cacheKey,
135
+ getInternalBody
117
136
  });
package/dist/response.mjs CHANGED
@@ -12,18 +12,19 @@ var buildOutgoingHttpHeaders = (headers) => {
12
12
  if (cookies.length > 0) {
13
13
  res["set-cookie"] = cookies;
14
14
  }
15
- res["content-type"] ??= "text/plain;charset=UTF-8";
15
+ res["content-type"] ??= "text/plain; charset=UTF-8";
16
16
  return res;
17
17
  };
18
18
 
19
19
  // src/response.ts
20
20
  var responseCache = Symbol("responseCache");
21
+ var getResponseCache = Symbol("getResponseCache");
21
22
  var cacheKey = Symbol("cache");
22
23
  var GlobalResponse = global.Response;
23
24
  var Response = class _Response {
24
25
  #body;
25
26
  #init;
26
- get cache() {
27
+ [getResponseCache]() {
27
28
  delete this[cacheKey];
28
29
  return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
29
30
  }
@@ -33,7 +34,7 @@ var Response = class _Response {
33
34
  const cachedGlobalResponse = init[responseCache];
34
35
  if (cachedGlobalResponse) {
35
36
  this.#init = cachedGlobalResponse;
36
- this.cache;
37
+ this[getResponseCache]();
37
38
  return;
38
39
  } else {
39
40
  this.#init = init.#init;
@@ -42,7 +43,7 @@ var Response = class _Response {
42
43
  this.#init = init;
43
44
  }
44
45
  if (typeof body === "string" || body instanceof ReadableStream) {
45
- let headers = init?.headers || { "content-type": "text/plain;charset=UTF-8" };
46
+ let headers = init?.headers || { "content-type": "text/plain; charset=UTF-8" };
46
47
  if (headers instanceof Headers) {
47
48
  headers = buildOutgoingHttpHeaders(headers);
48
49
  }
@@ -65,14 +66,14 @@ var Response = class _Response {
65
66
  ].forEach((k) => {
66
67
  Object.defineProperty(Response.prototype, k, {
67
68
  get() {
68
- return this.cache[k];
69
+ return this[getResponseCache]()[k];
69
70
  }
70
71
  });
71
72
  });
72
73
  ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
73
74
  Object.defineProperty(Response.prototype, k, {
74
75
  value: function() {
75
- return this.cache[k]();
76
+ return this[getResponseCache]()[k]();
76
77
  }
77
78
  });
78
79
  });
@@ -81,8 +82,25 @@ Object.setPrototypeOf(Response.prototype, GlobalResponse.prototype);
81
82
  Object.defineProperty(global, "Response", {
82
83
  value: Response
83
84
  });
85
+ var stateKey = Reflect.ownKeys(new GlobalResponse()).find(
86
+ (k) => typeof k === "symbol" && k.toString() === "Symbol(state)"
87
+ );
88
+ if (!stateKey) {
89
+ console.warn("Failed to find Response internal state key");
90
+ }
91
+ function getInternalBody(response) {
92
+ if (!stateKey) {
93
+ return;
94
+ }
95
+ if (response instanceof Response) {
96
+ response = response[getResponseCache]();
97
+ }
98
+ const state = response[stateKey];
99
+ return state && state.body || void 0;
100
+ }
84
101
  export {
85
102
  GlobalResponse,
86
103
  Response,
87
- cacheKey
104
+ cacheKey,
105
+ getInternalBody
88
106
  };
@@ -28,8 +28,9 @@ var import_fs = require("fs");
28
28
  // node_modules/hono/dist/utils/filepath.js
29
29
  var getFilePath = (options) => {
30
30
  let filename = options.filename;
31
- if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename))
31
+ if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename)) {
32
32
  return;
33
+ }
33
34
  let root = options.root || "";
34
35
  const defaultDocument = options.defaultDocument || "index.html";
35
36
  if (filename.endsWith("/")) {
@@ -46,34 +47,27 @@ var getFilePath = (options) => {
46
47
  };
47
48
 
48
49
  // node_modules/hono/dist/utils/mime.js
49
- var getMimeType = (filename) => {
50
+ var getMimeType = (filename, mimes = baseMimes) => {
50
51
  const regexp = /\.([a-zA-Z0-9]+?)$/;
51
52
  const match = filename.match(regexp);
52
- if (!match)
53
+ if (!match) {
53
54
  return;
55
+ }
54
56
  let mimeType = mimes[match[1]];
55
57
  if (mimeType && mimeType.startsWith("text") || mimeType === "application/json") {
56
58
  mimeType += "; charset=utf-8";
57
59
  }
58
60
  return mimeType;
59
61
  };
60
- var mimes = {
62
+ var baseMimes = {
61
63
  aac: "audio/aac",
62
- abw: "application/x-abiword",
63
- arc: "application/x-freearc",
64
64
  avi: "video/x-msvideo",
65
65
  avif: "image/avif",
66
66
  av1: "video/av1",
67
- azw: "application/vnd.amazon.ebook",
68
67
  bin: "application/octet-stream",
69
68
  bmp: "image/bmp",
70
- bz: "application/x-bzip",
71
- bz2: "application/x-bzip2",
72
- csh: "application/x-csh",
73
69
  css: "text/css",
74
70
  csv: "text/csv",
75
- doc: "application/msword",
76
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
77
71
  eot: "application/vnd.ms-fontobject",
78
72
  epub: "application/epub+zip",
79
73
  gif: "image/gif",
@@ -82,7 +76,6 @@ var mimes = {
82
76
  html: "text/html",
83
77
  ico: "image/x-icon",
84
78
  ics: "text/calendar",
85
- jar: "application/java-archive",
86
79
  jpeg: "image/jpeg",
87
80
  jpg: "image/jpeg",
88
81
  js: "text/javascript",
@@ -95,31 +88,20 @@ var mimes = {
95
88
  mp3: "audio/mpeg",
96
89
  mp4: "video/mp4",
97
90
  mpeg: "video/mpeg",
98
- mpkg: "application/vnd.apple.installer+xml",
99
- odp: "application/vnd.oasis.opendocument.presentation",
100
- ods: "application/vnd.oasis.opendocument.spreadsheet",
101
- odt: "application/vnd.oasis.opendocument.text",
102
91
  oga: "audio/ogg",
103
92
  ogv: "video/ogg",
104
93
  ogx: "application/ogg",
105
94
  opus: "audio/opus",
106
95
  otf: "font/otf",
107
96
  pdf: "application/pdf",
108
- php: "application/php",
109
97
  png: "image/png",
110
- ppt: "application/vnd.ms-powerpoint",
111
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
112
98
  rtf: "application/rtf",
113
- sh: "application/x-sh",
114
99
  svg: "image/svg+xml",
115
- swf: "application/x-shockwave-flash",
116
- tar: "application/x-tar",
117
100
  tif: "image/tiff",
118
101
  tiff: "image/tiff",
119
102
  ts: "video/mp2t",
120
103
  ttf: "font/ttf",
121
104
  txt: "text/plain",
122
- vsd: "application/vnd.visio",
123
105
  wasm: "application/wasm",
124
106
  webm: "video/webm",
125
107
  weba: "audio/webm",
@@ -127,14 +109,10 @@ var mimes = {
127
109
  woff: "font/woff",
128
110
  woff2: "font/woff2",
129
111
  xhtml: "application/xhtml+xml",
130
- xls: "application/vnd.ms-excel",
131
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
132
112
  xml: "application/xml",
133
- xul: "application/vnd.mozilla.xul+xml",
134
113
  zip: "application/zip",
135
114
  "3gp": "video/3gpp",
136
115
  "3g2": "video/3gpp2",
137
- "7z": "application/x-7z-compressed",
138
116
  gltf: "model/gltf+json",
139
117
  glb: "model/gltf-binary"
140
118
  };
@@ -4,8 +4,9 @@ import { createReadStream, existsSync, lstatSync } from "fs";
4
4
  // node_modules/hono/dist/utils/filepath.js
5
5
  var getFilePath = (options) => {
6
6
  let filename = options.filename;
7
- if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename))
7
+ if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename)) {
8
8
  return;
9
+ }
9
10
  let root = options.root || "";
10
11
  const defaultDocument = options.defaultDocument || "index.html";
11
12
  if (filename.endsWith("/")) {
@@ -22,34 +23,27 @@ var getFilePath = (options) => {
22
23
  };
23
24
 
24
25
  // node_modules/hono/dist/utils/mime.js
25
- var getMimeType = (filename) => {
26
+ var getMimeType = (filename, mimes = baseMimes) => {
26
27
  const regexp = /\.([a-zA-Z0-9]+?)$/;
27
28
  const match = filename.match(regexp);
28
- if (!match)
29
+ if (!match) {
29
30
  return;
31
+ }
30
32
  let mimeType = mimes[match[1]];
31
33
  if (mimeType && mimeType.startsWith("text") || mimeType === "application/json") {
32
34
  mimeType += "; charset=utf-8";
33
35
  }
34
36
  return mimeType;
35
37
  };
36
- var mimes = {
38
+ var baseMimes = {
37
39
  aac: "audio/aac",
38
- abw: "application/x-abiword",
39
- arc: "application/x-freearc",
40
40
  avi: "video/x-msvideo",
41
41
  avif: "image/avif",
42
42
  av1: "video/av1",
43
- azw: "application/vnd.amazon.ebook",
44
43
  bin: "application/octet-stream",
45
44
  bmp: "image/bmp",
46
- bz: "application/x-bzip",
47
- bz2: "application/x-bzip2",
48
- csh: "application/x-csh",
49
45
  css: "text/css",
50
46
  csv: "text/csv",
51
- doc: "application/msword",
52
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
53
47
  eot: "application/vnd.ms-fontobject",
54
48
  epub: "application/epub+zip",
55
49
  gif: "image/gif",
@@ -58,7 +52,6 @@ var mimes = {
58
52
  html: "text/html",
59
53
  ico: "image/x-icon",
60
54
  ics: "text/calendar",
61
- jar: "application/java-archive",
62
55
  jpeg: "image/jpeg",
63
56
  jpg: "image/jpeg",
64
57
  js: "text/javascript",
@@ -71,31 +64,20 @@ var mimes = {
71
64
  mp3: "audio/mpeg",
72
65
  mp4: "video/mp4",
73
66
  mpeg: "video/mpeg",
74
- mpkg: "application/vnd.apple.installer+xml",
75
- odp: "application/vnd.oasis.opendocument.presentation",
76
- ods: "application/vnd.oasis.opendocument.spreadsheet",
77
- odt: "application/vnd.oasis.opendocument.text",
78
67
  oga: "audio/ogg",
79
68
  ogv: "video/ogg",
80
69
  ogx: "application/ogg",
81
70
  opus: "audio/opus",
82
71
  otf: "font/otf",
83
72
  pdf: "application/pdf",
84
- php: "application/php",
85
73
  png: "image/png",
86
- ppt: "application/vnd.ms-powerpoint",
87
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
88
74
  rtf: "application/rtf",
89
- sh: "application/x-sh",
90
75
  svg: "image/svg+xml",
91
- swf: "application/x-shockwave-flash",
92
- tar: "application/x-tar",
93
76
  tif: "image/tiff",
94
77
  tiff: "image/tiff",
95
78
  ts: "video/mp2t",
96
79
  ttf: "font/ttf",
97
80
  txt: "text/plain",
98
- vsd: "application/vnd.visio",
99
81
  wasm: "application/wasm",
100
82
  webm: "video/webm",
101
83
  weba: "audio/webm",
@@ -103,14 +85,10 @@ var mimes = {
103
85
  woff: "font/woff",
104
86
  woff2: "font/woff2",
105
87
  xhtml: "application/xhtml+xml",
106
- xls: "application/vnd.ms-excel",
107
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
108
88
  xml: "application/xml",
109
- xul: "application/vnd.mozilla.xul+xml",
110
89
  zip: "application/zip",
111
90
  "3gp": "video/3gpp",
112
91
  "3g2": "video/3gpp2",
113
- "7z": "application/x-7z-compressed",
114
92
  gltf: "model/gltf+json",
115
93
  glb: "model/gltf-binary"
116
94
  };
package/dist/server.js CHANGED
@@ -55,7 +55,7 @@ var Request = class extends GlobalRequest {
55
55
  Object.defineProperty(global, "Request", {
56
56
  value: Request
57
57
  });
58
- var newRequestFromIncoming = (method, url, incoming) => {
58
+ var newRequestFromIncoming = (method, url, incoming, abortController) => {
59
59
  const headerRecord = [];
60
60
  const rawHeaders = incoming.rawHeaders;
61
61
  for (let i = 0; i < rawHeaders.length; i += 2) {
@@ -67,7 +67,8 @@ var newRequestFromIncoming = (method, url, incoming) => {
67
67
  }
68
68
  const init = {
69
69
  method,
70
- headers: headerRecord
70
+ headers: headerRecord,
71
+ signal: abortController.signal
71
72
  };
72
73
  if (!(method === "GET" || method === "HEAD")) {
73
74
  init.body = import_node_stream.Readable.toWeb(incoming);
@@ -78,6 +79,8 @@ var getRequestCache = Symbol("getRequestCache");
78
79
  var requestCache = Symbol("requestCache");
79
80
  var incomingKey = Symbol("incomingKey");
80
81
  var urlKey = Symbol("urlKey");
82
+ var abortControllerKey = Symbol("abortControllerKey");
83
+ var getAbortController = Symbol("getAbortController");
81
84
  var requestPrototype = {
82
85
  get method() {
83
86
  return this[incomingKey].method || "GET";
@@ -85,11 +88,17 @@ var requestPrototype = {
85
88
  get url() {
86
89
  return this[urlKey];
87
90
  },
91
+ [getAbortController]() {
92
+ this[getRequestCache]();
93
+ return this[abortControllerKey];
94
+ },
88
95
  [getRequestCache]() {
96
+ this[abortControllerKey] ||= new AbortController();
89
97
  return this[requestCache] ||= newRequestFromIncoming(
90
98
  this.method,
91
99
  this[urlKey],
92
- this[incomingKey]
100
+ this[incomingKey],
101
+ this[abortControllerKey]
93
102
  );
94
103
  }
95
104
  };
@@ -183,18 +192,19 @@ var buildOutgoingHttpHeaders = (headers) => {
183
192
  if (cookies.length > 0) {
184
193
  res["set-cookie"] = cookies;
185
194
  }
186
- res["content-type"] ??= "text/plain;charset=UTF-8";
195
+ res["content-type"] ??= "text/plain; charset=UTF-8";
187
196
  return res;
188
197
  };
189
198
 
190
199
  // src/response.ts
191
200
  var responseCache = Symbol("responseCache");
201
+ var getResponseCache = Symbol("getResponseCache");
192
202
  var cacheKey = Symbol("cache");
193
203
  var GlobalResponse = global.Response;
194
204
  var Response2 = class _Response {
195
205
  #body;
196
206
  #init;
197
- get cache() {
207
+ [getResponseCache]() {
198
208
  delete this[cacheKey];
199
209
  return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
200
210
  }
@@ -204,7 +214,7 @@ var Response2 = class _Response {
204
214
  const cachedGlobalResponse = init[responseCache];
205
215
  if (cachedGlobalResponse) {
206
216
  this.#init = cachedGlobalResponse;
207
- this.cache;
217
+ this[getResponseCache]();
208
218
  return;
209
219
  } else {
210
220
  this.#init = init.#init;
@@ -213,7 +223,7 @@ var Response2 = class _Response {
213
223
  this.#init = init;
214
224
  }
215
225
  if (typeof body === "string" || body instanceof ReadableStream) {
216
- let headers = init?.headers || { "content-type": "text/plain;charset=UTF-8" };
226
+ let headers = init?.headers || { "content-type": "text/plain; charset=UTF-8" };
217
227
  if (headers instanceof Headers) {
218
228
  headers = buildOutgoingHttpHeaders(headers);
219
229
  }
@@ -236,14 +246,14 @@ var Response2 = class _Response {
236
246
  ].forEach((k) => {
237
247
  Object.defineProperty(Response2.prototype, k, {
238
248
  get() {
239
- return this.cache[k];
249
+ return this[getResponseCache]()[k];
240
250
  }
241
251
  });
242
252
  });
243
253
  ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
244
254
  Object.defineProperty(Response2.prototype, k, {
245
255
  value: function() {
246
- return this.cache[k]();
256
+ return this[getResponseCache]()[k]();
247
257
  }
248
258
  });
249
259
  });
@@ -252,6 +262,22 @@ Object.setPrototypeOf(Response2.prototype, GlobalResponse.prototype);
252
262
  Object.defineProperty(global, "Response", {
253
263
  value: Response2
254
264
  });
265
+ var stateKey = Reflect.ownKeys(new GlobalResponse()).find(
266
+ (k) => typeof k === "symbol" && k.toString() === "Symbol(state)"
267
+ );
268
+ if (!stateKey) {
269
+ console.warn("Failed to find Response internal state key");
270
+ }
271
+ function getInternalBody(response) {
272
+ if (!stateKey) {
273
+ return;
274
+ }
275
+ if (response instanceof Response2) {
276
+ response = response[getResponseCache]();
277
+ }
278
+ const state = response[stateKey];
279
+ return state && state.body || void 0;
280
+ }
255
281
 
256
282
  // src/globals.ts
257
283
  var import_node_crypto = __toESM(require("crypto"));
@@ -301,48 +327,71 @@ var responseViaCache = (res, outgoing) => {
301
327
  );
302
328
  }
303
329
  };
304
- var responseViaResponseObject = async (res, outgoing) => {
305
- res = res instanceof Promise ? await res.catch(handleFetchError) : res;
306
- try {
307
- const isCached = cacheKey in res;
308
- if (isCached) {
309
- return responseViaCache(res, outgoing);
330
+ var responseViaResponseObject = async (res, outgoing, options = {}) => {
331
+ if (res instanceof Promise) {
332
+ if (options.errorHandler) {
333
+ try {
334
+ res = await res;
335
+ } catch (err) {
336
+ const errRes = await options.errorHandler(err);
337
+ if (!errRes) {
338
+ return;
339
+ }
340
+ res = errRes;
341
+ }
342
+ } else {
343
+ res = await res.catch(handleFetchError);
310
344
  }
311
- } catch (e) {
312
- return handleResponseError(e, outgoing);
345
+ }
346
+ if (cacheKey in res) {
347
+ return responseViaCache(res, outgoing);
313
348
  }
314
349
  const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
315
- if (res.body) {
316
- try {
317
- const {
318
- "transfer-encoding": transferEncoding,
319
- "content-encoding": contentEncoding,
320
- "content-length": contentLength,
321
- "x-accel-buffering": accelBuffering,
322
- "content-type": contentType
323
- } = resHeaderRecord;
324
- if (transferEncoding || contentEncoding || contentLength || // nginx buffering variant
325
- accelBuffering && regBuffer.test(accelBuffering) || !regContentType.test(contentType)) {
326
- outgoing.writeHead(res.status, resHeaderRecord);
327
- await writeFromReadableStream(res.body, outgoing);
328
- } else {
329
- const buffer = await res.arrayBuffer();
330
- resHeaderRecord["content-length"] = buffer.byteLength;
331
- outgoing.writeHead(res.status, resHeaderRecord);
332
- outgoing.end(new Uint8Array(buffer));
333
- }
334
- } catch (e) {
335
- handleResponseError(e, outgoing);
350
+ const internalBody = getInternalBody(res);
351
+ if (internalBody) {
352
+ if (internalBody.length) {
353
+ resHeaderRecord["content-length"] = internalBody.length;
354
+ }
355
+ outgoing.writeHead(res.status, resHeaderRecord);
356
+ if (typeof internalBody.source === "string" || internalBody.source instanceof Uint8Array) {
357
+ outgoing.end(internalBody.source);
358
+ } else if (internalBody.source instanceof Blob) {
359
+ outgoing.end(new Uint8Array(await internalBody.source.arrayBuffer()));
360
+ } else {
361
+ await writeFromReadableStream(internalBody.stream, outgoing);
362
+ }
363
+ } else if (res.body) {
364
+ const {
365
+ "transfer-encoding": transferEncoding,
366
+ "content-encoding": contentEncoding,
367
+ "content-length": contentLength,
368
+ "x-accel-buffering": accelBuffering,
369
+ "content-type": contentType
370
+ } = resHeaderRecord;
371
+ if (transferEncoding || contentEncoding || contentLength || // nginx buffering variant
372
+ accelBuffering && regBuffer.test(accelBuffering) || !regContentType.test(contentType)) {
373
+ outgoing.writeHead(res.status, resHeaderRecord);
374
+ await writeFromReadableStream(res.body, outgoing);
375
+ } else {
376
+ const buffer = await res.arrayBuffer();
377
+ resHeaderRecord["content-length"] = buffer.byteLength;
378
+ outgoing.writeHead(res.status, resHeaderRecord);
379
+ outgoing.end(new Uint8Array(buffer));
336
380
  }
337
381
  } else {
338
382
  outgoing.writeHead(res.status, resHeaderRecord);
339
383
  outgoing.end();
340
384
  }
341
385
  };
342
- var getRequestListener = (fetchCallback) => {
343
- return (incoming, outgoing) => {
386
+ var getRequestListener = (fetchCallback, options = {}) => {
387
+ return async (incoming, outgoing) => {
344
388
  let res;
345
389
  const req = newRequest(incoming);
390
+ outgoing.on("close", () => {
391
+ if (incoming.destroyed) {
392
+ req[getAbortController]().abort();
393
+ }
394
+ });
346
395
  try {
347
396
  res = fetchCallback(req, { incoming, outgoing });
348
397
  if (cacheKey in res) {
@@ -350,12 +399,23 @@ var getRequestListener = (fetchCallback) => {
350
399
  }
351
400
  } catch (e) {
352
401
  if (!res) {
353
- res = handleFetchError(e);
402
+ if (options.errorHandler) {
403
+ res = await options.errorHandler(e);
404
+ if (!res) {
405
+ return;
406
+ }
407
+ } else {
408
+ res = handleFetchError(e);
409
+ }
354
410
  } else {
355
411
  return handleResponseError(e, outgoing);
356
412
  }
357
413
  }
358
- return responseViaResponseObject(res, outgoing);
414
+ try {
415
+ return responseViaResponseObject(res, outgoing, options);
416
+ } catch (e) {
417
+ return handleResponseError(e, outgoing);
418
+ }
359
419
  };
360
420
  };
361
421