@nxtedition/lib 26.6.0 → 26.7.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.
Files changed (3) hide show
  1. package/http.js +18 -1
  2. package/package.json +1 -1
  3. package/shared.js +125 -1
package/http.js CHANGED
@@ -35,6 +35,9 @@ function onTimeout() {
35
35
  this.destroy((timeoutError ??= new createError.RequestTimeout()))
36
36
  }
37
37
 
38
+ const pending = (globalThis.__nxt_lib_http_pending = [])
39
+ const kPendingIndex = Symbol('pendingIndex')
40
+
38
41
  // TODO (fix): Make custom ServerRequest class with the properties
39
42
  // that is currently deprecated by Context.
40
43
 
@@ -45,7 +48,9 @@ export class Context {
45
48
  #ac
46
49
  #logger
47
50
  #query
48
- #target
51
+ #target;
52
+
53
+ [kPendingIndex] = -1
49
54
 
50
55
  constructor(req, res, logger) {
51
56
  assert(req)
@@ -210,6 +215,8 @@ export async function requestMiddleware(ctx, next) {
210
215
  if (stats?.pending != null) {
211
216
  stats.pending++
212
217
  }
218
+
219
+ ctx[kPendingIndex] = pending.push(ctx) - 1
213
220
  try {
214
221
  const isHealthcheck = req.url === '/healthcheck' || req.url === '/_up'
215
222
  if (!isHealthcheck) {
@@ -364,6 +371,16 @@ export async function requestMiddleware(ctx, next) {
364
371
  res.destroy()
365
372
  ctx.logger?.warn('request destroyed')
366
373
  }
374
+
375
+ {
376
+ const idx = ctx[kPendingIndex]
377
+ const tmp = pending.pop()
378
+ if (tmp !== ctx) {
379
+ pending[idx] = tmp
380
+ pending[idx][kPendingIndex] = idx
381
+ tmp[kPendingIndex] = -1
382
+ }
383
+ }
367
384
  }
368
385
  }
369
386
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "26.6.0",
3
+ "version": "26.7.0",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",
package/shared.js CHANGED
@@ -1,3 +1,6 @@
1
+ import stream from 'node:stream'
2
+ import assert from 'node:assert'
3
+
1
4
  // By placing the read and write indices far apart (multiples of a common
2
5
  // cache line size, 64 bytes), we prevent "false sharing". This is a
3
6
  // low-level CPU optimization where two cores writing to different variables
@@ -91,12 +94,16 @@ export function reader({ sharedState, sharedBuffer }) {
91
94
  // Instead, we pass a "view" into the shared buffer.
92
95
  data.offset = dataPos
93
96
  data.length = dataLen
94
- next(data)
97
+ const cont = next(data)
95
98
 
96
99
  if (readPos === writePos) {
97
100
  // If we reach the end of the buffer, we must re-check the writer's position.
98
101
  writePos = Atomics.load(state, WRITE_INDEX) | 0
99
102
  }
103
+
104
+ if (cont === false) {
105
+ break
106
+ }
100
107
  }
101
108
  }
102
109
 
@@ -345,6 +352,8 @@ export function writer({ sharedState, sharedBuffer }, { yield: onYield, logger }
345
352
  if (writePos === readPos) {
346
353
  throw new Error(`Write position ${writePos} cannot equal read position ${readPos}`)
347
354
  }
355
+
356
+ return true
348
357
  }
349
358
 
350
359
  function _flush() {
@@ -365,3 +374,118 @@ export function writer({ sharedState, sharedBuffer }, { yield: onYield, logger }
365
374
 
366
375
  return { tryWrite, writeSync, cork }
367
376
  }
377
+
378
+ export class Writable extends stream.Writable {
379
+ #writer
380
+ #retries = 0
381
+ #timeout = null
382
+
383
+ #chunk
384
+ #encoding
385
+ #callback
386
+
387
+ constructor({ state, ...options }) {
388
+ super({ ...options })
389
+ this.#writer = writer(state)
390
+ }
391
+
392
+ _write(chunk, encoding, callback) {
393
+ if (chunk.byteLength === 0) {
394
+ callback(null)
395
+ return
396
+ }
397
+
398
+ assert(!this.#timeout)
399
+
400
+ this.#chunk = chunk
401
+ this.#encoding = encoding
402
+ this.#callback = callback
403
+ this._writeSome()
404
+ }
405
+
406
+ _final(callback) {
407
+ this.#chunk = Buffer.allocUnsafe(0)
408
+ this.#encoding = null
409
+ this.#callback = callback
410
+ this._writeSome()
411
+ }
412
+
413
+ _writeSome = () => {
414
+ this.#timeout = null
415
+
416
+ if (this.#writer.tryWrite(this.#chunk.byteLength, this._doWrite)) {
417
+ const callback = this.#callback
418
+ this.#retries = 0
419
+ this.#chunk = null
420
+ this.#encoding = null
421
+ this.#callback = null
422
+ callback(null)
423
+ } else {
424
+ this.#retries += 1
425
+ this.#timeout = setTimeout(this._writeSome, Math.min(10, this.#retries))
426
+ }
427
+ }
428
+
429
+ _doWrite = (data) => {
430
+ const written =
431
+ typeof this.#chunk === 'string'
432
+ ? data.buffer.write(this.#chunk, data.offset, data.length, this.#encoding)
433
+ : this.#chunk.copy(data.buffer, data.offset, 0, this.#chunk.length)
434
+ return data.offset + written
435
+ }
436
+
437
+ _destroy(err, callback) {
438
+ if (this.#timeout != null) {
439
+ clearTimeout(this.#timeout)
440
+ this.#timeout = null
441
+ }
442
+ callback(err)
443
+ }
444
+ }
445
+
446
+ export class Readable extends stream.Readable {
447
+ #reader
448
+ #retries = 0
449
+ #timeout
450
+
451
+ constructor({ state, ...options }) {
452
+ super(options)
453
+ this.#reader = reader(state)
454
+ }
455
+
456
+ _read() {
457
+ assert(!this.#timeout)
458
+
459
+ this._readSome()
460
+ }
461
+
462
+ _readSome = () => {
463
+ this.#timeout = null
464
+
465
+ const count = this.#reader.readSome((data) => {
466
+ if (data.length === 0) {
467
+ this.push(null)
468
+ return false
469
+ }
470
+
471
+ const chunk = Buffer.allocUnsafe(data.length)
472
+ data.buffer.copy(chunk, 0, data.offset, data.offset + data.length)
473
+ return this.push(chunk)
474
+ })
475
+
476
+ if (count > 0 || this.readableEnded) {
477
+ this.#retries = 0
478
+ } else {
479
+ this.#retries += 1
480
+ this.#timeout = setTimeout(this._readSome, Math.min(10, this.#retries))
481
+ }
482
+ }
483
+
484
+ _destroy(err, callback) {
485
+ if (this.#timeout != null) {
486
+ clearTimeout(this.#timeout)
487
+ this.#timeout = null
488
+ }
489
+ callback(err)
490
+ }
491
+ }