ufiber
Version:
Next-gen webserver for node-js developer
125 lines (123 loc) • 4.12 kB
JavaScript
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
const require_tools = require('../utils/tools.cjs');
const require_consts = require('../consts.cjs');
const require_errors = require('../errors.cjs');
let stream = require("stream");
stream = require_rolldown_runtime.__toESM(stream);
//#region src/http/readable.ts
const kUwsRead = Symbol("uws-readable");
var UwsReadable = class extends stream.Readable {
#needsData = false;
#completed = false;
#usedStream = false;
#usedBuffer = false;
[kUwsRead] = {
bytes: 0,
chunks: [],
isDone: false,
buffer: null
};
constructor(res, limit) {
super({ highWaterMark: require_consts.HIGH_WATER_MARK });
res.onData((chunk, isLast) => {
if (this.destroyed) return;
const buf = isLast ? Buffer.from(chunk) : Buffer.from(new Uint8Array(chunk));
this[kUwsRead].chunks.push(buf);
this[kUwsRead].bytes += buf.length;
if (typeof limit !== "undefined" && this[kUwsRead].bytes > limit) return this.destroy(new require_errors.HttpError(413, { message: `Request body ${require_tools.formatBytes(this[kUwsRead].bytes)} exceeds limit: ${require_tools.formatBytes(limit)}` }));
if (isLast) {
this[kUwsRead].isDone = true;
if (!this.#usedStream && !this.#completed && (this.#usedBuffer || this.listenerCount("complete") > 0)) this.#complete();
}
if (this.#needsData) {
this.#needsData = false;
this._read();
}
});
}
_read() {
if (this.#usedBuffer) {
this.destroy(/* @__PURE__ */ new Error("Cannot read stream after getBuffer() has been called"));
return;
}
this.#usedStream = true;
while (this[kUwsRead].chunks.length > 0) {
const chunk = this[kUwsRead].chunks.shift();
if (!this.push(chunk)) break;
}
if (this[kUwsRead].isDone && this[kUwsRead].chunks.length === 0) this.push(null);
else if (this[kUwsRead].chunks.length === 0) this.#needsData = true;
}
_destroy(err, cb) {
const r = this[kUwsRead];
r.isDone = true;
r.chunks = [];
r.buffer = null;
this.#needsData = false;
this.#completed = false;
super._destroy(err, cb);
}
once(event, listener) {
if (event === "complete") {
if (this.#usedStream) throw new Error("Cannot use complete event after stream has been read");
if (this.#completed && this[kUwsRead].buffer) {
queueMicrotask(() => listener(this[kUwsRead].buffer));
return this;
}
if (this[kUwsRead].isDone && !this.#completed) this.#complete();
}
return super.once(event, listener);
}
#bodyBuf = () => {
if (this[kUwsRead].buffer) return this[kUwsRead].buffer;
const chunksLen = this[kUwsRead].chunks.length;
let buffer;
if (chunksLen === 0) buffer = Buffer.allocUnsafe(0);
else if (chunksLen === 1) buffer = this[kUwsRead].chunks[0];
else buffer = Buffer.concat(this[kUwsRead].chunks, this[kUwsRead].bytes);
this[kUwsRead].buffer = buffer;
this[kUwsRead].chunks = [];
return buffer;
};
#complete = () => {
if (this.#completed) return;
this.#completed = true;
queueMicrotask(() => {
if (this.destroyed) return;
const body = this.#bodyBuf();
this.emit("complete", body);
});
};
getBuffer = () => new Promise((resolve, reject) => {
if (this.destroyed) return reject(/* @__PURE__ */ new Error("Stream has been destroyed"));
if (this.#usedStream) return reject(/* @__PURE__ */ new Error("Cannot get buffer after stream has been read"));
this.#usedBuffer = true;
if (this[kUwsRead].isDone) return resolve(this.#bodyBuf());
const onComplete = (buffer) => {
cleanup();
resolve(buffer);
};
const onError = (err) => {
cleanup();
reject(err);
};
const onClose = () => {
cleanup();
reject(/* @__PURE__ */ new Error("Stream closed before completion"));
};
const cleanup = () => {
this.off("error", onError);
this.off("close", onClose);
this.off("complete", onComplete);
};
this.once("error", onError);
this.once("close", onClose);
this.once("complete", onComplete);
if (this.destroyed) {
cleanup();
return reject(/* @__PURE__ */ new Error("Stream has been destroyed"));
}
});
};
//#endregion
exports.UwsReadable = UwsReadable;