@nmtjs/http-transport 0.15.0-beta.2 → 0.15.0-beta.4
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/dist/constants.d.ts +80 -0
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/injectables.d.ts +4 -0
- package/dist/injectables.js +1 -0
- package/dist/injectables.js.map +1 -0
- package/dist/runtimes/bun.d.ts +6 -0
- package/dist/runtimes/bun.js +1 -0
- package/dist/runtimes/bun.js.map +1 -0
- package/dist/runtimes/deno.d.ts +6 -0
- package/dist/runtimes/deno.js +1 -0
- package/dist/runtimes/deno.js.map +1 -0
- package/dist/runtimes/node.d.ts +6 -0
- package/dist/runtimes/node.js +1 -0
- package/dist/runtimes/node.js.map +1 -0
- package/dist/server.d.ts +16 -0
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +72 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +1 -0
- package/dist/utils.js.map +1 -0
- package/package.json +11 -10
- package/src/constants.ts +152 -0
- package/src/index.ts +4 -0
- package/src/injectables.ts +11 -0
- package/src/runtimes/bun.ts +91 -0
- package/src/runtimes/deno.ts +139 -0
- package/src/runtimes/node.ts +157 -0
- package/src/server.ts +365 -0
- package/src/types.ts +102 -0
- package/src/utils.ts +28 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Transport } from '@nmtjs/gateway'
|
|
2
|
+
import type { ConnectionType } from '@nmtjs/protocol'
|
|
3
|
+
import { ProxyableTransportType } from '@nmtjs/gateway'
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
HttpAdapterParams,
|
|
7
|
+
HttpAdapterServer,
|
|
8
|
+
HttpTransportOptions,
|
|
9
|
+
} from '../types.ts'
|
|
10
|
+
import * as injectables from '../injectables.ts'
|
|
11
|
+
import { createHTTPTransportWorker } from '../server.ts'
|
|
12
|
+
import {
|
|
13
|
+
InternalServerErrorHttpResponse,
|
|
14
|
+
NotFoundHttpResponse,
|
|
15
|
+
StatusResponse,
|
|
16
|
+
} from '../utils.ts'
|
|
17
|
+
|
|
18
|
+
function adapterFactory(params: HttpAdapterParams<'bun'>): HttpAdapterServer {
|
|
19
|
+
let server: Bun.Server<undefined> | null = null
|
|
20
|
+
|
|
21
|
+
function createServer() {
|
|
22
|
+
return globalThis.Bun.serve({
|
|
23
|
+
...params.runtime,
|
|
24
|
+
unix: params.listen.unix as string,
|
|
25
|
+
port: params.listen.port ?? 0,
|
|
26
|
+
hostname: params.listen.hostname,
|
|
27
|
+
reusePort: params.listen.reusePort,
|
|
28
|
+
tls: params.tls
|
|
29
|
+
? {
|
|
30
|
+
cert: params.tls.cert,
|
|
31
|
+
key: params.tls.key,
|
|
32
|
+
passphrase: params.tls.passphrase,
|
|
33
|
+
}
|
|
34
|
+
: undefined,
|
|
35
|
+
// @ts-expect-error
|
|
36
|
+
routes: {
|
|
37
|
+
...params.runtime?.routes,
|
|
38
|
+
'/healthy': { GET: StatusResponse },
|
|
39
|
+
},
|
|
40
|
+
async fetch(request, server) {
|
|
41
|
+
const url = new URL(request.url)
|
|
42
|
+
try {
|
|
43
|
+
if (request.headers.get('upgrade') === 'websocket')
|
|
44
|
+
return NotFoundHttpResponse()
|
|
45
|
+
const { body, headers, method } = request
|
|
46
|
+
return await params.fetchHandler(
|
|
47
|
+
{ url, method, headers },
|
|
48
|
+
body,
|
|
49
|
+
request.signal,
|
|
50
|
+
)
|
|
51
|
+
} catch (err) {
|
|
52
|
+
// TODO: proper logging
|
|
53
|
+
console.error(err)
|
|
54
|
+
// params.logger.error({ err }, 'Error in fetch handler')
|
|
55
|
+
return InternalServerErrorHttpResponse()
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
runtime: {
|
|
63
|
+
get bun() {
|
|
64
|
+
return server!
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
start: async () => {
|
|
68
|
+
server = createServer()
|
|
69
|
+
return server!.url.href
|
|
70
|
+
},
|
|
71
|
+
stop: async () => {
|
|
72
|
+
if (server) {
|
|
73
|
+
await server.stop()
|
|
74
|
+
server = null
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const HttpTransport: Transport<
|
|
81
|
+
ConnectionType.Unidirectional,
|
|
82
|
+
HttpTransportOptions<'deno'>,
|
|
83
|
+
typeof injectables,
|
|
84
|
+
ProxyableTransportType.HTTP
|
|
85
|
+
> = {
|
|
86
|
+
proxyable: ProxyableTransportType.HTTP,
|
|
87
|
+
injectables,
|
|
88
|
+
factory(options) {
|
|
89
|
+
return createHTTPTransportWorker(adapterFactory, options)
|
|
90
|
+
},
|
|
91
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { Transport } from '@nmtjs/gateway'
|
|
2
|
+
import type { ConnectionType } from '@nmtjs/protocol'
|
|
3
|
+
import { ProxyableTransportType } from '@nmtjs/gateway'
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
DenoServer,
|
|
7
|
+
HttpAdapterParams,
|
|
8
|
+
HttpAdapterServer,
|
|
9
|
+
HttpTransportOptions,
|
|
10
|
+
} from '../types.ts'
|
|
11
|
+
import * as injectables from '../injectables.ts'
|
|
12
|
+
import { createHTTPTransportWorker } from '../server.ts'
|
|
13
|
+
import {
|
|
14
|
+
InternalServerErrorHttpResponse,
|
|
15
|
+
NotFoundHttpResponse,
|
|
16
|
+
StatusResponse,
|
|
17
|
+
} from '../utils.ts'
|
|
18
|
+
|
|
19
|
+
interface DenoNetAddr {
|
|
20
|
+
transport: 'tcp' | 'udp'
|
|
21
|
+
hostname: string
|
|
22
|
+
port: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface DenoUnixAddr {
|
|
26
|
+
transport: 'unix' | 'unixpacket'
|
|
27
|
+
path: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface DenoVsockAddr {
|
|
31
|
+
transport: 'vsock'
|
|
32
|
+
cid: number
|
|
33
|
+
port: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type DenoAddr = DenoNetAddr | DenoUnixAddr | DenoVsockAddr
|
|
37
|
+
|
|
38
|
+
function adapterFactory(params: HttpAdapterParams<'deno'>): HttpAdapterServer {
|
|
39
|
+
let server: DenoServer | null = null
|
|
40
|
+
|
|
41
|
+
function createServer() {
|
|
42
|
+
const listenOptions = params.listen.unix
|
|
43
|
+
? { path: params.listen.unix }
|
|
44
|
+
: {
|
|
45
|
+
port: params.listen.port,
|
|
46
|
+
hostname: params.listen.hostname,
|
|
47
|
+
reusePort: params.listen.reusePort,
|
|
48
|
+
}
|
|
49
|
+
const options = {
|
|
50
|
+
...listenOptions,
|
|
51
|
+
tls: params.tls
|
|
52
|
+
? {
|
|
53
|
+
cert: params.tls.cert,
|
|
54
|
+
key: params.tls.key,
|
|
55
|
+
passphrase: params.tls.passphrase,
|
|
56
|
+
}
|
|
57
|
+
: undefined,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return new Promise<{ server: DenoServer; addr: DenoAddr }>((resolve) => {
|
|
61
|
+
const server = globalThis.Deno.serve({
|
|
62
|
+
...params.runtime,
|
|
63
|
+
...options,
|
|
64
|
+
handler: async (request: Request, info: any) => {
|
|
65
|
+
const url = new URL(request.url)
|
|
66
|
+
if (url.pathname === '/healthy') {
|
|
67
|
+
return StatusResponse()
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
if (request.headers.get('upgrade') === 'websocket') {
|
|
71
|
+
return NotFoundHttpResponse()
|
|
72
|
+
}
|
|
73
|
+
const { headers, method, body } = request
|
|
74
|
+
return await params.fetchHandler(
|
|
75
|
+
{ url, method, headers },
|
|
76
|
+
body,
|
|
77
|
+
request.signal,
|
|
78
|
+
)
|
|
79
|
+
} catch (err) {
|
|
80
|
+
// TODO: proper logging
|
|
81
|
+
console.error(err)
|
|
82
|
+
// params.logger.error({ err }, 'Error in fetch handler')
|
|
83
|
+
return InternalServerErrorHttpResponse()
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
onListen(addr: DenoAddr) {
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
resolve({ server, addr })
|
|
89
|
+
}, 1)
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
runtime: {
|
|
97
|
+
get deno() {
|
|
98
|
+
return server!
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
start: async () => {
|
|
102
|
+
const { server: _server, addr } = await createServer()
|
|
103
|
+
server = _server
|
|
104
|
+
switch (addr.transport) {
|
|
105
|
+
case 'unix':
|
|
106
|
+
case 'unixpacket':
|
|
107
|
+
return `unix://${addr.path}`
|
|
108
|
+
case 'tcp':
|
|
109
|
+
case 'udp': {
|
|
110
|
+
const proto = params.tls ? 'https' : 'http'
|
|
111
|
+
return `${proto}://${addr.hostname}:${addr.port}`
|
|
112
|
+
}
|
|
113
|
+
case 'vsock':
|
|
114
|
+
return `vsock://${addr.cid}:${addr.port}`
|
|
115
|
+
default:
|
|
116
|
+
throw new Error(`Unsupported address transport`)
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
stop: async () => {
|
|
120
|
+
if (server) {
|
|
121
|
+
await server.shutdown()
|
|
122
|
+
server = null
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const HttpTransport: Transport<
|
|
129
|
+
ConnectionType.Unidirectional,
|
|
130
|
+
HttpTransportOptions<'deno'>,
|
|
131
|
+
typeof injectables,
|
|
132
|
+
ProxyableTransportType.HTTP
|
|
133
|
+
> = {
|
|
134
|
+
proxyable: ProxyableTransportType.HTTP,
|
|
135
|
+
injectables,
|
|
136
|
+
factory(options) {
|
|
137
|
+
return createHTTPTransportWorker(adapterFactory, options)
|
|
138
|
+
},
|
|
139
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import {} from 'node:dns'
|
|
2
|
+
import { setTimeout } from 'node:timers/promises'
|
|
3
|
+
|
|
4
|
+
import type { Transport } from '@nmtjs/gateway'
|
|
5
|
+
import type { ConnectionType } from '@nmtjs/protocol'
|
|
6
|
+
import { ProxyableTransportType } from '@nmtjs/gateway'
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
HttpAdapterParams,
|
|
10
|
+
HttpAdapterServer,
|
|
11
|
+
HttpTransportOptions,
|
|
12
|
+
} from '../types.ts'
|
|
13
|
+
import * as injectables from '../injectables.ts'
|
|
14
|
+
import { createHTTPTransportWorker } from '../server.ts'
|
|
15
|
+
import {
|
|
16
|
+
InternalServerErrorHttpResponse,
|
|
17
|
+
NotFoundHttpResponse,
|
|
18
|
+
StatusResponse,
|
|
19
|
+
} from '../utils.ts'
|
|
20
|
+
|
|
21
|
+
import { App, SSLApp, us_socket_local_port } from 'uWebSockets.js'
|
|
22
|
+
|
|
23
|
+
function adapterFactory(params: HttpAdapterParams<'node'>): HttpAdapterServer {
|
|
24
|
+
const server = params.tls
|
|
25
|
+
? SSLApp({
|
|
26
|
+
passphrase: params.tls.passphrase,
|
|
27
|
+
key_file_name: params.tls.key,
|
|
28
|
+
cert_file_name: params.tls.cert,
|
|
29
|
+
})
|
|
30
|
+
: App()
|
|
31
|
+
|
|
32
|
+
server
|
|
33
|
+
.get('/healthy', async (res) => {
|
|
34
|
+
res.onAborted(() => {})
|
|
35
|
+
const response = StatusResponse()
|
|
36
|
+
res.cork(async () => {
|
|
37
|
+
res
|
|
38
|
+
.writeStatus(`${response.status} ${response.statusText}`)
|
|
39
|
+
.end(await response.arrayBuffer())
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
.any('/*', async (res, req) => {
|
|
43
|
+
const controller = new AbortController()
|
|
44
|
+
res.onAborted(() => {
|
|
45
|
+
res.aborted = true
|
|
46
|
+
controller.abort()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
let response = NotFoundHttpResponse()
|
|
50
|
+
|
|
51
|
+
const headers = new Headers()
|
|
52
|
+
const method = req.getMethod()
|
|
53
|
+
req.forEach((k, v) => headers.append(k, v))
|
|
54
|
+
|
|
55
|
+
const host = headers.get('host') || 'localhost'
|
|
56
|
+
const proto =
|
|
57
|
+
headers.get('x-forwarded-proto') || params.tls ? 'https' : 'http'
|
|
58
|
+
const url = new URL(req.getUrl(), `${proto}://${host}`)
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const body = new ReadableStream({
|
|
62
|
+
start(controller) {
|
|
63
|
+
res.onData((chunk, isLast) => {
|
|
64
|
+
if (chunk) {
|
|
65
|
+
const copy = Buffer.allocUnsafe(chunk.byteLength)
|
|
66
|
+
copy.set(new Uint8Array(chunk))
|
|
67
|
+
controller.enqueue(copy)
|
|
68
|
+
}
|
|
69
|
+
if (isLast) controller.close()
|
|
70
|
+
})
|
|
71
|
+
res.onAborted(() => controller.error())
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
response = await params.fetchHandler(
|
|
75
|
+
{ url, method, headers },
|
|
76
|
+
body,
|
|
77
|
+
controller.signal,
|
|
78
|
+
)
|
|
79
|
+
} catch (err) {
|
|
80
|
+
// TODO: proper logging
|
|
81
|
+
console.error(err)
|
|
82
|
+
// params.logger.error({ err }, 'Error in fetch handler')
|
|
83
|
+
response = InternalServerErrorHttpResponse()
|
|
84
|
+
}
|
|
85
|
+
if (res.aborted) return undefined
|
|
86
|
+
else {
|
|
87
|
+
res.cork(() => {
|
|
88
|
+
if (res.aborted) return undefined
|
|
89
|
+
res.writeStatus(
|
|
90
|
+
`${response.status.toString()} ${response.statusText}`,
|
|
91
|
+
)
|
|
92
|
+
response.headers.forEach((v, k) => res.writeHeader(k, v))
|
|
93
|
+
})
|
|
94
|
+
if (response.body) {
|
|
95
|
+
try {
|
|
96
|
+
const reader = response.body.getReader()
|
|
97
|
+
let chunk = await reader.read()
|
|
98
|
+
do {
|
|
99
|
+
if (res.aborted) break
|
|
100
|
+
if (chunk.value) res.cork(() => res.write(chunk.value!))
|
|
101
|
+
chunk = await reader.read()
|
|
102
|
+
} while (!chunk.done)
|
|
103
|
+
if (!res.aborted) res.cork(() => res.end())
|
|
104
|
+
} catch {
|
|
105
|
+
if (!res.aborted) res.cork(() => res.close())
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
if (!res.aborted) res.cork(() => res.end())
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
runtime: { node: server },
|
|
115
|
+
start: () =>
|
|
116
|
+
new Promise<string>((resolve, reject) => {
|
|
117
|
+
if (params.listen.unix) {
|
|
118
|
+
server.listen_unix((socket) => {
|
|
119
|
+
if (socket) {
|
|
120
|
+
resolve('unix://' + params.listen.unix)
|
|
121
|
+
} else {
|
|
122
|
+
reject(new Error('Failed to start WebSockets server'))
|
|
123
|
+
}
|
|
124
|
+
}, params.listen.unix)
|
|
125
|
+
} else if (typeof params.listen.port === 'number') {
|
|
126
|
+
const proto = params.tls ? 'https' : 'http'
|
|
127
|
+
const hostname = params.listen.hostname || '127.0.0.1'
|
|
128
|
+
|
|
129
|
+
server.listen(hostname, params.listen.port, (socket) => {
|
|
130
|
+
if (socket) {
|
|
131
|
+
resolve(`${proto}://${hostname}:${us_socket_local_port(socket)}`)
|
|
132
|
+
} else {
|
|
133
|
+
reject(new Error('Failed to start WebSockets server'))
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
} else {
|
|
137
|
+
reject(new Error('Invalid listen parameters'))
|
|
138
|
+
}
|
|
139
|
+
}),
|
|
140
|
+
stop: () => {
|
|
141
|
+
server.close()
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const HttpTransport: Transport<
|
|
147
|
+
ConnectionType.Unidirectional,
|
|
148
|
+
HttpTransportOptions<'node'>,
|
|
149
|
+
typeof injectables,
|
|
150
|
+
ProxyableTransportType.HTTP
|
|
151
|
+
> = {
|
|
152
|
+
proxyable: ProxyableTransportType.HTTP,
|
|
153
|
+
injectables,
|
|
154
|
+
factory(options) {
|
|
155
|
+
return createHTTPTransportWorker(adapterFactory, options)
|
|
156
|
+
},
|
|
157
|
+
}
|