@whatwg-node/server
Version:
Fetch API compliant HTTP Server adapter
228 lines (227 loc) • 6.82 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.fakePromise = void 0;
exports.isUWSResponse = isUWSResponse;
exports.getRequestFromUWSRequest = getRequestFromUWSRequest;
exports.createWritableFromUWS = createWritableFromUWS;
exports.sendResponseToUwsOpts = sendResponseToUwsOpts;
const promise_helpers_1 = require("@whatwg-node/promise-helpers");
Object.defineProperty(exports, "fakePromise", { enumerable: true, get: function () { return promise_helpers_1.fakePromise; } });
function isUWSResponse(res) {
return !!res.onData;
}
function getRequestFromUWSRequest({ req, res, fetchAPI, controller, }) {
const method = req.getMethod();
let duplex;
const chunks = [];
const pushFns = [
(chunk) => {
chunks.push(chunk);
},
];
const push = (chunk) => {
for (const pushFn of pushFns) {
pushFn(chunk);
}
};
let stopped = false;
const stopFns = [
() => {
stopped = true;
},
];
const stop = () => {
for (const stopFn of stopFns) {
stopFn();
}
};
res.onData(function (ab, isLast) {
push(Buffer.from(Buffer.from(ab, 0, ab.byteLength)));
if (isLast) {
stop();
}
});
let getReadableStream;
if (method !== 'get' && method !== 'head') {
duplex = 'half';
controller.signal.addEventListener('abort', () => {
stop();
}, { once: true });
let readableStream;
getReadableStream = () => {
if (!readableStream) {
readableStream = new fetchAPI.ReadableStream({
start(controller) {
for (const chunk of chunks) {
controller.enqueue(chunk);
}
if (stopped) {
controller.close();
return;
}
pushFns.push((chunk) => {
controller.enqueue(chunk);
});
stopFns.push(() => {
if (controller.desiredSize) {
controller.close();
}
});
},
});
}
return readableStream;
};
}
const headers = new fetchAPI.Headers();
req.forEach((key, value) => {
headers.append(key, value);
});
let url = `http://localhost${req.getUrl()}`;
const query = req.getQuery();
if (query) {
url += `?${query}`;
}
let buffer;
function getBody() {
if (!getReadableStream) {
return null;
}
if (stopped) {
return getBufferFromChunks();
}
return getReadableStream();
}
const request = new fetchAPI.Request(url, {
method,
headers,
get body() {
return getBody();
},
signal: controller.signal,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - not in the TS types yet
duplex,
});
function getBufferFromChunks() {
if (!buffer) {
buffer = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks);
}
return buffer;
}
function collectBuffer() {
if (stopped) {
return (0, promise_helpers_1.fakePromise)(getBufferFromChunks());
}
return new Promise((resolve, reject) => {
try {
stopFns.push(() => {
resolve(getBufferFromChunks());
});
}
catch (e) {
reject(e);
}
});
}
Object.defineProperties(request, {
body: {
get() {
return getBody();
},
configurable: true,
enumerable: true,
},
json: {
value() {
return collectBuffer()
.then(b => b.toString('utf8'))
.then(t => JSON.parse(t));
},
configurable: true,
enumerable: true,
},
text: {
value() {
return collectBuffer().then(b => b.toString('utf8'));
},
configurable: true,
enumerable: true,
},
arrayBuffer: {
value() {
return collectBuffer();
},
configurable: true,
enumerable: true,
},
});
return request;
}
function createWritableFromUWS(uwsResponse, fetchAPI) {
return new fetchAPI.WritableStream({
write(chunk) {
uwsResponse.cork(() => {
uwsResponse.write(chunk);
});
},
close() {
uwsResponse.cork(() => {
uwsResponse.end();
});
},
});
}
function sendResponseToUwsOpts(uwsResponse, fetchResponse, controller, fetchAPI) {
if (!fetchResponse) {
uwsResponse.writeStatus('404 Not Found');
uwsResponse.end();
return;
}
const bufferOfRes = fetchResponse._buffer;
if (controller.signal.aborted) {
return;
}
uwsResponse.cork(() => {
uwsResponse.writeStatus(`${fetchResponse.status} ${fetchResponse.statusText}`);
for (const [key, value] of fetchResponse.headers) {
// content-length causes an error with Node.js's fetch
if (key !== 'content-length') {
if (key === 'set-cookie') {
const setCookies = fetchResponse.headers.getSetCookie?.();
if (setCookies) {
for (const setCookie of setCookies) {
uwsResponse.writeHeader(key, setCookie);
}
continue;
}
}
uwsResponse.writeHeader(key, value);
}
}
if (bufferOfRes) {
uwsResponse.end(bufferOfRes);
}
else if (!fetchResponse.body) {
uwsResponse.end();
}
});
if (bufferOfRes || !fetchResponse.body) {
return;
}
controller.signal.addEventListener('abort', () => {
if (!fetchResponse.body?.locked) {
fetchResponse.body?.cancel(controller.signal.reason);
}
}, { once: true });
return fetchResponse.body
.pipeTo(createWritableFromUWS(uwsResponse, fetchAPI), {
signal: controller.signal,
})
.catch(err => {
if (controller.signal.aborted) {
return;
}
throw err;
});
}
;