UNPKG

fastify-uws

Version:

A performant HTTP and WebSocket server for Fastify with uWebSockets.

146 lines (145 loc) 4.91 kB
import { EventEmitter as k } from "eventemitter3"; import { ERR_STREAM_DESTROYED as w } from "./errors.js"; import { kServer as a, kRes as h, kWriteOnly as g, kReadyState as f, kEncoding as m, kRemoteAdress as b, kUwsRemoteAddress as u, kHead as n, kClientError as p, kTimeoutRef as c, kHttps as B, kAddress as I, kWs as W } from "./symbols.js"; const U = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), o = (r, e, t) => r.subarray(e, t).toString("hex"), y = () => { }; function _() { this.aborted = !0, this.emit("aborted"), this.errored && this.emit("error", this.errored), this.emit("close"); } function x(r) { return this.emit("drain", r), !0; } function C() { this.destroyed || (this.emit("timeout"), this.abort()); } function N(r, e) { r.writableNeedDrain = !0; let t = !1; const s = () => { r.removeListener("drain", i), !t && (t = !0, e()); }, i = () => { t || (r.writableNeedDrain = !1, r.removeListener("close", s), r.removeListener("drain", i), e()); }; r.on("drain", i), r.once("close", s); } function R(r, e) { if (e.status && r.writeStatus(e.status), e.headers) for (const t of e.headers.values()) if (t.isMultiValue) for (const s of t.value) r.writeHeader(t.name, s); else r.writeHeader(t.name, t.value); } function T(r) { return r != null && r.empty ? 0 : (r == null ? void 0 : r.byteLength) !== void 0 ? r.byteLength : Buffer.byteLength(r); } function $(r) { return r != null && r.chunk ? r.chunk : r; } class M extends k { constructor(e, t, s) { super(), this.aborted = !1, this.writableNeedDrain = !1, this.bytesRead = 0, this.bytesWritten = 0, this.writableEnded = !1, this.errored = null, this[a] = e, this[h] = t, this[g] = s, this[f] = { read: !1, write: !1 }, this[m] = null, this[b] = null, this[u] = null, this[n] = null, this[p] = !1, this.once("error", y), t.onAborted(_.bind(this)), t.onWritable(x.bind(this)), e.timeout && (this[c] = setTimeout(C.bind(this), e.timeout)); } get readyState() { const e = this[f]; return e.read && !e.write ? "readOnly" : !e.read && e.write ? "writeOnly" : e.read ? "open" : "opening"; } get writable() { return !0; } get readable() { return !0; } get encrypted() { return !!this[a][B]; } get remoteAddress() { let e = this[b]; if (e) return e; let t = this[u]; return t || (t = this[u] = Buffer.from(this[h].getRemoteAddress())), t.length === 4 ? e = `${t.readUInt8(0)}.${t.readUInt8(1)}.${t.readUInt8( 2 )}.${t.readUInt8(3)}` : t.equals(U) ? e = "::1" : e = `${o(t, 0, 2)}:${o(t, 2, 4)}:${o( t, 4, 6 )}:${o(t, 6, 8)}:${o(t, 8, 10)}:${o( t, 10, 12 )}:${o(t, 12, 14)}:${o(t, 14)}`, this[b] = e, e; } get remoteFamily() { return this[u] || (this[u] = Buffer.from(this[h].getRemoteAddress())), this[u].length === 4 ? "IPv4" : "IPv6"; } get destroyed() { return this.writableEnded || this.aborted; } address() { return { ...this[a][I] }; } abort() { this.aborted || (this.aborted = !0, !this[W] && !this.writableEnded && this[h].close()); } setEncoding(e) { this[m] = e; } destroy(e) { this.aborted || (this._clearTimeout(), this.errored = e, this.abort()); } onRead(e) { if (this[g] || this.aborted) return e(null, null); let t = !1; this[f].read = !0; const s = this[m]; try { this[h].onData((i, l) => { t || (this.bytesRead += i.byteLength, s ? i = Buffer.from(i).toString(s) : i = Buffer.copyBytesFrom(new Uint8Array(i)), this.emit("data", i), e(null, i), l && (t = !0, e(null, null))); }); } catch (i) { t = !0, this.destroy(i), e(i); } } end(e, t, s = y) { if (this.aborted) throw new w(); if (!e) return this.abort(); this.writableEnded = !0, this._clearTimeout(); const i = this[h]; i.cork(() => { this[n] && (R(i, this[n]), this[n] = null), i.end($(e)), this.bytesWritten += T(e), this.emit("close"), this.emit("finish"), s(); }); } write(e, t, s = y) { if (this.destroyed) throw new w(); if (this[p] && e.startsWith("HTTP/")) { const [l, v] = e.split(`\r \r `), [E, ...A] = l.split(`\r `), [, D, L] = E.split(" "); return this[n] = { headers: A.map((d) => { const [S, ...H] = d.split(": "); return { name: S, value: H.join(": ").trim() }; }).filter((d) => d.name.toLowerCase() !== "content-length"), status: `${D} ${L}` }, e = v, this.end(e, t, s); } const i = this[h]; return this[f].write = !0, i.cork(() => { this[n] && (R(i, this[n]), this[n] = null); const l = i.write($(e)); if (this.bytesWritten += T(e), l) return s(); N(this, s); }), !this.writableNeedDrain; } _clearTimeout() { this[c] && clearTimeout(this[c]); } } export { M as HTTPSocket };