@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.
- package/http.js +72 -0
- 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()
|