@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/decorator.mjs
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
import cookie from 'cookie'
|
|
2
|
-
import http from 'http'
|
|
3
|
-
import { parseQuery } from './url.mjs'
|
|
4
|
-
import log from './log.mjs'
|
|
5
|
-
import { v4 as uuid } from 'uuid'
|
|
6
|
-
import stream from 'stream'
|
|
7
|
-
import httpStatusCodes, { defaultStatusMessages } from './httpStatusCodes.mjs'
|
|
8
|
-
|
|
9
|
-
const { Writable } = stream
|
|
10
|
-
|
|
11
|
-
const addressArrayBufferToString = addrBuf => String.fromCharCode.apply(null, new Int8Array(addrBuf))
|
|
12
|
-
const excludedMessageProps = {
|
|
13
|
-
setTimeout: true,
|
|
14
|
-
_read: true,
|
|
15
|
-
destroy: true,
|
|
16
|
-
_addHeaderLines: true,
|
|
17
|
-
_addHeaderLine: true,
|
|
18
|
-
_dump: true,
|
|
19
|
-
__proto__: true
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const normalizeHeader = header => header.toLowerCase()
|
|
23
|
-
|
|
24
|
-
const reqProtoProps = () => Object.keys(http.IncomingMessage.prototype).filter(p => !excludedMessageProps[p])
|
|
25
|
-
|
|
26
|
-
export const setCookie = (res) => function () {
|
|
27
|
-
return res.setHeader('Set-Cookie', [...(res.getHeader('Set-Cookie') || []), cookie.serialize(...arguments)])
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function decorateRequest (uwsReq, pathParameters, res, {
|
|
31
|
-
decodeQueryParameters,
|
|
32
|
-
decodePathParameters,
|
|
33
|
-
parseCookie,
|
|
34
|
-
extendIncomingMessage
|
|
35
|
-
} = {}) {
|
|
36
|
-
// uwsReq can't be used in async functions because it gets de-allocated when the handler function returns
|
|
37
|
-
const req = {}
|
|
38
|
-
if (extendIncomingMessage) {
|
|
39
|
-
// frameworks like passport like to modify the message prototype
|
|
40
|
-
// Setting the prototype of req is not desirable because the entire api of IncomingMessage is not supported
|
|
41
|
-
for (const p of reqProtoProps()) {
|
|
42
|
-
if (!req[p]) req[p] = http.IncomingMessage.prototype[p]
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const query = uwsReq.getQuery()
|
|
46
|
-
req.path = uwsReq.getUrl()
|
|
47
|
-
req.url = `${req.path}${query ? '?' + query : ''}`
|
|
48
|
-
const paramToIndex = pathParameters.reduce((acc, cur, i) => {
|
|
49
|
-
acc[cur] = i
|
|
50
|
-
return acc
|
|
51
|
-
}, {})
|
|
52
|
-
req.spliffyUrl = {
|
|
53
|
-
path: req.path,
|
|
54
|
-
query: (query && parseQuery(query, decodeQueryParameters)) || {},
|
|
55
|
-
param: name => uwsReq.getParameter(paramToIndex[name])
|
|
56
|
-
}
|
|
57
|
-
req.query = req.spliffyUrl.query
|
|
58
|
-
req.headers = {}
|
|
59
|
-
uwsReq.forEach((header, value) => { req.headers[header] = value })
|
|
60
|
-
req.method = uwsReq.getMethod().toUpperCase()
|
|
61
|
-
req.remoteAddress = addressArrayBufferToString(res.getRemoteAddressAsText())
|
|
62
|
-
req.proxiedRemoteAddress = addressArrayBufferToString(res.getProxiedRemoteAddressAsText())
|
|
63
|
-
req.get = header => req.headers[header]
|
|
64
|
-
if (parseCookie) {
|
|
65
|
-
req.cookies = (req.headers.cookie && cookie.parse(req.headers.cookie)) || {}
|
|
66
|
-
}
|
|
67
|
-
return req
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function toArrayBuffer (buffer) {
|
|
71
|
-
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function decorateResponse (res, req, finalizeResponse, errorTransformer, endError, { acceptsDefault }) {
|
|
75
|
-
res.onAborted(() => {
|
|
76
|
-
res.ended = true
|
|
77
|
-
res.writableEnded = true
|
|
78
|
-
res.finalized = true
|
|
79
|
-
log.error(`Request to ${req.url} was aborted`)
|
|
80
|
-
})
|
|
81
|
-
res.acceptsDefault = acceptsDefault
|
|
82
|
-
res.headers = {}
|
|
83
|
-
res.headersSent = false
|
|
84
|
-
res.setHeader = (header, value) => {
|
|
85
|
-
res.headers[normalizeHeader(header)] = value
|
|
86
|
-
}
|
|
87
|
-
res.removeHeader = header => {
|
|
88
|
-
delete res.headers[normalizeHeader(header)]
|
|
89
|
-
}
|
|
90
|
-
res.flushHeaders = () => {
|
|
91
|
-
if (res.headersSent) return
|
|
92
|
-
if (!res.statusCode) res.statusCode = httpStatusCodes.OK
|
|
93
|
-
if (!res.statusMessage) res.statusMessage = defaultStatusMessages[res.statusCode]
|
|
94
|
-
res.headersSent = true
|
|
95
|
-
res.writeStatus(`${res.statusCode} ${res.statusMessage}`)
|
|
96
|
-
if (typeof res.onFlushHeaders === 'function') {
|
|
97
|
-
res.onFlushHeaders(res)
|
|
98
|
-
}
|
|
99
|
-
for (const header of Object.keys(res.headers)) {
|
|
100
|
-
if (Array.isArray(res.headers[header])) {
|
|
101
|
-
for (const multiple of res.headers[header]) {
|
|
102
|
-
res.writeHeader(header, multiple.toString())
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
res.writeHeader(header, res.headers[header].toString())
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
res.writeHead = (status, headers) => {
|
|
110
|
-
res.statusCode = status
|
|
111
|
-
res.assignHeaders(headers)
|
|
112
|
-
}
|
|
113
|
-
res.assignHeaders = headers => {
|
|
114
|
-
for (const header of Object.keys(headers)) {
|
|
115
|
-
res.headers[normalizeHeader(header)] = headers[header]
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
res.getHeader = header => {
|
|
119
|
-
return res.headers[normalizeHeader(header)]
|
|
120
|
-
}
|
|
121
|
-
res.status = (code) => {
|
|
122
|
-
res.statusCode = code
|
|
123
|
-
return this
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
res.uwsWrite = res.write
|
|
127
|
-
res.write = (chunk, encoding, cb) => {
|
|
128
|
-
try {
|
|
129
|
-
res.streaming = true
|
|
130
|
-
res.flushHeaders()
|
|
131
|
-
let data
|
|
132
|
-
if (chunk instanceof Buffer) {
|
|
133
|
-
data = toArrayBuffer(chunk)
|
|
134
|
-
} else if (typeof chunk === 'string') {
|
|
135
|
-
data = toArrayBuffer(Buffer.from(chunk, encoding || 'utf8'))
|
|
136
|
-
} else {
|
|
137
|
-
data = toArrayBuffer(Buffer.from(JSON.stringify(chunk), encoding || 'utf8'))
|
|
138
|
-
}
|
|
139
|
-
const result = res.uwsWrite(data)
|
|
140
|
-
if (typeof cb === 'function') {
|
|
141
|
-
cb()
|
|
142
|
-
}
|
|
143
|
-
return result
|
|
144
|
-
} catch (e) {
|
|
145
|
-
if (typeof cb === 'function') {
|
|
146
|
-
cb(e)
|
|
147
|
-
} else {
|
|
148
|
-
throw e
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
let outStream
|
|
153
|
-
res.getWritable = () => {
|
|
154
|
-
if (!outStream) {
|
|
155
|
-
res.streaming = true
|
|
156
|
-
outStream = new Writable({
|
|
157
|
-
write: res.write
|
|
158
|
-
})
|
|
159
|
-
.on('finish', res.end)
|
|
160
|
-
.on('end', res.end)
|
|
161
|
-
.on('error', e => {
|
|
162
|
-
try {
|
|
163
|
-
outStream.destroy()
|
|
164
|
-
} finally {
|
|
165
|
-
endError(res, e, uuid(), errorTransformer)
|
|
166
|
-
}
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
return outStream
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const uwsEnd = res.end
|
|
173
|
-
res.ended = false
|
|
174
|
-
res.end = body => {
|
|
175
|
-
if (res.ended) {
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
// provide writableEnded like node does, with slightly different behavior
|
|
179
|
-
if (!res.writableEnded) {
|
|
180
|
-
res.flushHeaders()
|
|
181
|
-
uwsEnd.call(res, body)
|
|
182
|
-
res.writableEnded = true
|
|
183
|
-
res.ended = true
|
|
184
|
-
}
|
|
185
|
-
if (typeof res.onEnd === 'function') {
|
|
186
|
-
res.onEnd()
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
res.redirect = function (code, location) {
|
|
191
|
-
if (arguments.length === 1) {
|
|
192
|
-
location = code
|
|
193
|
-
code = httpStatusCodes.MOVED_PERMANENTLY
|
|
194
|
-
}
|
|
195
|
-
return finalizeResponse(req, res, {
|
|
196
|
-
statusCode: code,
|
|
197
|
-
headers: {
|
|
198
|
-
location: location
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
}
|
|
202
|
-
res.send = (body) => {
|
|
203
|
-
finalizeResponse(req, res, body)
|
|
204
|
-
}
|
|
205
|
-
res.json = res.send
|
|
206
|
-
res.setCookie = setCookie(res)
|
|
207
|
-
res.cookie = res.setCookie
|
|
208
|
-
return res
|
|
209
|
-
}
|
|
1
|
+
import cookie from 'cookie'
|
|
2
|
+
import http from 'http'
|
|
3
|
+
import { parseQuery } from './url.mjs'
|
|
4
|
+
import log from './log.mjs'
|
|
5
|
+
import { v4 as uuid } from 'uuid'
|
|
6
|
+
import stream from 'stream'
|
|
7
|
+
import httpStatusCodes, { defaultStatusMessages } from './httpStatusCodes.mjs'
|
|
8
|
+
|
|
9
|
+
const { Writable } = stream
|
|
10
|
+
|
|
11
|
+
const addressArrayBufferToString = addrBuf => String.fromCharCode.apply(null, new Int8Array(addrBuf))
|
|
12
|
+
const excludedMessageProps = {
|
|
13
|
+
setTimeout: true,
|
|
14
|
+
_read: true,
|
|
15
|
+
destroy: true,
|
|
16
|
+
_addHeaderLines: true,
|
|
17
|
+
_addHeaderLine: true,
|
|
18
|
+
_dump: true,
|
|
19
|
+
__proto__: true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const normalizeHeader = header => header.toLowerCase()
|
|
23
|
+
|
|
24
|
+
const reqProtoProps = () => Object.keys(http.IncomingMessage.prototype).filter(p => !excludedMessageProps[p])
|
|
25
|
+
|
|
26
|
+
export const setCookie = (res) => function () {
|
|
27
|
+
return res.setHeader('Set-Cookie', [...(res.getHeader('Set-Cookie') || []), cookie.serialize(...arguments)])
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function decorateRequest (uwsReq, pathParameters, res, {
|
|
31
|
+
decodeQueryParameters,
|
|
32
|
+
decodePathParameters,
|
|
33
|
+
parseCookie,
|
|
34
|
+
extendIncomingMessage
|
|
35
|
+
} = {}) {
|
|
36
|
+
// uwsReq can't be used in async functions because it gets de-allocated when the handler function returns
|
|
37
|
+
const req = {}
|
|
38
|
+
if (extendIncomingMessage) {
|
|
39
|
+
// frameworks like passport like to modify the message prototype
|
|
40
|
+
// Setting the prototype of req is not desirable because the entire api of IncomingMessage is not supported
|
|
41
|
+
for (const p of reqProtoProps()) {
|
|
42
|
+
if (!req[p]) req[p] = http.IncomingMessage.prototype[p]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const query = uwsReq.getQuery()
|
|
46
|
+
req.path = uwsReq.getUrl()
|
|
47
|
+
req.url = `${req.path}${query ? '?' + query : ''}`
|
|
48
|
+
const paramToIndex = pathParameters.reduce((acc, cur, i) => {
|
|
49
|
+
acc[cur] = i
|
|
50
|
+
return acc
|
|
51
|
+
}, {})
|
|
52
|
+
req.spliffyUrl = {
|
|
53
|
+
path: req.path,
|
|
54
|
+
query: (query && parseQuery(query, decodeQueryParameters)) || {},
|
|
55
|
+
param: name => uwsReq.getParameter(paramToIndex[name])
|
|
56
|
+
}
|
|
57
|
+
req.query = req.spliffyUrl.query
|
|
58
|
+
req.headers = {}
|
|
59
|
+
uwsReq.forEach((header, value) => { req.headers[header] = value })
|
|
60
|
+
req.method = uwsReq.getMethod().toUpperCase()
|
|
61
|
+
req.remoteAddress = addressArrayBufferToString(res.getRemoteAddressAsText())
|
|
62
|
+
req.proxiedRemoteAddress = addressArrayBufferToString(res.getProxiedRemoteAddressAsText())
|
|
63
|
+
req.get = header => req.headers[header]
|
|
64
|
+
if (parseCookie) {
|
|
65
|
+
req.cookies = (req.headers.cookie && cookie.parse(req.headers.cookie)) || {}
|
|
66
|
+
}
|
|
67
|
+
return req
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toArrayBuffer (buffer) {
|
|
71
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function decorateResponse (res, req, finalizeResponse, errorTransformer, endError, { acceptsDefault }) {
|
|
75
|
+
res.onAborted(() => {
|
|
76
|
+
res.ended = true
|
|
77
|
+
res.writableEnded = true
|
|
78
|
+
res.finalized = true
|
|
79
|
+
log.error(`Request to ${req.url} was aborted`)
|
|
80
|
+
})
|
|
81
|
+
res.acceptsDefault = acceptsDefault
|
|
82
|
+
res.headers = {}
|
|
83
|
+
res.headersSent = false
|
|
84
|
+
res.setHeader = (header, value) => {
|
|
85
|
+
res.headers[normalizeHeader(header)] = value
|
|
86
|
+
}
|
|
87
|
+
res.removeHeader = header => {
|
|
88
|
+
delete res.headers[normalizeHeader(header)]
|
|
89
|
+
}
|
|
90
|
+
res.flushHeaders = () => {
|
|
91
|
+
if (res.headersSent) return
|
|
92
|
+
if (!res.statusCode) res.statusCode = httpStatusCodes.OK
|
|
93
|
+
if (!res.statusMessage) res.statusMessage = defaultStatusMessages[res.statusCode]
|
|
94
|
+
res.headersSent = true
|
|
95
|
+
res.writeStatus(`${res.statusCode} ${res.statusMessage}`)
|
|
96
|
+
if (typeof res.onFlushHeaders === 'function') {
|
|
97
|
+
res.onFlushHeaders(res)
|
|
98
|
+
}
|
|
99
|
+
for (const header of Object.keys(res.headers)) {
|
|
100
|
+
if (Array.isArray(res.headers[header])) {
|
|
101
|
+
for (const multiple of res.headers[header]) {
|
|
102
|
+
res.writeHeader(header, multiple.toString())
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
res.writeHeader(header, res.headers[header].toString())
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
res.writeHead = (status, headers) => {
|
|
110
|
+
res.statusCode = status
|
|
111
|
+
res.assignHeaders(headers)
|
|
112
|
+
}
|
|
113
|
+
res.assignHeaders = headers => {
|
|
114
|
+
for (const header of Object.keys(headers)) {
|
|
115
|
+
res.headers[normalizeHeader(header)] = headers[header]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
res.getHeader = header => {
|
|
119
|
+
return res.headers[normalizeHeader(header)]
|
|
120
|
+
}
|
|
121
|
+
res.status = (code) => {
|
|
122
|
+
res.statusCode = code
|
|
123
|
+
return this
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
res.uwsWrite = res.write
|
|
127
|
+
res.write = (chunk, encoding, cb) => {
|
|
128
|
+
try {
|
|
129
|
+
res.streaming = true
|
|
130
|
+
res.flushHeaders()
|
|
131
|
+
let data
|
|
132
|
+
if (chunk instanceof Buffer) {
|
|
133
|
+
data = toArrayBuffer(chunk)
|
|
134
|
+
} else if (typeof chunk === 'string') {
|
|
135
|
+
data = toArrayBuffer(Buffer.from(chunk, encoding || 'utf8'))
|
|
136
|
+
} else {
|
|
137
|
+
data = toArrayBuffer(Buffer.from(JSON.stringify(chunk), encoding || 'utf8'))
|
|
138
|
+
}
|
|
139
|
+
const result = res.uwsWrite(data)
|
|
140
|
+
if (typeof cb === 'function') {
|
|
141
|
+
cb()
|
|
142
|
+
}
|
|
143
|
+
return result
|
|
144
|
+
} catch (e) {
|
|
145
|
+
if (typeof cb === 'function') {
|
|
146
|
+
cb(e)
|
|
147
|
+
} else {
|
|
148
|
+
throw e
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
let outStream
|
|
153
|
+
res.getWritable = () => {
|
|
154
|
+
if (!outStream) {
|
|
155
|
+
res.streaming = true
|
|
156
|
+
outStream = new Writable({
|
|
157
|
+
write: res.write
|
|
158
|
+
})
|
|
159
|
+
.on('finish', res.end)
|
|
160
|
+
.on('end', res.end)
|
|
161
|
+
.on('error', e => {
|
|
162
|
+
try {
|
|
163
|
+
outStream.destroy()
|
|
164
|
+
} finally {
|
|
165
|
+
endError(res, e, uuid(), errorTransformer)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
return outStream
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const uwsEnd = res.end
|
|
173
|
+
res.ended = false
|
|
174
|
+
res.end = body => {
|
|
175
|
+
if (res.ended) {
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
// provide writableEnded like node does, with slightly different behavior
|
|
179
|
+
if (!res.writableEnded) {
|
|
180
|
+
res.flushHeaders()
|
|
181
|
+
uwsEnd.call(res, body)
|
|
182
|
+
res.writableEnded = true
|
|
183
|
+
res.ended = true
|
|
184
|
+
}
|
|
185
|
+
if (typeof res.onEnd === 'function') {
|
|
186
|
+
res.onEnd()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
res.redirect = function (code, location) {
|
|
191
|
+
if (arguments.length === 1) {
|
|
192
|
+
location = code
|
|
193
|
+
code = httpStatusCodes.MOVED_PERMANENTLY
|
|
194
|
+
}
|
|
195
|
+
return finalizeResponse(req, res, {
|
|
196
|
+
statusCode: code,
|
|
197
|
+
headers: {
|
|
198
|
+
location: location
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
res.send = (body) => {
|
|
203
|
+
finalizeResponse(req, res, body)
|
|
204
|
+
}
|
|
205
|
+
res.json = res.send
|
|
206
|
+
res.setCookie = setCookie(res)
|
|
207
|
+
res.cookie = res.setCookie
|
|
208
|
+
return res
|
|
209
|
+
}
|