@srfnstack/spliffy 1.2.5 → 1.2.6
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/LICENSE.txt +20 -20
- package/README.md +43 -43
- package/package.json +47 -44
- package/src/content.mjs +97 -97
- package/src/decorator.mjs +209 -209
- package/src/handler.mjs +256 -256
- package/src/index.mjs +30 -30
- package/src/log.mjs +59 -59
- package/src/middleware.mjs +89 -89
- package/src/nodeModuleHandler.mjs +75 -75
- package/src/routes.mjs +205 -205
- package/src/server.mjs +141 -141
- package/src/serverConfig.mjs +100 -100
- package/src/start.mjs +25 -25
- package/src/staticHandler.mjs +74 -74
- package/src/url.mjs +24 -24
package/src/handler.mjs
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
import log from './log.mjs'
|
|
2
|
-
import { deserializeBody, serializeBody } from './content.mjs'
|
|
3
|
-
import { invokeMiddleware } from './middleware.mjs'
|
|
4
|
-
import { decorateResponse, decorateRequest } from './decorator.mjs'
|
|
5
|
-
import { v4 as uuid } from 'uuid'
|
|
6
|
-
import stream from 'stream'
|
|
7
|
-
const { Readable } = stream
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Execute the handler
|
|
11
|
-
* @param url The url being requested
|
|
12
|
-
* @param res The uws response object
|
|
13
|
-
* @param req The uws request object
|
|
14
|
-
* @param bodyPromise The request body promise
|
|
15
|
-
* @param handler The handler function for the route
|
|
16
|
-
* @param middleware The middleware that applies to this request
|
|
17
|
-
* @param errorTransformer An errorTransformer to convert error objects into response data
|
|
18
|
-
*/
|
|
19
|
-
const executeHandler = async (url, res, req, bodyPromise, handler, middleware, errorTransformer) => {
|
|
20
|
-
try {
|
|
21
|
-
bodyPromise = bodyPromise.then(bodyContent => {
|
|
22
|
-
if (bodyContent instanceof Readable) return bodyContent
|
|
23
|
-
if (res.writableEnded) return
|
|
24
|
-
return deserializeBody(bodyContent, req.headers['content-type'], res.acceptsDefault)
|
|
25
|
-
})
|
|
26
|
-
} catch (e) {
|
|
27
|
-
log.error('Failed to parse request.', e)
|
|
28
|
-
end(res, 400, handler.statusCodeOverride)
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const handled = await handler({ url, bodyPromise, headers: req.headers, req, res })
|
|
34
|
-
finalizeResponse(req, res, handled, handler.statusCodeOverride)
|
|
35
|
-
} catch (e) {
|
|
36
|
-
const refId = uuid()
|
|
37
|
-
await executeMiddleware(middleware, req, res, errorTransformer, refId, e)
|
|
38
|
-
endError(res, e, refId, errorTransformer)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const endError = (res, e, refId, errorTransformer) => {
|
|
43
|
-
if (e.body && typeof e.body !== 'string') {
|
|
44
|
-
e.body = JSON.stringify(e.body)
|
|
45
|
-
}
|
|
46
|
-
if (typeof errorTransformer === 'function') {
|
|
47
|
-
e = errorTransformer(e, refId)
|
|
48
|
-
}
|
|
49
|
-
res.headers['x-ref-id'] = refId
|
|
50
|
-
const status = e.statusCode || 500
|
|
51
|
-
if (status === 500) {
|
|
52
|
-
log.error(e)
|
|
53
|
-
}
|
|
54
|
-
end(res, status, null, e.body || '')
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const end = (res, defaultStatusCode, statusCodeOverride, body) => {
|
|
58
|
-
// status set directly on res wins
|
|
59
|
-
res.statusCode = statusCodeOverride || res.statusCode || defaultStatusCode
|
|
60
|
-
if (body instanceof Readable || res.streaming) {
|
|
61
|
-
res.streaming = true
|
|
62
|
-
if (body instanceof Readable) {
|
|
63
|
-
pipeResponse(res, body)
|
|
64
|
-
}
|
|
65
|
-
// handler is responsible for ending the response if they are streaming
|
|
66
|
-
} else {
|
|
67
|
-
res.end(doSerializeBody(body, res) || '')
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const ipv6CompressRegex = /\b:?(?:0+:?){2,}/g
|
|
72
|
-
|
|
73
|
-
const compressIpv6 = ip => ip && ip.includes(':') ? ip.replaceAll(ipv6CompressRegex, '::') : ip
|
|
74
|
-
|
|
75
|
-
const writeAccess = function (req, res) {
|
|
76
|
-
const start = new Date().getTime()
|
|
77
|
-
return () => {
|
|
78
|
-
log.access(compressIpv6(req.remoteAddress), compressIpv6(res.proxiedRemoteAddress) || '', res.statusCode, req.method, req.url, new Date().getTime() - start + 'ms')
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const finalizeResponse = (req, res, handled, statusCodeOverride) => {
|
|
83
|
-
if (!res.finalized) {
|
|
84
|
-
if (!handled) {
|
|
85
|
-
// if no error was thrown, assume everything is fine. Otherwise each handler must return truthy which is un-necessary for methods that don't need to return anything
|
|
86
|
-
end(res, 200, statusCodeOverride)
|
|
87
|
-
} else {
|
|
88
|
-
// if the returned object has known fields, treat it as a response object instead of the body
|
|
89
|
-
if (handled.body || handled.statusMessage || handled.statusCode || handled.headers) {
|
|
90
|
-
if (handled.headers) {
|
|
91
|
-
res.assignHeaders(handled.headers)
|
|
92
|
-
}
|
|
93
|
-
res.statusMessage = handled.statusMessage || res.statusMessage
|
|
94
|
-
end(res, handled.statusCode || 200, statusCodeOverride, handled.body)
|
|
95
|
-
} else {
|
|
96
|
-
end(res, 200, statusCodeOverride, handled)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
res.finalized = true
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const pipeResponse = (res, readStream, errorTransformer) => {
|
|
104
|
-
readStream.on('data', res.write)
|
|
105
|
-
.on('end', res.end)
|
|
106
|
-
.on('error', e => {
|
|
107
|
-
try {
|
|
108
|
-
readStream.destroy()
|
|
109
|
-
} finally {
|
|
110
|
-
endError(res, e, uuid(), errorTransformer)
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const doSerializeBody = (body, res) => {
|
|
116
|
-
if (!body || typeof body === 'string' || body instanceof Readable) {
|
|
117
|
-
return body
|
|
118
|
-
}
|
|
119
|
-
const contentType = res.getHeader('content-type')
|
|
120
|
-
const serialized = serializeBody(body, contentType, res.acceptsDefault)
|
|
121
|
-
|
|
122
|
-
if (serialized?.contentType && !contentType) {
|
|
123
|
-
res.headers['content-type'] = serialized.contentType
|
|
124
|
-
}
|
|
125
|
-
return serialized?.data || ''
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
async function executeMiddleware (middleware, req, res, errorTransformer, refId, e) {
|
|
129
|
-
if (!middleware) return
|
|
130
|
-
|
|
131
|
-
let applicableMiddleware = middleware[req.method]
|
|
132
|
-
if (middleware.ALL) {
|
|
133
|
-
if (applicableMiddleware) applicableMiddleware = middleware.ALL.concat(applicableMiddleware)
|
|
134
|
-
else applicableMiddleware = middleware.ALL
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (!applicableMiddleware || applicableMiddleware.length === 0) {
|
|
138
|
-
return
|
|
139
|
-
}
|
|
140
|
-
if (e) {
|
|
141
|
-
await invokeMiddleware(applicableMiddleware.filter(mw => mw.length === 4), req, res, e)
|
|
142
|
-
} else {
|
|
143
|
-
await invokeMiddleware(applicableMiddleware.filter(mw => mw.length === 3), req, res)
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const handleRequest = async (req, res, handler, middleware, errorTransformer) => {
|
|
148
|
-
try {
|
|
149
|
-
let reqBody
|
|
150
|
-
if (!handler.streamRequestBody) {
|
|
151
|
-
let buffer
|
|
152
|
-
reqBody = new Promise(
|
|
153
|
-
resolve =>
|
|
154
|
-
res.onData(async (data, isLast) => {
|
|
155
|
-
if (isLast) {
|
|
156
|
-
buffer = data.byteLength > 0 ? Buffer.concat([buffer, Buffer.from(data)].filter(b => b)) : buffer
|
|
157
|
-
resolve(buffer)
|
|
158
|
-
}
|
|
159
|
-
buffer = Buffer.concat([buffer, Buffer.from(data)].filter(b => b))
|
|
160
|
-
})
|
|
161
|
-
)
|
|
162
|
-
} else {
|
|
163
|
-
const readable = new Readable({
|
|
164
|
-
read: () => {
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
res.onData(async (data, isLast) => {
|
|
168
|
-
if (data.byteLength === 0 && !isLast) return
|
|
169
|
-
// data must be copied so it isn't lost
|
|
170
|
-
readable.push(Buffer.concat([Buffer.from(data)]))
|
|
171
|
-
if (isLast) {
|
|
172
|
-
readable.push(null)
|
|
173
|
-
}
|
|
174
|
-
})
|
|
175
|
-
reqBody = Promise.resolve(readable)
|
|
176
|
-
}
|
|
177
|
-
await executeMiddleware(middleware, req, res, errorTransformer)
|
|
178
|
-
if (!res.writableEnded && !res.ended) {
|
|
179
|
-
await executeHandler(req.spliffyUrl, res, req, reqBody, handler, middleware, errorTransformer)
|
|
180
|
-
}
|
|
181
|
-
} catch (e) {
|
|
182
|
-
const refId = uuid()
|
|
183
|
-
await executeMiddleware(middleware, req, res, errorTransformer, refId, e)
|
|
184
|
-
if (!res.writableEnded) { endError(res, e, refId, errorTransformer) }
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD', 'CONNECT', 'TRACE', 'WEBSOCKET']
|
|
189
|
-
|
|
190
|
-
let currentDate = new Date().toISOString()
|
|
191
|
-
setInterval(() => { currentDate = new Date().toISOString() }, 1000)
|
|
192
|
-
|
|
193
|
-
export const createHandler = (handler, middleware, pathParameters, config) => function (res, req) {
|
|
194
|
-
try {
|
|
195
|
-
res.cork(() => {
|
|
196
|
-
req = decorateRequest(req, pathParameters, res, config)
|
|
197
|
-
res = decorateResponse(res, req, finalizeResponse, config.errorTransformer, endError, config)
|
|
198
|
-
|
|
199
|
-
if (config.logAccess) {
|
|
200
|
-
res.onEnd = writeAccess(req, res)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (config.writeDateHeader) {
|
|
204
|
-
res.headers.date = currentDate
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
handleRequest(req, res, handler, middleware, config.errorTransformer)
|
|
208
|
-
.catch(e => {
|
|
209
|
-
log.error('Failed handling request', e)
|
|
210
|
-
res.statusCode = 500
|
|
211
|
-
res.end()
|
|
212
|
-
})
|
|
213
|
-
})
|
|
214
|
-
} catch (e) {
|
|
215
|
-
log.error('Failed handling request', e)
|
|
216
|
-
res.statusCode = 500
|
|
217
|
-
res.end()
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export const createNotFoundHandler = config => {
|
|
222
|
-
const handler = config.defaultRouteHandler || config.notFoundRouteHandler
|
|
223
|
-
const params = handler?.pathParameters || []
|
|
224
|
-
return (res, req) => {
|
|
225
|
-
try {
|
|
226
|
-
req = decorateRequest(req, params, res, config)
|
|
227
|
-
res = decorateResponse(res, req, finalizeResponse, config.errorTransformer, endError, config)
|
|
228
|
-
if (config.logAccess) {
|
|
229
|
-
res.onEnd = writeAccess(req, res)
|
|
230
|
-
}
|
|
231
|
-
if (handler && typeof handler === 'object') {
|
|
232
|
-
if (handler.handlers && typeof handler.handlers[req.method] === 'function') {
|
|
233
|
-
if ('statusCodeOverride' in handler) {
|
|
234
|
-
handler.handlers[req.method].statusCodeOverride = handler.statusCodeOverride
|
|
235
|
-
}
|
|
236
|
-
handleRequest(req, res,
|
|
237
|
-
handler.handlers[req.method],
|
|
238
|
-
handler.middleware,
|
|
239
|
-
config.errorTransformer
|
|
240
|
-
).catch((e) => {
|
|
241
|
-
log.error('Unexpected exception during request handling', e)
|
|
242
|
-
res.statusCode = 500
|
|
243
|
-
})
|
|
244
|
-
} else {
|
|
245
|
-
res.statusCode = 404
|
|
246
|
-
res.end()
|
|
247
|
-
}
|
|
248
|
-
} else {
|
|
249
|
-
res.statusCode = 404
|
|
250
|
-
res.end()
|
|
251
|
-
}
|
|
252
|
-
} catch (e) {
|
|
253
|
-
log.error('Failed handling request', e)
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
1
|
+
import log from './log.mjs'
|
|
2
|
+
import { deserializeBody, serializeBody } from './content.mjs'
|
|
3
|
+
import { invokeMiddleware } from './middleware.mjs'
|
|
4
|
+
import { decorateResponse, decorateRequest } from './decorator.mjs'
|
|
5
|
+
import { v4 as uuid } from 'uuid'
|
|
6
|
+
import stream from 'stream'
|
|
7
|
+
const { Readable } = stream
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Execute the handler
|
|
11
|
+
* @param url The url being requested
|
|
12
|
+
* @param res The uws response object
|
|
13
|
+
* @param req The uws request object
|
|
14
|
+
* @param bodyPromise The request body promise
|
|
15
|
+
* @param handler The handler function for the route
|
|
16
|
+
* @param middleware The middleware that applies to this request
|
|
17
|
+
* @param errorTransformer An errorTransformer to convert error objects into response data
|
|
18
|
+
*/
|
|
19
|
+
const executeHandler = async (url, res, req, bodyPromise, handler, middleware, errorTransformer) => {
|
|
20
|
+
try {
|
|
21
|
+
bodyPromise = bodyPromise.then(bodyContent => {
|
|
22
|
+
if (bodyContent instanceof Readable) return bodyContent
|
|
23
|
+
if (res.writableEnded) return
|
|
24
|
+
return deserializeBody(bodyContent, req.headers['content-type'], res.acceptsDefault)
|
|
25
|
+
})
|
|
26
|
+
} catch (e) {
|
|
27
|
+
log.error('Failed to parse request.', e)
|
|
28
|
+
end(res, 400, handler.statusCodeOverride)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const handled = await handler({ url, bodyPromise, headers: req.headers, req, res })
|
|
34
|
+
finalizeResponse(req, res, handled, handler.statusCodeOverride)
|
|
35
|
+
} catch (e) {
|
|
36
|
+
const refId = uuid()
|
|
37
|
+
await executeMiddleware(middleware, req, res, errorTransformer, refId, e)
|
|
38
|
+
endError(res, e, refId, errorTransformer)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const endError = (res, e, refId, errorTransformer) => {
|
|
43
|
+
if (e.body && typeof e.body !== 'string') {
|
|
44
|
+
e.body = JSON.stringify(e.body)
|
|
45
|
+
}
|
|
46
|
+
if (typeof errorTransformer === 'function') {
|
|
47
|
+
e = errorTransformer(e, refId)
|
|
48
|
+
}
|
|
49
|
+
res.headers['x-ref-id'] = refId
|
|
50
|
+
const status = e.statusCode || 500
|
|
51
|
+
if (status === 500) {
|
|
52
|
+
log.error(e)
|
|
53
|
+
}
|
|
54
|
+
end(res, status, null, e.body || '')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const end = (res, defaultStatusCode, statusCodeOverride, body) => {
|
|
58
|
+
// status set directly on res wins
|
|
59
|
+
res.statusCode = statusCodeOverride || res.statusCode || defaultStatusCode
|
|
60
|
+
if (body instanceof Readable || res.streaming) {
|
|
61
|
+
res.streaming = true
|
|
62
|
+
if (body instanceof Readable) {
|
|
63
|
+
pipeResponse(res, body)
|
|
64
|
+
}
|
|
65
|
+
// handler is responsible for ending the response if they are streaming
|
|
66
|
+
} else {
|
|
67
|
+
res.end(doSerializeBody(body, res) || '')
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ipv6CompressRegex = /\b:?(?:0+:?){2,}/g
|
|
72
|
+
|
|
73
|
+
const compressIpv6 = ip => ip && ip.includes(':') ? ip.replaceAll(ipv6CompressRegex, '::') : ip
|
|
74
|
+
|
|
75
|
+
const writeAccess = function (req, res) {
|
|
76
|
+
const start = new Date().getTime()
|
|
77
|
+
return () => {
|
|
78
|
+
log.access(compressIpv6(req.remoteAddress), compressIpv6(res.proxiedRemoteAddress) || '', res.statusCode, req.method, req.url, new Date().getTime() - start + 'ms')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const finalizeResponse = (req, res, handled, statusCodeOverride) => {
|
|
83
|
+
if (!res.finalized) {
|
|
84
|
+
if (!handled) {
|
|
85
|
+
// if no error was thrown, assume everything is fine. Otherwise each handler must return truthy which is un-necessary for methods that don't need to return anything
|
|
86
|
+
end(res, 200, statusCodeOverride)
|
|
87
|
+
} else {
|
|
88
|
+
// if the returned object has known fields, treat it as a response object instead of the body
|
|
89
|
+
if (handled.body || handled.statusMessage || handled.statusCode || handled.headers) {
|
|
90
|
+
if (handled.headers) {
|
|
91
|
+
res.assignHeaders(handled.headers)
|
|
92
|
+
}
|
|
93
|
+
res.statusMessage = handled.statusMessage || res.statusMessage
|
|
94
|
+
end(res, handled.statusCode || 200, statusCodeOverride, handled.body)
|
|
95
|
+
} else {
|
|
96
|
+
end(res, 200, statusCodeOverride, handled)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
res.finalized = true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const pipeResponse = (res, readStream, errorTransformer) => {
|
|
104
|
+
readStream.on('data', res.write)
|
|
105
|
+
.on('end', res.end)
|
|
106
|
+
.on('error', e => {
|
|
107
|
+
try {
|
|
108
|
+
readStream.destroy()
|
|
109
|
+
} finally {
|
|
110
|
+
endError(res, e, uuid(), errorTransformer)
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const doSerializeBody = (body, res) => {
|
|
116
|
+
if (!body || typeof body === 'string' || body instanceof Readable) {
|
|
117
|
+
return body
|
|
118
|
+
}
|
|
119
|
+
const contentType = res.getHeader('content-type')
|
|
120
|
+
const serialized = serializeBody(body, contentType, res.acceptsDefault)
|
|
121
|
+
|
|
122
|
+
if (serialized?.contentType && !contentType) {
|
|
123
|
+
res.headers['content-type'] = serialized.contentType
|
|
124
|
+
}
|
|
125
|
+
return serialized?.data || ''
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function executeMiddleware (middleware, req, res, errorTransformer, refId, e) {
|
|
129
|
+
if (!middleware) return
|
|
130
|
+
|
|
131
|
+
let applicableMiddleware = middleware[req.method]
|
|
132
|
+
if (middleware.ALL) {
|
|
133
|
+
if (applicableMiddleware) applicableMiddleware = middleware.ALL.concat(applicableMiddleware)
|
|
134
|
+
else applicableMiddleware = middleware.ALL
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!applicableMiddleware || applicableMiddleware.length === 0) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
if (e) {
|
|
141
|
+
await invokeMiddleware(applicableMiddleware.filter(mw => mw.length === 4), req, res, e)
|
|
142
|
+
} else {
|
|
143
|
+
await invokeMiddleware(applicableMiddleware.filter(mw => mw.length === 3), req, res)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const handleRequest = async (req, res, handler, middleware, errorTransformer) => {
|
|
148
|
+
try {
|
|
149
|
+
let reqBody
|
|
150
|
+
if (!handler.streamRequestBody) {
|
|
151
|
+
let buffer
|
|
152
|
+
reqBody = new Promise(
|
|
153
|
+
resolve =>
|
|
154
|
+
res.onData(async (data, isLast) => {
|
|
155
|
+
if (isLast) {
|
|
156
|
+
buffer = data.byteLength > 0 ? Buffer.concat([buffer, Buffer.from(data)].filter(b => b)) : buffer
|
|
157
|
+
resolve(buffer)
|
|
158
|
+
}
|
|
159
|
+
buffer = Buffer.concat([buffer, Buffer.from(data)].filter(b => b))
|
|
160
|
+
})
|
|
161
|
+
)
|
|
162
|
+
} else {
|
|
163
|
+
const readable = new Readable({
|
|
164
|
+
read: () => {
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
res.onData(async (data, isLast) => {
|
|
168
|
+
if (data.byteLength === 0 && !isLast) return
|
|
169
|
+
// data must be copied so it isn't lost
|
|
170
|
+
readable.push(Buffer.concat([Buffer.from(data)]))
|
|
171
|
+
if (isLast) {
|
|
172
|
+
readable.push(null)
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
reqBody = Promise.resolve(readable)
|
|
176
|
+
}
|
|
177
|
+
await executeMiddleware(middleware, req, res, errorTransformer)
|
|
178
|
+
if (!res.writableEnded && !res.ended) {
|
|
179
|
+
await executeHandler(req.spliffyUrl, res, req, reqBody, handler, middleware, errorTransformer)
|
|
180
|
+
}
|
|
181
|
+
} catch (e) {
|
|
182
|
+
const refId = uuid()
|
|
183
|
+
await executeMiddleware(middleware, req, res, errorTransformer, refId, e)
|
|
184
|
+
if (!res.writableEnded) { endError(res, e, refId, errorTransformer) }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export const HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD', 'CONNECT', 'TRACE', 'WEBSOCKET']
|
|
189
|
+
|
|
190
|
+
let currentDate = new Date().toISOString()
|
|
191
|
+
setInterval(() => { currentDate = new Date().toISOString() }, 1000)
|
|
192
|
+
|
|
193
|
+
export const createHandler = (handler, middleware, pathParameters, config) => function (res, req) {
|
|
194
|
+
try {
|
|
195
|
+
res.cork(() => {
|
|
196
|
+
req = decorateRequest(req, pathParameters, res, config)
|
|
197
|
+
res = decorateResponse(res, req, finalizeResponse, config.errorTransformer, endError, config)
|
|
198
|
+
|
|
199
|
+
if (config.logAccess) {
|
|
200
|
+
res.onEnd = writeAccess(req, res)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (config.writeDateHeader) {
|
|
204
|
+
res.headers.date = currentDate
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
handleRequest(req, res, handler, middleware, config.errorTransformer)
|
|
208
|
+
.catch(e => {
|
|
209
|
+
log.error('Failed handling request', e)
|
|
210
|
+
res.statusCode = 500
|
|
211
|
+
res.end()
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
} catch (e) {
|
|
215
|
+
log.error('Failed handling request', e)
|
|
216
|
+
res.statusCode = 500
|
|
217
|
+
res.end()
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const createNotFoundHandler = config => {
|
|
222
|
+
const handler = config.defaultRouteHandler || config.notFoundRouteHandler
|
|
223
|
+
const params = handler?.pathParameters || []
|
|
224
|
+
return (res, req) => {
|
|
225
|
+
try {
|
|
226
|
+
req = decorateRequest(req, params, res, config)
|
|
227
|
+
res = decorateResponse(res, req, finalizeResponse, config.errorTransformer, endError, config)
|
|
228
|
+
if (config.logAccess) {
|
|
229
|
+
res.onEnd = writeAccess(req, res)
|
|
230
|
+
}
|
|
231
|
+
if (handler && typeof handler === 'object') {
|
|
232
|
+
if (handler.handlers && typeof handler.handlers[req.method] === 'function') {
|
|
233
|
+
if ('statusCodeOverride' in handler) {
|
|
234
|
+
handler.handlers[req.method].statusCodeOverride = handler.statusCodeOverride
|
|
235
|
+
}
|
|
236
|
+
handleRequest(req, res,
|
|
237
|
+
handler.handlers[req.method],
|
|
238
|
+
handler.middleware,
|
|
239
|
+
config.errorTransformer
|
|
240
|
+
).catch((e) => {
|
|
241
|
+
log.error('Unexpected exception during request handling', e)
|
|
242
|
+
res.statusCode = 500
|
|
243
|
+
})
|
|
244
|
+
} else {
|
|
245
|
+
res.statusCode = 404
|
|
246
|
+
res.end()
|
|
247
|
+
}
|
|
248
|
+
} else {
|
|
249
|
+
res.statusCode = 404
|
|
250
|
+
res.end()
|
|
251
|
+
}
|
|
252
|
+
} catch (e) {
|
|
253
|
+
log.error('Failed handling request', e)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
package/src/index.mjs
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import { fileURLToPath } from 'url'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A helper for creating a redirect handler
|
|
6
|
-
* @param location The location to redirect to
|
|
7
|
-
* @param permanent Whether this is a permanent redirect or not
|
|
8
|
-
*/
|
|
9
|
-
export const redirect = (location, permanent = true) => () => ({
|
|
10
|
-
statusCode: permanent ? 301 : 302,
|
|
11
|
-
headers: {
|
|
12
|
-
location: location
|
|
13
|
-
}
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
export { default as log } from './log.mjs'
|
|
17
|
-
export { parseQuery, setMultiValueKey } from './url.mjs'
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Startup function for the spliffy server
|
|
21
|
-
* @param config See https://github.com/narcolepticsnowman/spliffy#config
|
|
22
|
-
* @returns {Promise<Server>} Either the https server if https is configured or the http server
|
|
23
|
-
*/
|
|
24
|
-
export { default } from './start.mjs'
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get the dirname for the given meta url
|
|
28
|
-
* @param metaUrl The import.meta.url value to get the dirname from
|
|
29
|
-
* @return {string} The full path to the directory the module is in
|
|
30
|
-
*/
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A helper for creating a redirect handler
|
|
6
|
+
* @param location The location to redirect to
|
|
7
|
+
* @param permanent Whether this is a permanent redirect or not
|
|
8
|
+
*/
|
|
9
|
+
export const redirect = (location, permanent = true) => () => ({
|
|
10
|
+
statusCode: permanent ? 301 : 302,
|
|
11
|
+
headers: {
|
|
12
|
+
location: location
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export { default as log } from './log.mjs'
|
|
17
|
+
export { parseQuery, setMultiValueKey } from './url.mjs'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Startup function for the spliffy server
|
|
21
|
+
* @param config See https://github.com/narcolepticsnowman/spliffy#config
|
|
22
|
+
* @returns {Promise<Server>} Either the https server if https is configured or the http server
|
|
23
|
+
*/
|
|
24
|
+
export { default } from './start.mjs'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the dirname for the given meta url
|
|
28
|
+
* @param metaUrl The import.meta.url value to get the dirname from
|
|
29
|
+
* @return {string} The full path to the directory the module is in
|
|
30
|
+
*/
|
|
31
31
|
export const moduleDirname = metaUrl => path.dirname(fileURLToPath(metaUrl))
|