@nxtedition/lib 26.0.1 → 26.0.3

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 (2) hide show
  1. package/http.js +58 -46
  2. package/package.json +1 -1
package/http.js CHANGED
@@ -154,10 +154,12 @@ export async function upgradeMiddleware(ctx, next) {
154
154
  }
155
155
  })
156
156
 
157
+ let logger = ctx.logger
157
158
  try {
158
159
  const isHealthcheck = req.url === '/healthcheck' || req.url === '/_up'
159
160
  if (!isHealthcheck) {
160
- ctx.logger?.debug({ req }, 'stream started')
161
+ logger = logger?.child({ req })
162
+ logger?.debug('request started')
161
163
  }
162
164
 
163
165
  const thenable = next()
@@ -173,15 +175,15 @@ export async function upgradeMiddleware(ctx, next) {
173
175
  if (isHealthcheck) {
174
176
  // Do nothing...
175
177
  } else if (socket.errored) {
176
- ctx.logger?.error({ err: socket.errored, req, socket, elapsedTime }, 'stream error')
178
+ logger?.error({ err: socket.errored, req, socket, elapsedTime }, 'stream error')
177
179
  } else if (!socket.writableEnded) {
178
- ctx.logger?.debug({ req, socket, elapsedTime }, 'stream aborted')
180
+ logger?.debug({ req, socket, elapsedTime }, 'stream aborted')
179
181
  } else if (socket.statusCode >= 500) {
180
- ctx.logger?.error({ req, socket, elapsedTime }, 'stream error')
182
+ logger?.error({ req, socket, elapsedTime }, 'stream error')
181
183
  } else if (socket.statusCode >= 400) {
182
- ctx.logger?.warn({ req, socket, elapsedTime }, 'stream failed')
184
+ logger?.warn({ req, socket, elapsedTime }, 'stream failed')
183
185
  } else {
184
- ctx.logger?.debug({ req, socket, elapsedTime }, 'stream completed')
186
+ logger?.debug({ req, socket, elapsedTime }, 'stream completed')
185
187
  }
186
188
  } catch (err) {
187
189
  ctx[kAbortController]?.abort(err)
@@ -190,17 +192,17 @@ export async function upgradeMiddleware(ctx, next) {
190
192
  const elapsedTime = performance.now() - startTime
191
193
 
192
194
  if (req.aborted || aborted || (!socket.errored && socket.closed) || err.name === 'AbortError') {
193
- ctx.logger?.debug({ err, req, socket, elapsedTime }, 'stream aborted')
195
+ logger?.debug({ err, req, socket, elapsedTime }, 'stream aborted')
194
196
  } else if (statusCode < 500) {
195
- ctx.logger?.warn({ err, req, socket, elapsedTime }, 'stream failed')
197
+ logger?.warn({ err, req, socket, elapsedTime }, 'stream failed')
196
198
  } else {
197
- ctx.logger?.error({ err, req, socket, elapsedTime }, 'stream error')
199
+ logger?.error({ err, req, socket, elapsedTime }, 'stream error')
198
200
  }
199
201
  socket.destroy(err)
200
202
  } finally {
201
203
  if (!socket.writableEnded && !socket.destroyed) {
202
204
  socket.destroy()
203
- ctx.logger?.warn('socket destroyed')
205
+ logger?.warn('socket destroyed')
204
206
  }
205
207
  }
206
208
  }
@@ -209,20 +211,41 @@ export async function requestMiddleware(ctx, next) {
209
211
  const { req, res } = ctx
210
212
  const startTime = performance.now()
211
213
 
214
+ let logger = ctx.logger
212
215
  try {
213
216
  const isHealthcheck = req.url === '/healthcheck' || req.url === '/_up'
214
217
  if (!isHealthcheck) {
215
- ctx.logger?.debug({ req }, 'request started')
218
+ logger = logger?.child({ req })
219
+ logger?.debug('request started')
216
220
  }
217
221
 
218
222
  if (ctx.id) {
219
223
  res.setHeader('request-id', ctx.id)
220
224
  }
221
225
 
226
+ if (req.httpVersionMajor === 1 && (req.method === 'HEAD' || req.method === 'GET')) {
227
+ // Fast dump where request "has" already emitted all lifecycle events.
228
+ // This avoid a lot of unnecessary overhead otherwise introduced by
229
+ // stream.Readable life cycle rules. The downside is that this will
230
+ // break some servers that read GET bodies.
231
+
232
+ req._dumped = true
233
+ req._readableState.ended = true
234
+ req._readableState.endEmitted = true
235
+ req._readableState.destroyed = true
236
+ req._readableState.closed = true
237
+ req._readableState.closeEmitted = true
238
+
239
+ req._read()
240
+ }
241
+
222
242
  const thenable = next()
223
243
 
224
- if (thenable?.then || res.errored || req.errored) {
244
+ if (!req.destroyed || req.errored) {
225
245
  req.on('error', noop)
246
+ }
247
+
248
+ if (!res.destroyed || res.errored) {
226
249
  res.on('error', noop)
227
250
  }
228
251
 
@@ -230,22 +253,24 @@ export async function requestMiddleware(ctx, next) {
230
253
  await thenable
231
254
  }
232
255
 
233
- assert(res.destroyed || res.writableEnded, 'response not completed')
256
+ if (!res.destroyed || !res.writableEnded) {
257
+ throw new Error('Response not completed')
258
+ }
234
259
 
235
260
  const elapsedTime = performance.now() - startTime
236
261
 
237
262
  if (isHealthcheck) {
238
263
  // Do nothing...
239
264
  } else if (res.errored) {
240
- ctx.logger?.error({ err: res.errored, req, res, elapsedTime }, 'request error')
265
+ logger?.error({ err: res.errored, req, res, elapsedTime }, 'request error')
241
266
  } else if (!res.writableEnded) {
242
- ctx.logger?.debug({ req, res, elapsedTime }, 'request aborted')
267
+ logger?.debug({ req, res, elapsedTime }, 'request aborted')
243
268
  } else if (res.statusCode >= 500) {
244
- ctx.logger?.error({ req, res, elapsedTime }, 'request error')
269
+ logger?.error({ req, res, elapsedTime }, 'request error')
245
270
  } else if (res.statusCode >= 400) {
246
- ctx.logger?.warn({ req, res, elapsedTime }, 'request failed')
271
+ logger?.warn({ req, res, elapsedTime }, 'request failed')
247
272
  } else {
248
- ctx.logger?.debug({ req, res, elapsedTime }, 'request completed')
273
+ logger?.debug({ req, res, elapsedTime }, 'request completed')
249
274
  }
250
275
  } catch (err) {
251
276
  ctx[kAbortController]?.abort(err)
@@ -285,7 +310,7 @@ export async function requestMiddleware(ctx, next) {
285
310
  }
286
311
  }
287
312
  } else if (err.headers != null) {
288
- ctx.logger?.warn({ req, err }, 'invalid headers')
313
+ logger?.warn({ req, err }, 'invalid headers')
289
314
  }
290
315
 
291
316
  if (fp.isPlainObject(err.body)) {
@@ -300,17 +325,17 @@ export async function requestMiddleware(ctx, next) {
300
325
  }
301
326
 
302
327
  if (statusCode < 500) {
303
- ctx.logger?.warn({ req, res, err, elapsedTime }, 'request failed')
328
+ logger?.warn({ res, err, elapsedTime }, 'request failed')
304
329
  } else {
305
- ctx.logger?.error({ req, res, err, elapsedTime }, 'request error')
330
+ logger?.error({ res, err, elapsedTime }, 'request error')
306
331
  }
307
332
  } else {
308
333
  if (req.aborted || (!res.errored && res.closed) || err.name === 'AbortError') {
309
- ctx.logger?.debug({ req, res, err, elapsedTime }, 'request aborted')
334
+ logger?.debug({ res, err, elapsedTime }, 'request aborted')
310
335
  } else if (statusCode < 500) {
311
- ctx.logger?.warn({ req, res, err, elapsedTime }, 'request failed')
336
+ logger?.warn({ res, err, elapsedTime }, 'request failed')
312
337
  } else {
313
- ctx.logger?.error({ req, res, err, elapsedTime }, 'request error')
338
+ logger?.error({ res, err, elapsedTime }, 'request error')
314
339
  }
315
340
  res.destroy(err)
316
341
  }
@@ -319,7 +344,7 @@ export async function requestMiddleware(ctx, next) {
319
344
  // Do nothing..
320
345
  } else {
321
346
  res.destroy()
322
- ctx.logger?.warn({ req }, 'request destroyed')
347
+ logger?.warn('request destroyed')
323
348
  }
324
349
  }
325
350
  }
