This commit is contained in:
Spencer Pogorzelski 2023-08-12 17:04:02 -07:00
parent db82f47aca
commit 753bccf51b
6 changed files with 2264 additions and 179 deletions

View file

@ -10,6 +10,7 @@ import {
export class Connection { export class Connection {
callbacks: Record<number, Function> = {}; callbacks: Record<number, Function> = {};
openStreams: Record<number, ReadableStreamDefaultController<any>> = {};
counter: number = 0; counter: number = 0;
@ -29,20 +30,32 @@ export class Connection {
console.log(requestID, requestType); console.log(requestID, requestType);
switch (requestType) { switch (requestType) {
case S2CRequestTypes.HTTPResponse: { case S2CRequestTypes.HTTPResponseStart:
const payloadLen = view.getUint32(cursor);
cursor += 4;
const decoder = new TextDecoder(); const decoder = new TextDecoder();
const payloadRaw = decoder.decode( const payload = JSON.parse(decoder.decode(data.slice(cursor)));
data.slice(cursor, cursor + payloadLen) const stream = new ReadableStream({
); start: (controller) => {
console.log({ payloadLen, payloadRaw }); this.openStreams[requestID] = controller;
const payload = JSON.parse(payloadRaw); },
cursor += payloadLen; pull: (controller) => {
// not needed
this.callbacks[requestID]({ payload, body: data.slice(cursor) }); },
cancel: () => {
// TODO
},
});
this.callbacks[requestID]({ payload, body: stream });
break;
case S2CRequestTypes.HTTPResponseChunk:
this.openStreams[requestID]?.enqueue(
new Uint8Array(data.slice(cursor))
);
break;
case S2CRequestTypes.HTTPResponseEnd:
this.openStreams[requestID]?.close();
break; break;
}
} }
} }

View file

@ -146,6 +146,8 @@
); );
} }
} }
(window as any).bare = new BareClient();
</script> </script>
{#if ready} {#if ready}

2338
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -4,3 +4,4 @@ packages:
- frontend - frontend
- client - client
- firebase-config - firebase-config
- corium

View file

@ -9,10 +9,13 @@ export const C2SRequestTypes = {
export type C2SRequestType = ObjectValues<typeof C2SRequestTypes>; export type C2SRequestType = ObjectValues<typeof C2SRequestTypes>;
export const S2CRequestTypes = { export const S2CRequestTypes = {
HTTPResponse: 0, HTTPResponseStart: 0,
WSOpen: 1, HTTPResponseChunk: 1,
WSDataText: 2, HTTPResponseEnd: 2,
WSDataBinary: 3, WSOpen: 3,
WSDataText: 4,
WSDataBinary: 5,
WSClose: 6,
} as const; } as const;
export type S2CRequestType = ObjectValues<typeof S2CRequestTypes>; export type S2CRequestType = ObjectValues<typeof S2CRequestTypes>;

View file

@ -9,6 +9,7 @@ import {
ProtoBareHeaders, ProtoBareHeaders,
S2CRequestTypes, S2CRequestTypes,
} from "protocol"; } from "protocol";
import { Readable } from "stream";
import { BareError, bareFetch, options } from "./http"; import { BareError, bareFetch, options } from "./http";
export class Client { export class Client {
@ -57,7 +58,7 @@ export class Client {
static bareErrorToResponse(e: BareError): { static bareErrorToResponse(e: BareError): {
payload: HTTPResponsePayload; payload: HTTPResponsePayload;
body: Buffer; body: AsyncIterable<Buffer>;
} { } {
return { return {
payload: { payload: {
@ -65,13 +66,13 @@ export class Client {
statusText: STATUS_CODES[e.status] || "", statusText: STATUS_CODES[e.status] || "",
headers: {}, headers: {},
}, },
body: Buffer.from(JSON.stringify(e.body)), body: Readable.from(JSON.stringify(e.body)),
}; };
} }
async handleHTTPRequest(payload: HTTPRequestPayload): Promise<{ async handleHTTPRequest(payload: HTTPRequestPayload): Promise<{
payload: HTTPResponsePayload; payload: HTTPResponsePayload;
body: Buffer; body: AsyncIterable<Buffer>;
}> { }> {
const abort = new AbortController(); const abort = new AbortController();
const onClose = () => { const onClose = () => {
@ -97,13 +98,6 @@ export class Client {
} }
this.events.off("close", onClose); this.events.off("close", onClose);
const buffers: any[] = [];
// node.js readable streams implement the async iterator protocol
for await (const data of resp) {
buffers.push(data);
}
const body = Buffer.concat(buffers);
return { return {
payload: { payload: {
@ -113,19 +107,34 @@ export class Client {
Object.entries(resp.headersDistinct).filter(([_k, v]) => Boolean(v)) Object.entries(resp.headersDistinct).filter(([_k, v]) => Boolean(v))
) as ProtoBareHeaders, ) as ProtoBareHeaders,
}, },
body, body: resp,
}; };
} }
sendHTTPResponse(seq: number, payload: HTTPResponsePayload, body: Buffer) { sendHTTPResponseStart(seq: number, payload: HTTPResponsePayload) {
const payloadBuffer = Buffer.from(JSON.stringify(payload)); const payloadBuffer = Buffer.from(JSON.stringify(payload));
const buf = Buffer.alloc(2 + 1 + 4 + payloadBuffer.length + body.length); const buf = Buffer.alloc(2 + 1 + payloadBuffer.length);
let cursor = 0; let cursor = 0;
cursor = buf.writeUInt16BE(seq, cursor); cursor = buf.writeUInt16BE(seq, cursor);
cursor = buf.writeUInt8(S2CRequestTypes.HTTPResponse, cursor); cursor = buf.writeUInt8(S2CRequestTypes.HTTPResponseStart, cursor);
cursor = buf.writeUInt32BE(payloadBuffer.length, cursor); payloadBuffer.copy(buf, cursor);
cursor += payloadBuffer.copy(buf, cursor); this.send(buf);
body.copy(buf, cursor); }
sendHTTPResponseChunk(seq: number, chunk: Buffer) {
const buf = Buffer.alloc(2 + 1 + chunk.length);
let cursor = 0;
cursor = buf.writeUInt16BE(seq, cursor);
cursor = buf.writeUInt8(S2CRequestTypes.HTTPResponseChunk, cursor);
chunk.copy(buf, cursor);
this.send(buf);
}
sendHTTPResponseEnd(seq: number) {
const buf = Buffer.alloc(2 + 1);
let cursor = 0;
cursor = buf.writeUInt16BE(seq, cursor);
cursor = buf.writeUInt8(S2CRequestTypes.HTTPResponseEnd, cursor);
this.send(buf); this.send(buf);
} }
@ -135,7 +144,10 @@ export class Client {
const { cursor, seq, op } = init; const { cursor, seq, op } = init;
switch (op) { switch (op) {
case C2SRequestTypes.HTTPRequest: case C2SRequestTypes.HTTPRequest:
let resp; let resp: {
payload: HTTPResponsePayload;
body: AsyncIterable<Buffer>;
};
const reqPayload = Client.parseHttpReqPayload(msg.subarray(cursor)); const reqPayload = Client.parseHttpReqPayload(msg.subarray(cursor));
if (!reqPayload) return; if (!reqPayload) return;
try { try {
@ -166,7 +178,11 @@ export class Client {
} }
const { payload, body } = resp; const { payload, body } = resp;
this.sendHTTPResponse(seq, payload, body); this.sendHTTPResponseStart(seq, payload);
for await (const chunk of body) {
this.sendHTTPResponseChunk(seq, chunk);
}
this.sendHTTPResponseEnd(seq);
break; break;
default: default:
// not implemented // not implemented