@nxtedition/lib 23.6.21 → 23.7.1

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 +72 -0
  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('stream started')
182
+
183
+ const thenable = next()
184
+
185
+ if (thenable?.then) {
186
+ await thenable
187
+ }
188
+
189
+ assert(socket.destroyed || socket.writableEnded, 'stream 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 }, 'stream error')
197
+ } else if (!socket.writableEnded) {
198
+ reqLogger?.debug({ req, socket, elapsedTime }, 'stream aborted')
199
+ } else if (socket.statusCode >= 500) {
200
+ reqLogger?.error({ req, socket, elapsedTime }, 'stream error')
201
+ } else if (socket.statusCode >= 400) {
202
+ reqLogger?.warn({ req, socket, elapsedTime }, 'stream failed')
203
+ } else {
204
+ reqLogger?.debug({ req, socket, elapsedTime }, 'stream 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 }, 'stream aborted')
214
+ } else if (statusCode < 500) {
215
+ reqLogger?.warn({ socket, err, elapsedTime }, 'stream failed')
216
+ } else {
217
+ reqLogger?.error({ socket, err, elapsedTime }, 'stream 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()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/lib",
3
- "version": "23.6.21",
3
+ "version": "23.7.1",
4
4
  "license": "MIT",
5
5
  "author": "Robert Nagy <robert.nagy@boffins.se>",
6
6
  "type": "module",