UNPKG

trpc-uwebsockets

Version:

tRPC adapter for uWebSockets.js server

184 lines 5.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decorateHttpResponse = decorateHttpResponse; exports.uWsToRequestNoBody = uWsToRequestNoBody; exports.uWsToRequest = uWsToRequest; exports.createURL = createURL; exports.uWsSendResponse = uWsSendResponse; exports.uWsSendResponseStreamed = uWsSendResponseStreamed; const server_1 = require("@trpc/server"); function decorateHttpResponse(res, ssl = false) { const resDecorated = res; resDecorated.aborted = false; resDecorated.ssl = ssl; return resDecorated; } function uWsToRequestNoBody(req, res) { const headers = createHeaders(req); const method = req.getCaseSensitiveMethod().toUpperCase(); const isSsl = res.ssl; const url = createURL(req, isSsl ? 'https' : 'http'); const init = { headers: headers, method: method, }; const request = new Request(url, init); return request; } function uWsToRequest(req, res, opts) { const ac = new AbortController(); const onAbort = () => { res.aborted = true; ac.abort(); }; res.onAborted(onAbort); const headers = createHeaders(req); const method = req.getCaseSensitiveMethod().toUpperCase(); const isSsl = res.ssl; const url = createURL(req, isSsl ? 'https' : 'http'); const init = { headers: headers, method: method, signal: ac.signal, }; if (method !== 'GET' && method !== 'HEAD') { init.body = createBody(res, opts); init.duplex = 'half'; } const request = new Request(url, init); return request; } function createHeaders(req) { const headers = new Headers(); req.forEach((key, value) => { /* istanbul ignore next -- @preserve */ if (key.startsWith(':')) { // Skip HTTP/2 pseudo-headers return; } if (value.length != 0) { headers.append(key, value); } }); return headers; } function createURL(req, protocol) { try { const host = req.getHeader('host') ?? 'localhost'; const path = req.getUrl(); const qs = req.getQuery(); if (qs) { return new URL(`${path}?${qs}`, `${protocol}://${host}`); } else { return new URL(path, `${protocol}://${host}`); } } catch (cause) { throw new server_1.TRPCError({ code: 'BAD_REQUEST', message: 'Invalid URL', cause, }); } } function createBody(res, opts) { let size = 0; let hasClosed = false; return new ReadableStream({ start(controller) { res.onData((tempChunk, isLast) => { if (hasClosed) return; // This copies the buffer *immediately* and avoids using a detached buffer const chunk = new Uint8Array(tempChunk.byteLength); chunk.set(new Uint8Array(tempChunk)); // must be done before enqueue size += chunk.byteLength; if (!opts.maxBodySize || size <= opts.maxBodySize) { controller.enqueue(chunk); if (isLast) { hasClosed = true; controller.close(); } } else { hasClosed = true; controller.error(new server_1.TRPCError({ code: 'PAYLOAD_TOO_LARGE' })); } }); res.onAborted(() => { if (!hasClosed) { res.aborted = true; hasClosed = true; controller.error(new server_1.TRPCError({ code: 'CLIENT_CLOSED_REQUEST' })); } }); }, cancel() { res.aborted = true; res.cork(() => { res.close(); }); }, }); } async function uWsSendResponse(res, fetchRes) { const unsteamed_text = await fetchRes.text(); if (res.aborted) return; res.cork(() => { res.writeStatus(fetchRes.status.toString()); fetchRes.headers.forEach((value, key) => { res.writeHeader(key, value); }); res.end(unsteamed_text); }); } async function uWsSendResponseStreamed(fetchRes, res) { if (res.aborted) return; res.cork(() => { res.writeStatus(fetchRes.status.toString()); fetchRes.headers.forEach((value, key) => { res.writeHeader(key, value); }); }); if (!fetchRes.body) { if (!res.aborted) { res.cork(() => { res.end(); }); } return; } const reader = fetchRes.body.getReader(); try { while (true) { if (res.aborted) break; const { value, done } = await reader.read(); if (res.aborted) break; if (done) { if (!res.aborted) { res.cork(() => { res.end(); }); } break; } if (!res.aborted) { res.cork(() => { res.write(value); }); } } } finally { // let the reader know that nothing will be read from it // potentially useful during res.aborted early return; also should theoretically help GC clean up the reader? // this may not be needed, but lets keep it here just in case reader.releaseLock(); } } //# sourceMappingURL=fetchCompat.js.map