@nxtedition/lib 26.5.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.
- package/http.js +18 -1
- package/package.json +1 -1
- package/shared.js +149 -3
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
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
|
|
|
@@ -296,7 +303,7 @@ export function writer({ sharedState, sharedBuffer }, { yield: onYield, logger }
|
|
|
296
303
|
* @param {number} len The maximum expected length of the payload.
|
|
297
304
|
* @param {({ buffer, view, offset, length }) => number} fn The callback that writes the data.
|
|
298
305
|
*/
|
|
299
|
-
function
|
|
306
|
+
function writeSync(len, fn) {
|
|
300
307
|
if (len < 0) {
|
|
301
308
|
throw new Error(`Length ${len} is negative`)
|
|
302
309
|
}
|
|
@@ -325,6 +332,30 @@ export function writer({ sharedState, sharedBuffer }, { yield: onYield, logger }
|
|
|
325
332
|
}
|
|
326
333
|
}
|
|
327
334
|
|
|
335
|
+
function tryWrite(len, fn) {
|
|
336
|
+
if (len < 0) {
|
|
337
|
+
throw new Error(`Length ${len} is negative`)
|
|
338
|
+
}
|
|
339
|
+
if (len >= 2 ** 31 || len > size - 8) {
|
|
340
|
+
throw new Error(`Length ${len} exceeds maximum allowed size`)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (!_acquire(len)) {
|
|
344
|
+
readPos = Atomics.load(state, READ_INDEX) | 0
|
|
345
|
+
if (!_acquire(len)) {
|
|
346
|
+
return false
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
_write(len, fn)
|
|
351
|
+
|
|
352
|
+
if (writePos === readPos) {
|
|
353
|
+
throw new Error(`Write position ${writePos} cannot equal read position ${readPos}`)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return true
|
|
357
|
+
}
|
|
358
|
+
|
|
328
359
|
function _flush() {
|
|
329
360
|
if (pending > 0) {
|
|
330
361
|
Atomics.store(state, WRITE_INDEX, writePos)
|
|
@@ -341,5 +372,120 @@ export function writer({ sharedState, sharedBuffer }, { yield: onYield, logger }
|
|
|
341
372
|
}
|
|
342
373
|
}
|
|
343
374
|
|
|
344
|
-
return {
|
|
375
|
+
return { tryWrite, writeSync, cork }
|
|
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
|
+
}
|
|
345
491
|
}
|