-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
one session for multiple tabs? #243
Comments
SERVER: import fs from "fs";
import path from "path";
import mime from "mime";
import http from "http";
import https from "https";
import { Http3Server } from "@fails-components/webtransport";
import { generateWebTransportCertificate } from './mkcert';
import { readFile } from "node:fs/promises";
// node events
import { EventEmitter } from "node:events";
type StreamType = "datagram" | "bidirectional" | "unidirectional";
const isProduction = process.env.NODE_ENV === "production";
const PORT = 4433;
const HOST = (isProduction)
? "demo.web-transport.io"
: "gpt.pavels.lv";
console.log(HOST);
/**
* Proxy to serve local development server (:5173) on HTTPS (:4433)
*/
const proxy = http.createServer((clientReq, clientRes) => {
const options = {
hostname: 'gpt.pavels.lv',
port: 5173,
path: clientReq.url,
method: clientReq.method,
headers: clientReq.headers
};
const proxyReq = http.request(options, (proxyRes) => {
clientRes.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(clientRes, {
end: true
});
});
clientReq.pipe(proxyReq, {
end: true
});
proxyReq.on('error', (err) => {
console.error('Proxy request error:', err);
clientRes.end();
});
});
let stack = [];
async function readData(readable, writable) {
const reader = readable.getReader();
const writer = writable.getWriter();
console.log("readData", reader)
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
// value is a Uint8Array.
console.log(value);
writer.write(value);
}
}
async function writeData(writable) {
const writer = writable.getWriter();
const data1 = new Uint8Array([65, 66, 67]);
const data2 = new Uint8Array([68, 69, 70]);
writer.write(data1);
writer.write(data2);
}
async function receiveBidirectional(stream) {
const reader = stream.getReader();
while (true) {
const { done, value: bidi } = await reader.read();
if (done) {
break;
}
// value is an instance of WebTransportBidirectionalStream
console.log('calling readData');
readData(bidi.readable,bidi.writable);
// await writeData(bidi.writable);
}
}
async function main() {
const certificate = {
/**
* Replace with your own certificate.
*/
private: fs.readFileSync("/etc/letsencrypt/live/gpt.pavels.lv/privkey.pem"),
cert: fs.readFileSync("/etc/letsencrypt/live/gpt.pavels.lv/fullchain.pem"),
fingerprint: "" // not used in production
}
/**
* Create a HTTPS server to serve static files
*/
https.createServer({
cert: certificate?.cert,
key: certificate?.private,
}, async function (req, res) {
const filename = req.url?.substring(1) || "index.html"; // fallback to "index.html"
if (filename === "fingerprint") {
const fingerprint = certificate?.fingerprint!.split(":").map((hex) => parseInt(hex, 16));
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify(fingerprint));
return;
}
if (process.env.NODE_ENV !== "production") {
/**
* DEVELOPMENT:
* Use proxy to serve local development server
*/
proxy.emit('request', req, res);
} else {
/**
* PRODUCTION:
* Serve static files from "client/dist"
*/
const filepath = path.join(__dirname, "..", "client", "dist", filename);
if (fs.existsSync(filepath)) {
res.writeHead(200, {
"content-type": (mime.getType(filename) || "text/plain"),
"Alt-Svc": `h3=":${PORT}"`
});
res.end((await readFile(filepath)));
} else {
res.writeHead(404);
res.end('Not found');
}
}
}).listen(PORT);
// https://github.com/fails-components/webtransport/blob/master/test/testsuite.js
const h3Server = new Http3Server({
host: HOST,
port: PORT,
secret: "mysecret",
cert: certificate?.cert,
privKey: certificate?.private,
});
h3Server.startServer();
// h3Server.updateCert(certificate?.cert, certificate?.private);
let isKilled: boolean = false;
function handle(e: any) {
console.log("SIGNAL RECEIVED:", e);
isKilled = true;
h3Server.stopServer();
}
process.on("SIGINT", handle);
process.on("SIGTERM", handle);
try {
const sessionStream = await h3Server.sessionStream("/");
const sessionReader = sessionStream.getReader();
sessionReader.closed.catch((e: any) => console.log("session reader closed with error!", e));
while (!isKilled) {
console.log("sessionReader.read() - waiting for session...");
const { done, value: webtransportSession } = await sessionReader.read();
if (done) {
console.log("done! break loop.");
break;
}
webtransportSession.closed.then(() => {
console.log("Session closed successfully!");
}).catch((e: any) => {
console.log("Session closed with error! " + e);
});
webtransportSession.ready.then(async () => {
console.log("session ready!");
// WebTransportReceiveStream
await receiveBidirectional(webtransportSession.incomingBidirectionalStreams);
}).catch((e: any) => {
console.log("session failed to be ready!", e);
});
}
} catch (e) {
console.error("error:", e);
}
}
main(); Client: async function readData(reader, type) {
console.log("Reader created", reader, type);
while (true) {
console.log("Reading data", reader);
const { value, done } = await reader.read();
if (done) {
console.log("Stream is done");
break;
}
// value is a Uint8Array.
console.log('readData', value);
}
}
async function receiveBidirectional(stream) {
const reader = stream.getReader();
console.log('receiveBidirectional')
while (true) {
const { done, value: bidi } = await reader.read();
if (done) {
break;
}
console.log("receiveBidirectional readData"), bidi;
// value is an instance of WebTransportBidirectionalStream
await readData(bidi.readable.getReader());
// await writeData(bidi.writable);
}
}
const ENDPOINT = `https://gpt.pavels.lv:4433`;
let transport = new WebTransport(ENDPOINT);
var bidirectionalDataWriterRef = {}
var bidirectionalDataReaderRef = {}
transport.ready.then(async () => {
console.log("Transport ready");
const bidi = await transport.createBidirectionalStream();
console.log("Bidirectional stream create start");
const reader = bidi.readable.getReader();
bidirectionalDataReaderRef = reader;
//reader.closed.catch(e => console.log("bidi readable closed", e.toString()));
readData(reader, 'readable.reader');
receiveBidirectional(transport.incomingBidirectionalStreams);
const writer = bidi.writable.getWriter();
bidirectionalDataWriterRef = writer;
writer.closed.catch(e => console.log("bidi writable closed", e.toString()));
});
To repeat open two tabs, and in first tab in chrome developer tools write: server will receive it and immediately write it (inside readData), and client#1 will receive response back. However client#2 never receives any, it seems I need to somehow propogate it or combine sessions,.. |
Of course, the session is different for every tab. Anything else would be a security problem, and such a mechanism is handled by the browser. |
If you want to share a single WebTransport session between multiple browsing contexts, you should run it in a SharedWorker. Note that browsers will enforce the same-origin policy, similar to how you'd access other web resources. |
My fault, I absolutely forgot that nodejs works as a single application and doesn't not die, so each connection can be handled with events and stored in variables that later can be forwared further, so when ever client X sends message, we forward and client Y also sees it.. |
Trying to send WRITE for BidirectionalStream it works during the same browser tab, however I'm trying to achieve that second tab READER receives the WRITE from first tab as well, not only server.
It seems that session is different for every browser tab and thus it cannot "write" for both of the sessions, is there any idea how to achieve this logic for multiple tabs?
The text was updated successfully, but these errors were encountered: