initial onerror impl

This commit is contained in:
Spencer Pogorzelski 2023-08-14 14:13:39 -07:00
parent 8d8e5bbf2e
commit feaac8d6c3
4 changed files with 45 additions and 17 deletions

View file

@ -93,11 +93,13 @@ export class AdriftBareClient extends Client {
data, data,
}) })
); );
},
(message: string) => {
ws.dispatchEvent(new ErrorEvent("error", { message }));
} }
); );
ws.send = (data: any) => { ws.send = (data: any) => {
console.log("Reached AdriftClient.ts send");
send(data); send(data);
}; };

View file

@ -8,11 +8,13 @@ import {
S2CRequestTypes, S2CRequestTypes,
Transport, Transport,
WSClosePayload, WSClosePayload,
WSErrorPayload,
} from "protocol"; } from "protocol";
type OpenWSMeta = { type OpenWSMeta = {
onclose: (code: number, reason: string, wasClean: boolean) => void; onclose: (code: number, reason: string, wasClean: boolean) => void;
onmessage: (data: any) => void; onmessage: (data: any) => void;
onerror: (message: string) => void;
}; };
export class Connection { export class Connection {
@ -42,10 +44,12 @@ export class Connection {
console.log(requestID, requestType); console.log(requestID, requestType);
const msgText = () => new TextDecoder().decode(data.slice(cursor));
const msgJSON = () => JSON.parse(msgText());
switch (requestType) { switch (requestType) {
case S2CRequestTypes.HTTPResponseStart: case S2CRequestTypes.HTTPResponseStart:
const decoder = new TextDecoder(); const payload = msgJSON();
const payload = JSON.parse(decoder.decode(data.slice(cursor)));
const stream = new ReadableStream({ const stream = new ReadableStream({
start: (controller) => { start: (controller) => {
this.openRequestStreams[requestID] = controller; this.openRequestStreams[requestID] = controller;
@ -82,32 +86,41 @@ export class Connection {
case S2CRequestTypes.WSDataText: { case S2CRequestTypes.WSDataText: {
const socketMeta = this.openSockets[requestID]; const socketMeta = this.openSockets[requestID];
if (!socketMeta) return; if (!socketMeta) return;
socketMeta.onmessage(new TextDecoder().decode(data.slice(cursor))); setTimeout(() => socketMeta.onmessage(msgText()));
break; break;
} }
case S2CRequestTypes.WSDataBinary: { case S2CRequestTypes.WSDataBinary: {
const socketMeta = this.openSockets[requestID]; const socketMeta = this.openSockets[requestID];
if (!socketMeta) return; if (!socketMeta) return;
socketMeta.onmessage(data.slice(cursor)); setTimeout(() => socketMeta.onmessage(data.slice(cursor)));
break; break;
} }
case S2CRequestTypes.WSClose: { case S2CRequestTypes.WSClose: {
const socketMeta = this.openSockets[requestID]; const socketMeta = this.openSockets[requestID];
if (!socketMeta) return; if (!socketMeta) return;
const payload: WSClosePayload = JSON.parse( const payload: WSClosePayload = msgJSON();
new TextDecoder().decode(data.slice(cursor)) setTimeout(() =>
); socketMeta.onclose(
socketMeta.onclose( payload.code || 1005,
payload.code || 1005, payload.reason || "",
payload.reason || "", "wasClean" in payload ? Boolean(payload.wasClean) : false
"wasClean" in payload ? Boolean(payload.wasClean) : false )
); );
delete this.openSockets[requestID]; delete this.openSockets[requestID];
break; break;
} }
case S2CRequestTypes.WSError: {
const socketMeta = this.openSockets[requestID];
if (!socketMeta) return;
const payload: WSErrorPayload = msgJSON();
setTimeout(() => socketMeta.onerror(payload.message));
setTimeout(() => socketMeta.onclose(1006, "", false));
break;
}
default: default:
break; break;
} }
@ -150,7 +163,8 @@ export class Connection {
url: URL, url: URL,
onopen: () => void, onopen: () => void,
onclose: (code: number, reason: string, wasClean: boolean) => void, onclose: (code: number, reason: string, wasClean: boolean) => void,
onmessage: (data: any) => void onmessage: (data: any) => void,
onerror: (message: string) => void
): { ): {
send: (data: any) => void; send: (data: any) => void;
close: (code?: number, reason?: string) => void; close: (code?: number, reason?: string) => void;
@ -171,7 +185,10 @@ export class Connection {
}); });
// this can't be async, just call onopen when opened // this can't be async, just call onopen when opened
this.openingSockets[seq] = { onopen, rest: { onmessage, onclose } }; this.openingSockets[seq] = {
onopen,
rest: { onmessage, onclose, onerror },
};
return { return {
send: (data) => { send: (data) => {

View file

@ -16,6 +16,7 @@ export const S2CRequestTypes = {
WSDataText: 4, WSDataText: 4,
WSDataBinary: 5, WSDataBinary: 5,
WSClose: 6, WSClose: 6,
WSError: 7,
} as const; } as const;
export type S2CRequestType = ObjectValues<typeof S2CRequestTypes>; export type S2CRequestType = ObjectValues<typeof S2CRequestTypes>;
@ -44,4 +45,8 @@ export type WSClosePayload = {
wasClean: boolean; wasClean: boolean;
}; };
export type WSErrorPayload = {
message: string;
};
export { Transport } from "./Transport"; export { Transport } from "./Transport";

View file

@ -9,6 +9,7 @@ import {
S2CRequestType, S2CRequestType,
S2CRequestTypes, S2CRequestTypes,
WSClosePayload, WSClosePayload,
WSErrorPayload,
} from "protocol"; } from "protocol";
import { Readable } from "stream"; import { Readable } from "stream";
import { BareError, bareFetch, options } from "./http"; import { BareError, bareFetch, options } from "./http";
@ -164,6 +165,10 @@ export class AdriftServer {
this._sendJSONRes(seq, S2CRequestTypes.WSClose, payload); this._sendJSONRes(seq, S2CRequestTypes.WSClose, payload);
} }
sendWSError(seq: number, payload: WSErrorPayload) {
this._sendJSONRes(seq, S2CRequestTypes.WSError, payload);
}
sendWSText(seq: number, textEncoded: ArrayBuffer) { sendWSText(seq: number, textEncoded: ArrayBuffer) {
const buf = new ArrayBuffer(2 + 1 + textEncoded.byteLength); const buf = new ArrayBuffer(2 + 1 + textEncoded.byteLength);
const dataView = new DataView(buf); const dataView = new DataView(buf);
@ -240,10 +245,9 @@ export class AdriftServer {
const payload = AdriftServer.tryParseJSONPayload(msg.slice(cursor)); const payload = AdriftServer.tryParseJSONPayload(msg.slice(cursor));
const ws = (this.sockets[seq] = new WebSocket(payload.url)); const ws = (this.sockets[seq] = new WebSocket(payload.url));
ws.binaryType = "arraybuffer"; ws.binaryType = "arraybuffer";
// TODO v important: onerror
ws.onerror = (e) => { ws.onerror = (e) => {
console.log("ws onerror", e); // WSError implies close with code 1006, reason "" and wasClean false
this.sendWSClose(seq, { code: 1006, reason: "", wasClean: false }); this.sendWSError(seq, { message: e.message });
}; };
ws.onopen = () => { ws.onopen = () => {
this.sendWSOpen(seq); this.sendWSOpen(seq);