@nxtedition/lib 26.6.0 → 26.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/http.js +18 -1
- package/package.json +1 -1
- 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) {
|
|
@@ -357,6 +364,16 @@ export async function requestMiddleware(ctx, next) {
|
|
|
357
364
|
stats.pending--
|
|
358
365
|
}
|
|
359
366
|
|
|
367
|
+
if (ctx[kPendingIndex] !== -1) {
|
|
368
|
+
const idx = ctx[kPendingIndex]
|
|
369
|
+
const tmp = pending.pop()
|
|
370
|
+
if (tmp !== ctx) {
|
|
371
|
+
pending[idx] = tmp
|
|
372
|
+
pending[idx][kPendingIndex] = idx
|
|
373
|
+
tmp[kPendingIndex] = -1
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
360
377
|
if (res.writableEnded || res.destroyed || res.stream?.destroyed) {
|
|
361
378
|
// Do nothing..
|
|
362
379
|
} else {
|
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
|
|
|
@@ -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
|
+
}
|