@trpc/server
Version:
1 lines • 15.1 kB
Source Map (JSON)
{"version":3,"file":"node-http-BUlb5EdB.mjs","names":[],"sources":["../src/adapters/node-http/incomingMessageToRequest.ts","../src/adapters/node-http/writeResponse.ts","../src/adapters/node-http/nodeHTTPRequestHandler.ts"],"sourcesContent":["import type * as http from 'http';\nimport { IncomingMessage } from 'node:http';\nimport { TRPCError } from '../../@trpc/server';\nimport type { NodeHTTPRequest, NodeHTTPResponse } from './types';\n\nfunction createBody(\n req: NodeHTTPRequest,\n opts: {\n /**\n * Max body size in bytes. If the body is larger than this, the request will be aborted\n */\n maxBodySize: number | null;\n },\n): RequestInit['body'] {\n // Some adapters will pre-parse the body and add it to the request object\n if ('body' in req) {\n if (req.body === undefined) {\n // If body property exists but is undefined, return undefined\n return undefined;\n }\n // If the body is already a string, return it directly\n if (typeof req.body === 'string') {\n return req.body;\n }\n // formData use\n if (req.body instanceof IncomingMessage) {\n return req.body as any;\n }\n // If body exists but isn't a string, stringify it as JSON\n return JSON.stringify(req.body);\n }\n let size = 0;\n let hasClosed = false;\n\n return new ReadableStream({\n start(controller) {\n const onData = (chunk: Buffer) => {\n size += chunk.length;\n if (!opts.maxBodySize || size <= opts.maxBodySize) {\n controller.enqueue(\n new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength),\n );\n return;\n }\n controller.error(\n new TRPCError({\n code: 'PAYLOAD_TOO_LARGE',\n }),\n );\n hasClosed = true;\n req.off('data', onData);\n req.off('end', onEnd);\n };\n\n const onEnd = () => {\n if (hasClosed) {\n return;\n }\n hasClosed = true;\n req.off('data', onData);\n req.off('end', onEnd);\n controller.close();\n };\n\n req.on('data', onData);\n req.on('end', onEnd);\n },\n cancel() {\n req.destroy();\n },\n });\n}\nexport function createURL(req: NodeHTTPRequest): URL {\n try {\n const protocol =\n // http2\n (req.headers[':scheme'] && req.headers[':scheme'] === 'https') ||\n // http1\n (req.socket && 'encrypted' in req.socket && req.socket.encrypted)\n ? 'https:'\n : 'http:';\n\n const host = req.headers.host ?? req.headers[':authority'] ?? 'localhost';\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return new URL(req.url!, `${protocol}//${host}`);\n } catch (cause) {\n throw new TRPCError({\n code: 'BAD_REQUEST',\n message: 'Invalid URL',\n cause,\n });\n }\n}\n\nfunction createHeaders(incoming: http.IncomingHttpHeaders): Headers {\n const headers = new Headers();\n\n for (const key in incoming) {\n const value = incoming[key];\n if (typeof key === 'string' && key.startsWith(':')) {\n // Skip HTTP/2 pseudo-headers\n continue;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(key, item);\n }\n } else if (value != null) {\n headers.append(key, value);\n }\n }\n\n return headers;\n}\n\n/**\n * Convert an [`IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage) to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)\n */\nexport function incomingMessageToRequest(\n req: NodeHTTPRequest,\n res: NodeHTTPResponse,\n opts: {\n /**\n * Max body size in bytes. If the body is larger than this, the request will be aborted\n */\n maxBodySize: number | null;\n },\n): Request {\n const ac = new AbortController();\n\n const onAbort = () => {\n res.off('close', onAbort);\n req.off('aborted', onAbort);\n\n // abort the request\n ac.abort();\n };\n\n res.once('close', onAbort);\n req.once('aborted', onAbort);\n\n // Get host from either regular header or HTTP/2 pseudo-header\n const url = createURL(req);\n\n const init: RequestInit = {\n headers: createHeaders(req.headers),\n method: req.method,\n signal: ac.signal,\n };\n\n if (req.method !== 'GET' && req.method !== 'HEAD') {\n init.body = createBody(req, opts);\n\n // init.duplex = 'half' must be set when body is a ReadableStream, and Node follows the spec.\n // However, this property is not defined in the TypeScript types for RequestInit, so we have\n // to cast it here in order to set it without a type error.\n // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex\n // @ts-expect-error this is fine\n init.duplex = 'half';\n }\n\n const request = new Request(url, init);\n\n return request;\n}\n","// eslint-disable-next-line no-restricted-imports\nimport { isAbortError } from '../../unstable-core-do-not-import';\nimport type { NodeHTTPResponse } from './types';\n\nasync function writeResponseBodyChunk(\n res: NodeHTTPResponse,\n chunk: Uint8Array,\n) {\n // useful for debugging 🙃\n // console.debug('writing', new TextDecoder().decode(chunk));\n\n if (res.write(chunk) === false) {\n await new Promise<void>((resolve, reject) => {\n const onError = (err: unknown) => {\n reject(err);\n cleanup();\n };\n const onDrain = () => {\n resolve();\n cleanup();\n };\n const cleanup = () => {\n res.off('error', onError);\n res.off('drain', onDrain);\n };\n res.once('error', onError);\n res.once('drain', onDrain);\n });\n }\n}\n/**\n * @internal\n */\n\nexport async function writeResponseBody(opts: {\n res: NodeHTTPResponse;\n signal: AbortSignal;\n body: NonNullable<Response['body']>;\n}) {\n const { res } = opts;\n\n try {\n const writableStream = new WritableStream({\n async write(chunk) {\n await writeResponseBodyChunk(res, chunk);\n res.flush?.();\n },\n });\n\n await opts.body.pipeTo(writableStream, {\n signal: opts.signal,\n });\n } catch (err) {\n if (isAbortError(err)) {\n return;\n }\n throw err;\n }\n}\n/**\n * @internal\n */\n\nexport async function writeResponse(opts: {\n request: Request;\n response: Response;\n rawResponse: NodeHTTPResponse;\n}) {\n const { response, rawResponse } = opts;\n\n // Only override status code if it hasn't been explicitly set in a procedure etc\n if (rawResponse.statusCode === 200) {\n rawResponse.statusCode = response.status;\n }\n for (const [key, value] of response.headers) {\n if (key.toLowerCase() === 'set-cookie') continue;\n rawResponse.setHeader(key, value);\n }\n const cookies = response.headers.getSetCookie();\n if (cookies.length > 0) {\n rawResponse.setHeader('set-cookie', cookies);\n }\n try {\n if (response.body) {\n await writeResponseBody({\n res: rawResponse,\n signal: opts.request.signal,\n body: response.body,\n });\n }\n } catch (err) {\n if (!rawResponse.headersSent) {\n rawResponse.statusCode = 500;\n }\n throw err;\n } finally {\n rawResponse.end();\n }\n}\n","/**\n * If you're making an adapter for tRPC and looking at this file for reference, you should import types and functions from `@trpc/server` and `@trpc/server/http`\n *\n * @example\n * ```ts\n * import type { AnyTRPCRouter } from '@trpc/server'\n * import type { HTTPBaseHandlerOptions } from '@trpc/server/http'\n * ```\n */\n\n// @trpc/server\n\nimport {\n getTRPCErrorFromUnknown,\n transformTRPCResponse,\n type AnyRouter,\n} from '../../@trpc/server';\nimport type { ResolveHTTPRequestOptionsContextFn } from '../../@trpc/server/http';\nimport { resolveResponse } from '../../@trpc/server/http';\n// eslint-disable-next-line no-restricted-imports\nimport { getErrorShape, run } from '../../unstable-core-do-not-import';\nimport { incomingMessageToRequest } from './incomingMessageToRequest';\nimport type {\n NodeHTTPRequest,\n NodeHTTPRequestHandlerOptions,\n NodeHTTPResponse,\n} from './types';\nimport { writeResponse } from './writeResponse';\n\n/**\n * @internal\n */\nexport function internal_exceptionHandler<\n TRouter extends AnyRouter,\n TRequest extends NodeHTTPRequest,\n TResponse extends NodeHTTPResponse,\n>(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>) {\n return (cause: unknown) => {\n const { res, req } = opts;\n const error = getTRPCErrorFromUnknown(cause);\n\n const shape = getErrorShape({\n config: opts.router._def._config,\n error,\n type: 'unknown',\n path: undefined,\n input: undefined,\n ctx: undefined,\n });\n\n opts.onError?.({\n req,\n error,\n type: 'unknown',\n path: undefined,\n input: undefined,\n ctx: undefined,\n });\n\n const transformed = transformTRPCResponse(opts.router._def._config, {\n error: shape,\n });\n\n res.statusCode = shape.data.httpStatus;\n res.end(JSON.stringify(transformed));\n };\n}\n\n/**\n * @remark the promise never rejects\n */\nexport async function nodeHTTPRequestHandler<\n TRouter extends AnyRouter,\n TRequest extends NodeHTTPRequest,\n TResponse extends NodeHTTPResponse,\n>(opts: NodeHTTPRequestHandlerOptions<TRouter, TRequest, TResponse>) {\n return new Promise<void>((resolve) => {\n const handleViaMiddleware =\n opts.middleware ?? ((_req, _res, next) => next());\n\n opts.res.once('finish', () => {\n resolve();\n });\n return handleViaMiddleware(opts.req, opts.res, (err: unknown) => {\n run(async () => {\n const request = incomingMessageToRequest(opts.req, opts.res, {\n maxBodySize: opts.maxBodySize ?? null,\n });\n\n // Build tRPC dependencies\n const createContext: ResolveHTTPRequestOptionsContextFn<\n TRouter\n > = async (innerOpts) => {\n return await opts.createContext?.({\n ...opts,\n ...innerOpts,\n });\n };\n\n const response = await resolveResponse({\n ...opts,\n req: request,\n error: err ? getTRPCErrorFromUnknown(err) : null,\n createContext,\n onError(o) {\n opts?.onError?.({\n ...o,\n req: opts.req,\n });\n },\n });\n\n await writeResponse({\n request,\n response,\n rawResponse: opts.res,\n });\n }).catch(internal_exceptionHandler(opts));\n });\n });\n}\n"],"mappings":"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"}