fulmine.js 5.21.5 → 5.21.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fulmine.js",
3
- "version": "5.21.5",
3
+ "version": "5.21.6",
4
4
  "description": "Drop-in Express 5 replacement on uWebSockets.js, up to 20x faster. Same API, your middleware and framework keep working.",
5
5
  "main": "src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -17,27 +17,6 @@ See the License for the specific language governing permissions and
17
17
  limitations under the License.
18
18
  */
19
19
 
20
- // A project on pnpm owns the uWebSockets.js dependency itself, see `npx fulmine.js pnpm`, so the
21
- // one installed can drift from the one this package pins and was tested against. Said once, at
22
- // require time, where it reaches every deployment rather than only the ones that run verify.
23
- {
24
- const pinned = /#v?([\d.]+)$/.exec(require("../package.json").dependencies["uWebSockets.js"])?.[1];
25
- /** @type {string|undefined} */
26
- let installed;
27
- try {
28
- // its exports map does not expose package.json, so it is read beside the entry point
29
- const beside = require("path").join(require.resolve("uWebSockets.js"), "..", "package.json");
30
- installed = JSON.parse(require("fs").readFileSync(beside, "utf8")).version;
31
- } catch {
32
- // nothing to compare against, which is not worth a warning of its own
33
- }
34
- if (pinned && installed && installed !== pinned) {
35
- console.warn(
36
- `fulmine.js: uWebSockets.js ${installed} is installed, this version was tested with ${pinned}.\n` +
37
- " On pnpm the pin is the project's own: `npx fulmine.js pnpm` writes the tested one."
38
- );
39
- }
40
- }
41
20
  const Application = require("./application.js");
42
21
  const Router = require("./router.js");
43
22
  const Route = require("./route.js");
package/src/node-shim.js CHANGED
@@ -15,14 +15,36 @@ limitations under the License.
15
15
  */
16
16
 
17
17
  // A uWS-shaped request and response over node's own, for supertest and http.createServer(app).
18
- // Not fast and not meant to be. Where the models disagree node's gives way: cork only runs its
19
- // callback, a status is kept until the first byte of body
18
+ // Not the fast path, app.listen() is, but about 1.5x Express on a hello world on Linux. Where the
19
+ // models disagree node's gives way: cork only runs its callback, a status is kept until the first
20
+ // byte of body
20
21
 
21
22
  const { IncomingMessage } = require("http");
23
+ const { HEADER_NAME_BUF, HEADER_VALUE_BUF } = require("./response-utils.js");
22
24
 
23
25
  /** What µWS returns for an address nobody declared. */
24
26
  const emptyAddress = new ArrayBuffer(0);
25
27
 
28
+ // The strings behind the Buffers writeHeaders hands over for the recurring names and values.
29
+ // Decoded on every response they gave node a new string to check, lowercase and hash each time
30
+ /** @type {Map<unknown, string>} */
31
+ const HEADER_TEXT = new Map();
32
+ for (const cache of [HEADER_NAME_BUF, HEADER_VALUE_BUF]) {
33
+ for (const text in cache) {
34
+ HEADER_TEXT.set(cache[text], text);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * A header name or value as node's appendHeader takes it.
40
+ *
41
+ * @param {unknown} part
42
+ * @returns {string}
43
+ */
44
+ function headerText(part) {
45
+ return typeof part === "string" ? part : (HEADER_TEXT.get(part) ?? String(part));
46
+ }
47
+
26
48
  /**
27
49
  * An IP address as the four or sixteen bytes uWS hands over; unreadable comes back empty.
28
50
  *
@@ -93,6 +115,22 @@ function toArrayBuffer(chunk) {
93
115
  return /** @type {ArrayBuffer} */ (buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength));
94
116
  }
95
117
 
118
+ /**
119
+ * A body chunk as node's write takes it: a string goes as it is and node encodes it once, with
120
+ * the head, anything else is copied as toArrayBuffer does.
121
+ *
122
+ * @param {ArrayBuffer|Buffer|string} chunk
123
+ * @returns {Buffer|string}
124
+ */
125
+ function toNodeChunk(chunk) {
126
+ return typeof chunk === "string" ? chunk : Buffer.from(toArrayBuffer(chunk));
127
+ }
128
+
129
+ /** @param {Buffer|string} chunk */
130
+ function byteLength(chunk) {
131
+ return typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
132
+ }
133
+
96
134
  /** uWS's HttpRequest over node's IncomingMessage. */
