30 lines
1.3 KiB
JavaScript
30 lines
1.3 KiB
JavaScript
|
|
// The byte pipe to a smolmaild server. Browsers cannot open TCP sockets, so
|
||
|
|
// bytes travel through the local bridge's WebSocket relay unchanged; the
|
||
|
|
// Noise session lives in this page either way.
|
||
|
|
|
||
|
|
const BRIDGE = `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}`;
|
||
|
|
|
||
|
|
export function connectStream(host, port, timeoutMs = 0) {
|
||
|
|
return new Promise((resolve, reject) => {
|
||
|
|
const socket = new WebSocket(`${BRIDGE}/tcp/${host}/${port}`);
|
||
|
|
socket.binaryType = "arraybuffer";
|
||
|
|
const stream = {
|
||
|
|
send: bytes => socket.send(bytes),
|
||
|
|
close: () => socket.close(),
|
||
|
|
onData: null,
|
||
|
|
onClose: null,
|
||
|
|
};
|
||
|
|
// Settings' timeout only governs this; the WebSocket may otherwise never
|
||
|
|
// fire open, error, or close if something between here and the bridge
|
||
|
|
// just drops packets.
|
||
|
|
const timer = timeoutMs ? setTimeout(() => {
|
||
|
|
socket.close();
|
||
|
|
reject(new Error(`could not reach ${host}:${port} within ${timeoutMs / 1000}s`));
|
||
|
|
}, timeoutMs) : null;
|
||
|
|
socket.onopen = () => { clearTimeout(timer); resolve(stream); };
|
||
|
|
socket.onerror = () => { clearTimeout(timer); reject(new Error(`cannot reach the bridge at ${BRIDGE}`)); };
|
||
|
|
socket.onmessage = event => stream.onData?.(new Uint8Array(event.data));
|
||
|
|
socket.onclose = () => stream.onClose?.();
|
||
|
|
});
|
||
|
|
}
|