mirror of
https://github.com/MercuryWorkshop/adrift.git
synced 2025-05-12 22:00:02 -04:00
chunking
This commit is contained in:
parent
db82f47aca
commit
753bccf51b
6 changed files with 2264 additions and 179 deletions
|
@ -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;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,6 +146,8 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(window as any).bare = new BareClient();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if ready}
|
{#if ready}
|
||||||
|
|
2338
pnpm-lock.yaml
generated
2338
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -4,3 +4,4 @@ packages:
|
||||||
- frontend
|
- frontend
|
||||||
- client
|
- client
|
||||||
- firebase-config
|
- firebase-config
|
||||||
|
- corium
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue