Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/http-transport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@nmtjs/common": "workspace:*",
"@nmtjs/gateway": "workspace:*",
"@nmtjs/protocol": "workspace:*",
"@nmtjs/server": "workspace:*",
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.67.0"
},
"peerDependencies": {
Expand All @@ -62,6 +63,7 @@
"@nmtjs/core": "workspace:*",
"@nmtjs/gateway": "workspace:*",
"@nmtjs/protocol": "workspace:*",
"@nmtjs/server": "workspace:*",
"@types/bun": "^1.3.0",
"@types/deno": "^2.3.0",
"@types/node": "^24.13.2",
Expand Down
39 changes: 39 additions & 0 deletions packages/http-transport/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type {
ServerHost,
ServerHostOptions,
ServerRuntimeName,
} from '@nmtjs/server'

import type { HttpAdapterParams, HttpAdapterServer } from './types.ts'

/**
* Shared-server mode mounts onto the provided host; listen mode owns a
* private one — either way a ServerHost is the only socket owner, so both
* modes exercise the same server code path.
*/
export function createHostAdapter<R extends ServerRuntimeName>(
createHost: (options: ServerHostOptions<R>) => ServerHost<R>,
params: HttpAdapterParams<R>,
): HttpAdapterServer {
let host: ServerHost<R>
if (params.server) {
host = params.server
} else if (params.listen) {
host = createHost({
listen: params.listen,
tls: params.tls,
maxRequestBodySize: params.maxRequestBodySize,
runtime: params.runtime,
})
} else {
throw new Error('Either `server` or `listen` option is required')
}
host.setFetchHandler(params.fetchHandler)
return {
get runtime() {
return host.native
},
start: () => host.start(),
stop: () => host.stop(),
}
}
4 changes: 2 additions & 2 deletions packages/http-transport/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type { MetadataKind } from '@nmtjs/application'
import { createMeta } from '@nmtjs/application'
import { ErrorCode } from '@nmtjs/protocol'

// Matches Bun's maxRequestBodySize default so all runtimes behave the same
export const DEFAULT_MAX_REQUEST_BODY_SIZE = 128 * 1024 * 1024
// Single source in @nmtjs/server: the host enforces the same default bound
export { DEFAULT_MAX_REQUEST_BODY_SIZE } from '@nmtjs/server'

export enum HttpStatus {
Continue = 100,
Expand Down
81 changes: 5 additions & 76 deletions packages/http-transport/src/runtimes/bun.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,15 @@
import type { ApplicationTransport } from '@nmtjs/application'
import type { ConnectionType } from '@nmtjs/protocol'
import { ProxyableTransportType } from '@nmtjs/gateway'
import { createServerHost } from '@nmtjs/server/bun'

import type {
HttpAdapterParams,
HttpAdapterServer,
HttpTransportOptions,
} from '../types.ts'
import type { HttpAdapterParams, HttpTransportOptions } from '../types.ts'
import { createHostAdapter } from '../adapter.ts'
import * as injectables from '../injectables.ts'
import { createHTTPTransportWorker } from '../server.ts'
import {
InternalServerErrorHttpResponse,
NotFoundHttpResponse,
OkResponse,
} from '../utils.ts'

function adapterFactory(params: HttpAdapterParams<'bun'>): HttpAdapterServer {
let server: Bun.Server<undefined> | null = null

function createServer() {
const routes =
typeof params.runtime?.routes === 'object' && params.runtime.routes
? params.runtime.routes
: {}

return globalThis.Bun.serve({
...params.runtime,
// Bun's own default (128MiB) applies when neither option is set
maxRequestBodySize:
params.runtime?.maxRequestBodySize ?? params.maxRequestBodySize,
unix: params.listen.unix as string,
port: params.listen.port ?? 0,
hostname: params.listen.hostname,
reusePort: params.listen.reusePort,
tls: params.tls
? {
cert: params.tls.cert,
key: params.tls.key,
passphrase: params.tls.passphrase,
}
: undefined,
routes: Object.assign({}, routes, {
'/healthy': { GET: OkResponse },
}) as any,
async fetch(request) {
const url = new URL(request.url)
try {
if (request.headers.get('upgrade') === 'websocket')
return NotFoundHttpResponse()
const { body, headers, method } = request
return await params.fetchHandler(
{ url, method, headers },
body,
request.signal,
)
} catch (err) {
// TODO: proper logging
console.error(err)
// params.logger.error({ err }, 'Error in fetch handler')
return InternalServerErrorHttpResponse()
}
},
} as any)
}

return {
runtime: {
get bun() {
return server!
},
},
start: async () => {
server = createServer()
return server!.url.href
},
stop: async () => {
if (server) {
await server.stop()
server = null
}
},
}
function adapterFactory(params: HttpAdapterParams<'bun'>) {
return createHostAdapter(createServerHost, params)
}

export const HttpTransport: ApplicationTransport<
Expand Down
123 changes: 5 additions & 118 deletions packages/http-transport/src/runtimes/deno.ts
Original file line number Diff line number Diff line change
@@ -1,128 +1,15 @@
import type { ApplicationTransport } from '@nmtjs/application'
import type { ConnectionType } from '@nmtjs/protocol'
import { ProxyableTransportType } from '@nmtjs/gateway'
import { createServerHost } from '@nmtjs/server/deno'

import type {
DenoServer,
HttpAdapterParams,
HttpAdapterServer,
HttpTransportOptions,
} from '../types.ts'
import type { HttpAdapterParams, HttpTransportOptions } from '../types.ts'
import { createHostAdapter } from '../adapter.ts'
import * as injectables from '../injectables.ts'
import { createHTTPTransportWorker } from '../server.ts'
import {
InternalServerErrorHttpResponse,
NotFoundHttpResponse,
OkResponse,
} from '../utils.ts'

interface DenoNetAddr {
transport: 'tcp' | 'udp'
hostname: string
port: number
}

interface DenoUnixAddr {
transport: 'unix' | 'unixpacket'
path: string
}

interface DenoVsockAddr {
transport: 'vsock'
cid: number
port: number
}

type DenoAddr = DenoNetAddr | DenoUnixAddr | DenoVsockAddr

function adapterFactory(params: HttpAdapterParams<'deno'>): HttpAdapterServer {
let server: DenoServer | null = null

function createServer() {
const listenOptions = params.listen.unix
? { path: params.listen.unix }
: {
port: params.listen.port,
hostname: params.listen.hostname,
reusePort: params.listen.reusePort,
}
const options = {
...listenOptions,
tls: params.tls
? {
cert: params.tls.cert,
key: params.tls.key,
passphrase: params.tls.passphrase,
}
: undefined,
}

return new Promise<{ server: DenoServer; addr: DenoAddr }>((resolve) => {
const server = globalThis.Deno.serve({
...params.runtime,
...options,
handler: async (request: Request) => {
const url = new URL(request.url)
if (url.pathname === '/healthy') {
return OkResponse()
}
try {
if (request.headers.get('upgrade') === 'websocket') {
return NotFoundHttpResponse()
}
const { headers, method, body } = request
return await params.fetchHandler(
{ url, method, headers },
body,
request.signal,
)
} catch (err) {
// TODO: proper logging
console.error(err)
// params.logger.error({ err }, 'Error in fetch handler')
return InternalServerErrorHttpResponse()
}
},
onListen(addr: DenoAddr) {
setTimeout(() => {
resolve({ server, addr })
}, 1)
},
})
})
}

return {
runtime: {
get deno() {
return server!
},
},
start: async () => {
const { server: _server, addr } = await createServer()
server = _server
switch (addr.transport) {
case 'unix':
case 'unixpacket':
return `unix://${addr.path}`
case 'tcp':
case 'udp': {
const proto = params.tls ? 'https' : 'http'
return `${proto}://${addr.hostname}:${addr.port}`
}
case 'vsock':
return `vsock://${addr.cid}:${addr.port}`
default:
throw new Error(`Unsupported address transport`)
}
},
stop: async () => {
if (server) {
await server.shutdown()
server = null
}
},
}
function adapterFactory(params: HttpAdapterParams<'deno'>) {
return createHostAdapter(createServerHost, params)
}

export const HttpTransport: ApplicationTransport<
Expand Down
Loading
Loading