Proposal: Refactor DeclarativeResponse
- Allow to write blobs
- Check text/blob size
PS. I like this ArrayBuffer based API 😉
PPS. In theory we can send blobs of byte length greater than > MAX U16 by calling write/5 multiple times.
It blows up in size.
@uNetworkingAB Should I close this one? (I use different implementation anyway.)
It still needs binary support but I did not like the size blowing up like that. What kind of implementation do you use?
Something like (playgound):
const TE = new TextEncoder();
const EU8A = new Uint8Array(0);
function toU8(data: string | Uint8Array | undefined): Uint8Array {
if (!data) return EU8A;
if (typeof data === 'string') return TE.encode(data);
return data;
}
const enum OPC {
END = 0,
WRITE_HEADER = 1,
WRITE_BODY_VALUE = 2,
WRITE_QUERY_VALUE = 3,
WRITE_HEADER_VALUE = 4,
WRITE = 5,
WRITE_PARAMETER_VALUE = 6,
}
const MAX_U8 = Math.pow(2, 8) - 1;
const MAX_U16 = Math.pow(2, 16) - 1;
const OPCODES = [
Uint8Array.from([OPC.END]),
Uint8Array.from([OPC.WRITE_HEADER]),
Uint8Array.from([OPC.WRITE_BODY_VALUE]),
Uint8Array.from([OPC.WRITE_QUERY_VALUE]),
Uint8Array.from([OPC.WRITE_HEADER_VALUE]),
Uint8Array.from([OPC.WRITE]),
Uint8Array.from([OPC.WRITE_PARAMETER_VALUE]),
] as const;
export class DeclarativeResponse {
#instructions: Array<Uint8Array> = [];
#instructionsLength: number = 0;
#appendOpCode(opcode: OPC) {
this.#instructions.push(OPCODES[opcode]);
this.#instructionsLength += 1;
}
#appendInstruction(text: string) {
const bytes = TE.encode(text);
const length = bytes.byteLength;
if (length > MAX_U8) throw new RangeError(`Data byte length ${length} greater than ${MAX_U8}`);
this.#instructions.push(Uint8Array.from([length]));
this.#instructions.push(bytes);
this.#instructionsLength += 1 + length;
}
#appendBytes(bytes: Uint8Array) {
const length = bytes.byteLength;
this.#instructions.push(Uint8Array.from([length & 0xff, (length >> 8) & 0xff]));
if (length) this.#instructions.push(bytes);
this.#instructionsLength += 2 + length;
}
writeHeader(key: string, value: string) {
this.#appendOpCode(OPC.WRITE_HEADER);
this.#appendInstruction(key);
this.#appendInstruction(value);
return this;
}
writeBody() {
this.#appendOpCode(OPC.WRITE_BODY_VALUE);
return this;
}
writeQueryValue(key: string) {
this.#appendOpCode(OPC.WRITE_QUERY_VALUE);
this.#appendInstruction(key);
return this;
}
writeHeaderValue(key: string) {
this.#appendOpCode(OPC.WRITE_HEADER_VALUE);
this.#appendInstruction(key);
return this;
}
write(data: string | Uint8Array) {
const bytes = toU8(data);
if (!bytes.byteLength) {
return this;
}
if (bytes.byteLength > MAX_U16) {
let offset = 0;
while (offset < bytes.byteLength) {
this.#appendOpCode(OPC.WRITE);
this.#appendBytes(bytes.subarray(offset, offset + MAX_U16));
offset += MAX_U16;
}
} else {
this.#appendOpCode(OPC.WRITE);
this.#appendBytes(bytes);
}
return this;
}
writeParameterValue(key: string) {
this.#appendOpCode(OPC.WRITE_PARAMETER_VALUE);
this.#appendInstruction(key);
return this;
}
end(data?: string | Uint8Array): ArrayBufferLike {
const bytes = toU8(data);
if (bytes.byteLength > MAX_U16) {
let offset = 0;
while (offset < bytes.byteLength) {
this.#appendOpCode(offset + MAX_U16 < bytes.byteLength ? OPC.WRITE : OPC.END);
this.#appendBytes(bytes.subarray(offset, offset + MAX_U16));
offset += MAX_U16;
}
} else {
this.#appendOpCode(OPC.END);
this.#appendBytes(bytes);
}
const output = new Uint8Array(this.#instructionsLength);
let offset = 0;
for (const chunk of this.#instructions) {
output.set(chunk, offset);
offset += chunk.byteLength;
}
return output.buffer;
}
}
I added this https://github.com/uNetworking/uWebSockets.js/commit/4c29320e4ed7ed042cff9d5111ec7b1555c274aa
Ah never mind that's not it
Maybe best to just take your variant
You might be interested in whether it is possible to dynamically increase the buffer size. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer/resize
Clean-up dashboard.