@@ -334,13 +359,8 @@ export class IncomingMessage extends http.IncomingMessage {
334
359
  #search
335
360
  #query
336
361
 
337
- constructor(...args) {
338
- super(...args)
339
- this.#id = this.headers['request-id'] || this.headers['Request-Id'] || genReqId()
340
- }
341
-
342
362
  get id() {
343
- return this.#id
363
+ return (this.#id ??= this.headers['request-id'] || this.headers['Request-Id'] || genReqId())
344
364
  }
345
365
 
346
366
  get target() {
@@ -485,6 +505,12 @@ export class ServerResponse extends http.ServerResponse {
485
505
  headers = headers ? Object.assign(this.#headersObj, headers) : this.#headersObj
486
506
  }
487
507
 
508
+ if (!this.destroyed) {
509
+ if (this.#headers === -1) {
510
+ this.#headers = performance.now() - this.#created
511
+ }
512
+ }
513
+
488
514
  this.#headersObj = null
489
515
 
490
516
  return super.writeHead(statusCode, headers)
@@ -503,15 +529,6 @@ export class ServerResponse extends http.ServerResponse {
503
529
  return super.assignSocket(socket)
504
530
  }
505
531
 
506
- flushHeaders() {
507
- if (!this.destroyed) {
508
- if (this.#headers === -1) {
509
- this.#headers = performance.now() - this.#created
510
- }
511
- }
512
- return super.flushHeaders()
513
- }
514
-
515
532
  write(chunk, encoding, callback) {
516
533
  if (!this.destroyed) {
517
534
  if (this.#data === -1) {
@@ -584,13 +601,8 @@ export class Http2ServerRequest extends http2.Http2ServerRequest {
584
601
  #search
585
602
  #query
586
603
 
587
- constructor(...args) {
588
- super(...args)
589
- this.#id = this.headers['request-id'] || this.headers['Request-Id'] || genReqId()
590
- }
591
-
592
604
  get id() {
593
- return this.#id
605
+ return (this.#id ??= this.headers['request-id'] || this.headers['Request-Id'] || genReqId())
594
606
  }
595
607
 
596
608
  get target() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "26.0.1",
3
+ "version": "26.0.3",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",