client side impl begin

This commit is contained in:
Spencer Pogorzelski 2023-08-13 18:46:07 -07:00
parent f97e3b247f
commit 07b0ba3a07
2 changed files with 95 additions and 22 deletions

View file

@ -79,7 +79,7 @@ export class AdriftBareClient extends Client {
const ws = new webSocketImpl("ws:null", protocols); const ws = new webSocketImpl("ws:null", protocols);
// this will error. that's okay // this will error. that's okay
let send = this.connection.wsconnect( let { send, close } = this.connection.wsconnect(
remote, remote,
() => { () => {
onReadyState(WebSocketFields.OPEN); onReadyState(WebSocketFields.OPEN);
@ -88,8 +88,6 @@ export class AdriftBareClient extends Client {
() => { () => {
onReadyState(WebSocketFields.CLOSED); onReadyState(WebSocketFields.CLOSED);
ws.dispatchEvent(new Event("close")); ws.dispatchEvent(new Event("close"));
// what do i do for WebSocketFields.closing?
}, },
(data) => { (data) => {
ws.dispatchEvent( ws.dispatchEvent(
@ -100,11 +98,16 @@ export class AdriftBareClient extends Client {
} }
); );
(ws as any).__defineGetter__("send", () => (data: any) => { (ws as any).__defineGetter__("send", () => (data: any) => {
send(data); send(data);
}); });
(ws as any).__defineSetter__("send", () => { }); // uv wraps it and we don't want that
// i can probably fix later but this is fine for now -CE
(ws as any).__defineSetter__("send", () => {});
(ws as any).__defineGetter__("close", (code?: number, reason?: string) => {
close(code, reason);
});
return ws; return ws;
} }

View file

@ -9,8 +9,13 @@ import {
} from "protocol"; } from "protocol";
export class Connection { export class Connection {
callbacks: Record<number, Function> = {}; requestCallbacks: Record<number, Function> = {};
openStreams: Record<number, ReadableStreamDefaultController<any>> = {}; openRequestStreams: Record<number, ReadableStreamDefaultController<any>> = {};
openingSockets: Record<number, () => void>;
openSockets: Record<
number,
{ onclose: () => void; onmessage: (data: any) => void }
>;
counter: number = 0; counter: number = 0;
@ -18,6 +23,10 @@ export class Connection {
transport.ondata = this.ondata.bind(this); transport.ondata = this.ondata.bind(this);
} }
nextSeq() {
return ++this.counter;
}
ondata(data: ArrayBuffer) { ondata(data: ArrayBuffer) {
let cursor = 0; let cursor = 0;
const view = new DataView(data); const view = new DataView(data);
@ -35,7 +44,7 @@ export class Connection {
const payload = JSON.parse(decoder.decode(data.slice(cursor))); const payload = JSON.parse(decoder.decode(data.slice(cursor)));
const stream = new ReadableStream({ const stream = new ReadableStream({
start: (controller) => { start: (controller) => {
this.openStreams[requestID] = controller; this.openRequestStreams[requestID] = controller;
}, },
pull: (controller) => { pull: (controller) => {
// not needed // not needed
@ -44,17 +53,19 @@ export class Connection {
// TODO // TODO
}, },
}); });
this.callbacks[requestID]({ payload, body: stream }); this.requestCallbacks[requestID]({ payload, body: stream });
delete this.requestCallbacks[requestID];
break; break;
case S2CRequestTypes.HTTPResponseChunk: case S2CRequestTypes.HTTPResponseChunk:
this.openStreams[requestID]?.enqueue( this.openRequestStreams[requestID]?.enqueue(
new Uint8Array(data.slice(cursor)) new Uint8Array(data.slice(cursor))
); );
break; break;
case S2CRequestTypes.HTTPResponseEnd: case S2CRequestTypes.HTTPResponseEnd:
this.openStreams[requestID]?.close(); this.openRequestStreams[requestID]?.close();
delete this.openRequestStreams[requestID];
break; break;
} }
} }
@ -86,23 +97,82 @@ export class Connection {
let json = JSON.stringify(data); let json = JSON.stringify(data);
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
let id = ++this.counter; let seq = this.nextSeq();
this.callbacks[id] = resolve; this.requestCallbacks[seq] = resolve;
await this.send(id, new Blob([json]), C2SRequestTypes.HTTPRequest); await this.send(seq, new Blob([json]), C2SRequestTypes.HTTPRequest);
}); });
} }
// idk the type of data, figure it out ig
wsconnect(url: URL, onopen: () => void, onclose: () => void, onmessage: (data: any) => void): (data: any) => void {
// do the connection shit here wsconnect(
url: URL,
onopen: () => void,
onclose: () => void,
onmessage: (data: any) => void
): {
send: (data: any) => void;
close: (code?: number, reason?: string) => void;
} {
const payload = JSON.stringify({ url });
let seq = this.nextSeq();
this.send(
seq,
new TextEncoder().encode(payload),
C2SRequestTypes.WSOpen
).catch((e) => {
console.error(e);
onclose();
});
onopen();
// this can't be async, just call onopen when opened // this can't be async, just call onopen when opened
this.openingSockets[seq] = onopen;
return (data) => { return {
send: (data) => {
// send "data" to the server if (!this.openSockets[seq]) {
throw new Error("send on closed socket");
}
const cleanup = (e: any) => {
console.error(e);
delete this.openSockets[seq];
};
if (typeof data === "string") {
this.send(
seq,
new TextEncoder().encode(data),
C2SRequestTypes.WSSendText
).catch(cleanup);
return;
}
if (data instanceof ArrayBuffer) {
this.send(seq, data, C2SRequestTypes.WSSendBinary).catch(cleanup);
return;
}
if (ArrayBuffer.isView(data)) {
this.send(
seq,
data.buffer.slice(
data.byteOffset,
data.byteOffset + data.byteLength
),
C2SRequestTypes.WSSendBinary
).catch(cleanup);
return;
}
console.error({ data });
throw new Error("Unexpected type passed to send");
},
close: (code?: number, reason?: string) => {
const payload = JSON.stringify({ code, reason });
this.send(
seq,
new TextEncoder().encode(payload),
C2SRequestTypes.WSClose
).catch((e) => {
// At this point there is nothing left to clean up
console.error(e);
});
delete this.openSockets[seq];
},
}; };
} }
} }