@nxtedition/lib 23.6.20 → 23.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 (2) hide show
  1. package/http.js +73 -1
  2. package/package.json +1 -1
package/http.js CHANGED
@@ -155,6 +155,78 @@ function noop() {}
155
155
 
156
156
  const pendingSet = (globalThis._nxt_lib_http_pending ??= new Set())
157
157
 
158
+ export async function upgradeMiddleware(ctx, next) {
159
+ const { req, socket, logger } = ctx
160
+ const startTime = performance.now()
161
+
162
+ let aborted = false
163
+
164
+ req.on('error', noop)
165
+ socket.on('error', (err) => {
166
+ // NOTE: Special case where the client becomes unreachable.
167
+ if (err.message.startsWith('read ')) {
168
+ aborted = true
169
+ }
170
+ })
171
+
172
+ const isHealthcheck = req.url === '/healthcheck' || req.url === '/_up'
173
+ const reqLogger = isHealthcheck ? null : logger.child({ req })
174
+
175
+ pendingSet.add(ctx)
176
+ try {
177
+ if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
178
+ req.resume() // Dump the body if there is one.
179
+ }
180
+
181
+ reqLogger?.debug('request started')
182
+
183
+ const thenable = next()
184
+
185
+ if (thenable?.then) {
186
+ await thenable
187
+ }
188
+
189
+ assert(socket.destroyed || socket.writableEnded, 'response not completed')
190
+
191
+ const elapsedTime = performance.now() - startTime
192
+
193
+ if (isHealthcheck) {
194
+ // Do nothing...
195
+ } else if (socket.errored) {
196
+ reqLogger?.error({ err: socket.errored, req, socket, elapsedTime }, 'request error')
197
+ } else if (!socket.writableEnded) {
198
+ reqLogger?.debug({ req, socket, elapsedTime }, 'request aborted')
199
+ } else if (socket.statusCode >= 500) {
200
+ reqLogger?.error({ req, socket, elapsedTime }, 'request error')
201
+ } else if (socket.statusCode >= 400) {
202
+ reqLogger?.warn({ req, socket, elapsedTime }, 'request failed')
203
+ } else {
204
+ reqLogger?.debug({ req, socket, elapsedTime }, 'request completed')
205
+ }
206
+ } catch (err) {
207
+ ctx[kAbortController]?.abort(err)
208
+
209
+ const statusCode = err.statusCode || err.$metadata?.httpStatusCode || 500
210
+ const elapsedTime = performance.now() - startTime
211
+
212
+ if (req.aborted || aborted || (!socket.errored && socket.closed) || err.name === 'AbortError') {
213
+ reqLogger?.debug({ socket, err, elapsedTime }, 'request aborted')
214
+ } else if (statusCode < 500) {
215
+ reqLogger?.warn({ socket, err, elapsedTime }, 'request failed')
216
+ } else {
217
+ reqLogger?.error({ socket, err, elapsedTime }, 'request error')
218
+ }
219
+ socket.destroy(err)
220
+ } finally {
221
+ pendingSet.delete(ctx)
222
+
223
+ if (!socket.writableEnded && !socket.destroyed) {
224
+ socket.destroy()
225
+ reqLogger?.warn('socket destroyed')
226
+ }
227
+ }
228
+ }
229
+
158
230
  export async function requestMiddleware(ctx, next) {
159
231
  const { req, res, logger } = ctx
160
232
  const startTime = performance.now()
@@ -405,7 +477,7 @@ export class ServerResponse extends http.ServerResponse {
405
477
  }
406
478
  }
407
479
 
408
- export class Http2IncomingMessage extends http2.IncomingMessage {
480
+ export class Http2ServerRequest extends http2.Http2ServerRequest {
409
481
  #target
410
482
  #host
411
483
  #url
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "23.6.20",
3
+ "version": "23.7.0",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",