97
135
  class NodeHttpRequest {
98
136
  /** @type {import("http").IncomingMessage} */
@@ -241,16 +279,16 @@ class NodeHttpResponse {
241
279
  writeHeader(key, value) {
242
280
  if (!this._nodeRes.headersSent) {
243
281
  // writeHeaders hands the recurring names and values over as Buffers
244
- this._nodeRes.appendHeader(String(key), String(value));
282
+ this._nodeRes.appendHeader(headerText(key), headerText(value));
245
283
  }
246
284
  return this;
247
285
  }
248
286
 
249
287
  /** @param {ArrayBuffer|Buffer|string} chunk @returns {boolean} false when the socket is full */
250
288
  write(chunk) {
251
- const buffer = Buffer.from(toArrayBuffer(chunk));
252
- this._offset += buffer.length;
253
- return this._nodeRes.write(buffer);
289
+ const data = toNodeChunk(chunk);
290
+ this._offset += byteLength(data);
291
+ return this._nodeRes.write(data);
254
292
  }
255
293
 
256
294
  /** @param {ArrayBuffer|Buffer|string} [body] */
@@ -262,9 +300,9 @@ class NodeHttpResponse {
262
300
  this._nodeRes.end();
263
301
  return this;
264
302
  }
265
- const buffer = Buffer.from(toArrayBuffer(body));
266
- this._offset += buffer.length;
267
- this._nodeRes.end(buffer);
303
+ const data = toNodeChunk(body);
304
+ this._offset += byteLength(data);
305
+ this._nodeRes.end(data);
268
306
  return this;
269
307
  }
270
308
 
@@ -298,9 +336,9 @@ class NodeHttpResponse {
298
336
  if (!this._nodeRes.headersSent && !this._nodeRes.hasHeader("Content-Length")) {
299
337
  this._nodeRes.setHeader("Content-Length", String(totalSize));
300
338
  }
301
- const buffer = Buffer.from(toArrayBuffer(chunk));
302
- const ok = this._nodeRes.write(buffer);
303
- this._offset += buffer.length;
339
+ const data = toNodeChunk(chunk);
340
+ const ok = this._nodeRes.write(data);
341
+ this._offset += byteLength(data);
304
342
  const done = this._offset >= totalSize;
305
343
  if (done) {
306
344
  this._nodeRes.end();
package/src/uws.js CHANGED
@@ -21,6 +21,30 @@ limitations under the License.
21
21
  /** @type {any} */
22
22
  let uWS;
23
23
 
24
+ /**
25
+ * A project on pnpm owns the uWebSockets.js dependency itself, see `npx fulmine.js pnpm`, so the
26
+ * one installed can drift from the one this package pins and was tested against. Said once, when
27
+ * it is loaded: an app served through node's http never loads it and has nothing to hear.
28
+ */
29
+ function warnOnDrift() {
30
+ const pinned = /#v?([\d.]+)$/.exec(require("../package.json").dependencies["uWebSockets.js"])?.[1];
31
+ /** @type {string|undefined} */
32
+ let installed;
33
+ try {
34
+ // its exports map does not expose package.json, so it is read beside the entry point
35
+ const beside = require("path").join(require.resolve("uWebSockets.js"), "..", "package.json");
36
+ installed = JSON.parse(require("fs").readFileSync(beside, "utf8")).version;
37
+ } catch {
38
+ // nothing to compare against, which is not worth a warning of its own
39
+ }
40
+ if (pinned && installed && installed !== pinned) {
41
+ console.warn(
42
+ `fulmine.js: uWebSockets.js ${installed} is installed, this version was tested with ${pinned}.\n` +
43
+ " On pnpm the pin is the project's own: `npx fulmine.js pnpm` writes the tested one."
44
+ );
45
+ }
46
+ }
47
+
24
48
  /**
25
49
  * The µWS module. H3App, DeclarativeResponse and _cfg exist at runtime but are missing from the
26
50
  * .d.ts the package ships, so it is handed back loosely typed.
@@ -29,6 +53,7 @@ let uWS;
29
53
  */
30
54
  function loadUWS() {
31
55
  if (uWS === undefined) {
56
+ warnOnDrift();
32
57
  uWS = /** @type {any} */ (require("uWebSockets.js"));
33
58
  try {
34
59
  // disable Uwebsockets header