@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.
@@ -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
+ }