From c75e6b90334191d46ac80b4301cf3663a32d5a9c Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Fri, 15 Sep 2023 16:49:09 +0200 Subject: [PATCH 01/72] Restructure code before merge --- .gitignore | 2 +- .vscode/settings.json | 3 +- Cargo.toml | 7 +- bindings.json | 0 cert.crt | 9 - cert.key | 5 - cert.pem | 9 - certificate.key | 28 --- certificate.pem | 20 -- deno.ts | 100 -------- examples/index.html | 103 +++++++++ fingerprint.hex | 1 - index.html | 160 ------------- key.pem | 5 - mod/crypto.ts | 28 +++ mod/deno.ts | 75 ++++++ mod/deps.ts | 1 + mod/lib.ts | 50 ++++ src/certificate.rs | 53 +++++ src/lib.rs | 253 ++++++++++----------- src/{old_runtime.rs => old_runtime.rs.old} | 0 src/{oldlib.rs => oldlib.rs.old} | 0 22 files changed, 441 insertions(+), 471 deletions(-) delete mode 100644 bindings.json delete mode 100644 cert.crt delete mode 100644 cert.key delete mode 100644 cert.pem delete mode 100644 certificate.key delete mode 100644 certificate.pem delete mode 100644 deno.ts create mode 100644 examples/index.html delete mode 100644 fingerprint.hex delete mode 100644 index.html delete mode 100644 key.pem create mode 100644 mod/crypto.ts create mode 100644 mod/deno.ts create mode 100644 mod/deps.ts create mode 100644 mod/lib.ts create mode 100644 src/certificate.rs rename src/{old_runtime.rs => old_runtime.rs.old} (100%) rename src/{oldlib.rs => oldlib.rs.old} (100%) diff --git a/.gitignore b/.gitignore index 7a51535..46913a2 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ # will have compiled files and executables debug/ target/ - +certs/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c7ddc5..010b04f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "deno.enable": true, "deno.lint": true, - "deno.unstable": true + "deno.unstable": true, + "deno.cacheOnSave": true } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 227b5a8..b21d41f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,9 @@ once_cell = "1.18.0" smol = "1.3.0" tokio = { version = "1.28.1", features = ["rt", "rt-multi-thread", "macros"] } wtransport = "0.1.4" -serde = { version = "1", features = ["derive"] } \ No newline at end of file +serde = { version = "1", features = ["derive"] } +base64 = "0.21.4" +rcgen = "0.11.1" +ring = "0.16.20" +time = "0.3.28" +anyhow = "1.0.75" diff --git a/bindings.json b/bindings.json deleted file mode 100644 index e69de29..0000000 diff --git a/cert.crt b/cert.crt deleted file mode 100644 index 96457eb..0000000 --- a/cert.crt +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBNjCB3KADAgECAggr3hcUukuKrDAKBggqhkjOPQQDAjAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMjMwOTEyMTgwMTA0WhcNMjMwOTE2MTgwMTA0WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASOlej65PFh -PThJi0R8GKRVqBNbITNkFWppDi8+ROQ/rJCNJjb4jytT960lxpRElIrxGkesv8aZ -yIIhSDKdeu77oxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwID -SQAwRgIhAL5P+Ks84VtQBjUubta6y1EPqnVczMeIgl1+oUF/vqkpAiEAm1YFhD9r -SezQf4HsVgnDNGcGzifzJu9vjxldFryzTVw= ------END CERTIFICATE----- diff --git a/cert.key b/cert.key deleted file mode 100644 index 69ee83d..0000000 --- a/cert.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgsn0KihQyihVxJImg -kTZbpYhkDgNbx67KTym2gXcqIoShRANCAASOlej65PFhPThJi0R8GKRVqBNbITNk -FWppDi8+ROQ/rJCNJjb4jytT960lxpRElIrxGkesv8aZyIIhSDKdeu77 ------END PRIVATE KEY----- diff --git a/cert.pem b/cert.pem deleted file mode 100644 index 84dbed5..0000000 --- a/cert.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBNTCB3KADAgECAggikfe7vbxE3DAKBggqhkjOPQQDAjAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMjMwOTEyMTMwMjU2WhcNMjMwOTE2MTMwMjU2WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASXNjGpiW3z -8G5HaaQ0Qn8uZUbckzpyAHqnnJtpPAFDxgrTAVTLOwmLQhRafzdUPLzuIMiFzzkn -ydayU/P8HNWmoxgwFjAUBgNVHREEDTALgglsb2NhbGhvc3QwCgYIKoZIzj0EAwID -SAAwRQIhAI4TlJ3uuoEsf3QlUgWIz3i8WZsVkHwm0GF1sbSfiwXdAiAdL3CiopNt -6+Z9/dbAk1Dxh4iRrUbmKiC/Q4JYTOhTpQ== ------END CERTIFICATE----- diff --git a/certificate.key b/certificate.key deleted file mode 100644 index 5d333c6..0000000 --- a/certificate.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCVyXZN1oL+6D1j -VuzjSyExD5P6SOrDwC3hviZmhjP9jeOwG9gb7bi+lza40SxvtDMNB0QzZQFPjMKT -Jca0idpLV3+v2hhficwbwfq2T5EialOfiQBOglXUsQBnuK2VbGOOoedO115BOLdo -Z0jd0HzWtgH1nkd8scX8YsGxqc+dGnv2nzkTSnr25hL6Go+kPiPH87HFDgUoQnO4 -bPAtHMljJVQv5+vuC7DMEbNC3VprQp4DCbXjc2OPFO1RIrlMrOQufDOGtKcPaQSu -yJBh4BXs0PL6sMEuRKlinEkeGYSDRe0jG084KBz82NpcHcl2GdChCGzeUCB5utxt -VIdf0kPvAgMBAAECggEAP1Z71KlHJkQNJVMOA4Ty6ZyFPdoUj6bVn/X4pfTMykR0 -CbWUeibZLpqhlKA60pdX2QQAFl15IurKIk/giNob+SzsPO+Ty95odTpe6jWWEP/2 -EWlsvEYHxlL8cV+Z4yv8o0UaGvAeFqVFhPvbx5QQHfjDtllyMCu6JSGbdyVvuaXh -8p5Tveu9rk0icz6c5XRQE/W+zIWcnB1hojGXcadalYLMfPnPAMQ0cojyhiFz5u0h -CgZgEKLLa2bOTerSSFX8CLu9VZXYQSmzPw08FN0r9wukBKJBaFz5mCAWhRDLUwLN -tzYK9jTrRp7BI/QJv+uBFh6/Wvy40WWkJuj+NOpivQKBgQDPOmmw/BiYNI0bq/2v -JSyKpuoovtBBEXnPRW3plYpCDUf//mt34/TfRUK15gJFNDuqObHSVPn8N/skfFHf -kXsdZYPNLMNgWfWkvswNNOzZbD3zLvTz51Mi/JQ9nBdvzhPLvlkVHieOarZm6+ai -qUihGJpSp+KLXpYpv5QVPt3FzQKBgQC5CjD5XZ+RmwKsnWBaPCFgOOUv5YDvzxlm -l8KcIA6IkLW44uvbCgb0kVATr1bp/1i5jPF/Tw8EX/ZUCXJOKp4BtwmSdCqr6/Lv -Ov8qwYXB2LguLjIglhfd0sRNsdHRyqiQpKdOgo4/6n9ibgdxCODcrJVZWaVnMYph -r8cwMge0qwKBgE7I0m3rKh6TvCINHYF9DJYaJ2QeR2a1ki3vI35u0AWUrw7wV0NZ -czt/RYGKVMqitRxemvBwRipRzjVs3mO1F61xbs6OeikjinR75XAP8wwmTtcpvw8L -n1vp0yIpOe/T1Urcr1mMAVXv1DEE9sZYvbghdmp+UW7TIxv0LgR1xjLxAoGABJrj -UyFoWjhQXblg7331mq5vzbxZdB5kVHBMcJQ/qFhpnVtQYVgjiiyfoI3JjPNE4wFF -9VQjXImC2N6PULCw0/wZKeLoOPUbS6bdONQuHv/kvYl6x+LUXznilshpH156yXa/ -jy2imqacWvfACakd54AIC3w2qJXMCthUDbgBBxcCgYAPJ57OJLtbzcK0wE6vY5n3 -+s8TG8yf6Oe6ikDFyGFe/Elfh2Jqpi9MFTUGOUFkNygEqUlLuYAcyRhUA3NkjvGe -JmERaLswAVS01JcdA2yYfDlRgxJftijGW56AvyVmko6ZPe8dtY5FbfgIAxAtWrrm -ss9N3MS0jII1FxerNEB6Qg== ------END PRIVATE KEY----- diff --git a/certificate.pem b/certificate.pem deleted file mode 100644 index a5a264f..0000000 --- a/certificate.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDLTCCAhWgAwIBAgIUchaBMPWLkCvnsAtp9qohJfGWr7kwDQYJKoZIhvcNAQEL -BQAwGzEZMBcGA1UEAwwQVGVzdCBDZXJ0aWZpY2F0ZTAeFw0yMzA5MTQxNDE3MTZa -Fw0yMzEwMTQxNDE3MTZaMBsxGTAXBgNVBAMMEFRlc3QgQ2VydGlmaWNhdGUwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVyXZN1oL+6D1jVuzjSyExD5P6 -SOrDwC3hviZmhjP9jeOwG9gb7bi+lza40SxvtDMNB0QzZQFPjMKTJca0idpLV3+v -2hhficwbwfq2T5EialOfiQBOglXUsQBnuK2VbGOOoedO115BOLdoZ0jd0HzWtgH1 -nkd8scX8YsGxqc+dGnv2nzkTSnr25hL6Go+kPiPH87HFDgUoQnO4bPAtHMljJVQv -5+vuC7DMEbNC3VprQp4DCbXjc2OPFO1RIrlMrOQufDOGtKcPaQSuyJBh4BXs0PL6 -sMEuRKlinEkeGYSDRe0jG084KBz82NpcHcl2GdChCGzeUCB5utxtVIdf0kPvAgMB -AAGjaTBnMB0GA1UdDgQWBBTUe5gB7JjjNnvY/1qhvcm3DlDGUTAfBgNVHSMEGDAW -gBTUe5gB7JjjNnvY/1qhvcm3DlDGUTAPBgNVHRMBAf8EBTADAQH/MBQGA1UdEQQN -MAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAOnkFLGRpJMx2FkQSt0Pi -PzXdPUx5PiR3gutu9TpJv18hhtNeuyto/oGefrcp8lX4CHwogc6ZXQISxpSoJiKQ -4b04qydfFEbWkPvGkgjN/p0TgRftNglGgT78sc2xKgVXZ2BVg8uq4U8gfsOxHtpM -eA4WGwXGeB2+K+RddkfuLkff9wYhsrH2n5VavdHVfFHaZhF1nivSM+soEaz9TZ1o -ht3JuCxQ8Q8vXR851/Uvlxjli7TX3fDZO2dMGtttpT8yKBjjcQ62JiHDb6dGh/dW -AtlJy/f7f8XCPK8CfK/fAlEIh+P6YByO88J1pBNbbJSOfP4ncB7XqbCbYq8PMPdp -Cw== ------END CERTIFICATE----- diff --git a/deno.ts b/deno.ts deleted file mode 100644 index b55add4..0000000 --- a/deno.ts +++ /dev/null @@ -1,100 +0,0 @@ -const lib = Deno.dlopen("./target/release/ftlt.dll", { - start: { - parameters: ["function", "buffer", "pointer"], - result: "pointer", - callback: true, - }, - handle_session: { - parameters: ["pointer", "pointer"], - result: "pointer", - nonblocking: true, - }, - init_runtime: { - parameters: [], - result: "pointer", - }, - proc_rec: { - parameters: ["pointer"], - result: "pointer", - nonblocking: true, - }, - proc_rec_streams: { - parameters: ["pointer", "pointer", "pointer"], - result: "void", - nonblocking: true, - }, - proc_recv_ch_datagram: { - parameters: ["pointer", "pointer", "buffer"], - result: "usize", - nonblocking: true, - }, - test_proc: { - parameters: ["pointer"], - result: "void", - nonblocking: true, - }, -}) - -const ptrstate = new Uint32Array(1); - -const sender = new Deno.UnsafeCallback( - { - parameters: ["u32", "pointer", "u32"], - result: "void", - }, - (_code: unknown | number, buffer, buflen) => { - const code = _code as typeof ptrstate[0]; - if (buflen < 0) { - return; - } - const pointer = Deno.UnsafePointerView.getArrayBuffer( - buffer as unknown as NonNullable, - buflen, - ); - }, - ); - const runtime = lib.symbols.init_runtime(); - const resptr = lib.symbols.start(sender.pointer, ptrstate, runtime); - await lib.symbols.handle_session(resptr, runtime); - - -Promise.all([(async () => { - let client = await lib.symbols.proc_rec(resptr); - - while(client !== null) { - console.log("New connection"); - await lib.symbols.proc_rec_streams(resptr, runtime, client) - // //start a new thread to handle the connection - // lib.symbols.proc_rec_streams(resptr, client); - // // - // console.log("Connection handled"); - - // // let mut buffer = vec![0; 65536].into_boxed_slice(); - Promise.all([(async () => { - let buffer = new Uint8Array(65536); - let res = await lib.symbols.proc_recv_ch_datagram(resptr, client, buffer); - while (res > 0) { - const ress = buffer.subarray(0, res as number); - console.log(ress); - buffer = buffer.fill(0); - res = await lib.symbols.proc_recv_ch_datagram(resptr, client, buffer); - } - })()]); - - // // const buffview = new Deno.UnsafePointerView(res!); - // // console.log(buffview.getBigInt64(0)); - client = await lib.symbols.proc_rec(resptr); - } -})()]); - -// setInterval(() => { -// console.log(ptrstate[0]); -// }, 5000); - -Deno.serve((_req: Request) => { - return new Response(Deno.readTextFileSync("./index.html"), { - headers: { - "content-type": "text/html" - } - }); -}) \ No newline at end of file diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 0000000..73833d1 --- /dev/null +++ b/examples/index.html @@ -0,0 +1,103 @@ + + + + + + + Document + + + + + + + \ No newline at end of file diff --git a/fingerprint.hex b/fingerprint.hex deleted file mode 100644 index 0ce454c..0000000 --- a/fingerprint.hex +++ /dev/null @@ -1 +0,0 @@ -SHA2-256(stdin)= 76055c1a369c1eb43d62225a087bea480d2e6c2f4c7a67d8d85f19f83a9ddf84 diff --git a/index.html b/index.html deleted file mode 100644 index 9386332..0000000 --- a/index.html +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - Document - - - - - - - \ No newline at end of file diff --git a/key.pem b/key.pem deleted file mode 100644 index 754bd72..0000000 --- a/key.pem +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgf/I2d7oP44Q1UKSr -mAr3PDoq4ez61Yhsct0yiEbFBkOhRANCAASXNjGpiW3z8G5HaaQ0Qn8uZUbckzpy -AHqnnJtpPAFDxgrTAVTLOwmLQhRafzdUPLzuIMiFzzknydayU/P8HNWm ------END PRIVATE KEY----- diff --git a/mod/crypto.ts b/mod/crypto.ts new file mode 100644 index 0000000..5521354 --- /dev/null +++ b/mod/crypto.ts @@ -0,0 +1,28 @@ + +export function base64ToArrayBuffer(base64: string) { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} + +export async function calculateSHA256(buffer: Uint8Array) { + const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + return hashHex; +} + +export function readCertFile(certpath: string) { + try { + Deno.statSync(certpath); + } catch { + throw new Error("Certificate file does not exist"); + } + const cert = Deno.readTextFileSync(certpath); + const certBase64 = cert.replace(/-----BEGIN CERTIFICATE-----/g, "").replace(/-----END CERTIFICATE-----/g, "").replace(/\n/g, ""); + const certBuffer = base64ToArrayBuffer(certBase64); + return certBuffer; +} \ No newline at end of file diff --git a/mod/deno.ts b/mod/deno.ts new file mode 100644 index 0000000..f75ec5c --- /dev/null +++ b/mod/deno.ts @@ -0,0 +1,75 @@ +import { calculateSHA256, readCertFile } from "./crypto.ts"; +import { PackedStruct, u32, u8, NullTerminatedString } from "https://deno.land/x/byte_type@0.2.2/mod.ts"; + +import { LIB } from "./lib.ts"; +const lib = LIB; + + +const ptrstate = new Uint32Array(1); + +const sender = new Deno.UnsafeCallback( + { + parameters: ["u32", "pointer", "u32"], + result: "void", + }, + (_code: unknown | number, buffer, buflen) => { + const code = _code as typeof ptrstate[0]; + console.log(code); + if (buflen < 0) { + return; + } + const pointer = Deno.UnsafePointerView.getArrayBuffer( + buffer as unknown as NonNullable, + buflen, + ); + console.log(pointer); + }, +); +try { + const resptr = lib.symbols.proc_init(4433, sender.pointer); + lib.symbols.proc_listen(resptr); +} catch (e) { + // console.error(e); +} +// + +// Promise.all([(async () => { +// let client = await lib.symbols.proc_rec(resptr); + +// while(client !== null) { +// console.log("New connection"); +// await lib.symbols.proc_rec_streams(resptr, runtime, client) +// // //start a new thread to handle the connection +// // lib.symbols.proc_rec_streams(resptr, client); +// // // +// // console.log("Connection handled"); + +// // // let mut buffer = vec![0; 65536].into_boxed_slice(); +// Promise.all([(async () => { +// let buffer = new Uint8Array(65536); +// let res = await lib.symbols.proc_recv_ch_datagram(resptr, client, buffer); +// while (res > 0) { +// const ress = buffer.subarray(0, res as number); +// console.log(ress); +// buffer = buffer.fill(0); +// res = await lib.symbols.proc_recv_ch_datagram(resptr, client, buffer); +// } +// })()]); + +// // // const buffview = new Deno.UnsafePointerView(res!); +// // // console.log(buffview.getBigInt64(0)); +// client = await lib.symbols.proc_rec(resptr); +// } +// })()]); + +// // setInterval(() => { +// // console.log(ptrstate[0]); +// // }, 5000); + +// Deno.serve((_req: Request) => { +// return new Response(Deno.readTextFileSync("./index.html"), { +// headers: { +// "content-type": "text/html" +// } +// }); +// }) diff --git a/mod/deps.ts b/mod/deps.ts new file mode 100644 index 0000000..ffa5ca4 --- /dev/null +++ b/mod/deps.ts @@ -0,0 +1 @@ +export { type FetchOptions, dlopen } from "https://deno.land/x/plug@1.0.2/mod.ts"; \ No newline at end of file diff --git a/mod/lib.ts b/mod/lib.ts new file mode 100644 index 0000000..21170de --- /dev/null +++ b/mod/lib.ts @@ -0,0 +1,50 @@ +import { FetchOptions, dlopen } from "./deps.ts"; + +const symbols = { + proc_init: { + parameters: ["u16", "function"], + result: "pointer", + callback: true, + }, + proc_listen: { + parameters: ["pointer"], + result: "pointer", + nonblocking: true, + }, + proc_newconn: { + parameters: ["pointer"], + result: "pointer", + nonblocking: true, + }, + proc_init_client_streams: { + parameters: ["pointer", "pointer", "buffer"], + result: "void", + nonblocking: true, + }, + proc_recv_datagram: { + parameters: ["pointer", "pointer", "buffer"], + result: "usize", + nonblocking: true, + }, + proc_send_datagram: { + parameters: ["pointer", "pointer", "buffer", "usize"], + result: "void", + nonblocking: false, + }, + + // DEBUG CODE + test_proc: { + parameters: ["pointer"], + result: "void", + nonblocking: true, + }, +} as const; + +//TODO(hironichu): Make this works from internet path +const options: FetchOptions = { + name: "ftlt", + url: "./target/release/", +}; + + +export const LIB = await dlopen(options,symbols); \ No newline at end of file diff --git a/src/certificate.rs b/src/certificate.rs new file mode 100644 index 0000000..dc9fdf4 --- /dev/null +++ b/src/certificate.rs @@ -0,0 +1,53 @@ +use anyhow::Result; +use base64::engine::general_purpose::STANDARD as Base64Engine; +use base64::Engine; +use rcgen::CertificateParams; +use rcgen::DistinguishedName; +use rcgen::DnType; +use rcgen::KeyPair; +use rcgen::PKCS_ECDSA_P256_SHA256; +use ring::digest::digest; +use ring::digest::SHA256; +use time::Duration; +use time::OffsetDateTime; + +#[derive(Clone)] +pub struct SelfCertificate { + /// DER certificate. + pub certificate: Vec, + + /// DER private key. + pub key: Vec, + + /// Base64 SHA256 public key. + pub fingerprint: String, +} + +/// Generates a self-signed certificate for WebTransport connections. +pub fn generate_certificate>(common_name: S) -> Result { + let keypair = KeyPair::generate(&PKCS_ECDSA_P256_SHA256)?; + let digest = digest(&SHA256, &keypair.public_key_der()); + let fingerprint = Base64Engine.encode(digest); + + let mut dname = DistinguishedName::new(); + dname.push(DnType::CommonName, common_name.as_ref()); + + let mut cert_params = CertificateParams::new(vec![common_name.as_ref().to_string()]); + cert_params.distinguished_name = dname; + cert_params.alg = &PKCS_ECDSA_P256_SHA256; + cert_params.key_pair = Some(keypair); + cert_params.not_before = OffsetDateTime::now_utc() + .checked_sub(Duration::days(5)) + .unwrap(); + cert_params.not_after = OffsetDateTime::now_utc() + .checked_add(Duration::days(5)) + .unwrap(); + + let certificate = rcgen::Certificate::from_params(cert_params)?; + + Ok(SelfCertificate { + certificate: certificate.serialize_der()?, + key: certificate.serialize_private_key_der(), + fingerprint, + }) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 309304d..77b9cd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,12 @@ use client::ClientConn; use flume::Receiver; use flume::Sender; -// use futures_util::{pin_mut, select, FutureExt}; +use once_cell::sync::Lazy; +use wtransport::config::ServerConfigBuilder; +use wtransport::config::WantsBindAddress; use std::time::Duration; use tokio::runtime::Runtime; -// use wtransport::datagram::Datagram; use wtransport::endpoint; - -// use wtransport::endpoint::SessionRequest; use wtransport::tls::Certificate; use wtransport::Connection; use wtransport::Endpoint; @@ -15,7 +14,7 @@ use wtransport::ServerConfig; mod client; mod executor; - +mod certificate; #[repr(C)] pub struct ErrorMessage { pub code: i32, @@ -23,38 +22,40 @@ pub struct ErrorMessage { } pub struct WebTransport { - /// The innter Server object pub server: Option>, - // + conn_ch_sender: Option>, conn_ch_receiver: Option>, - // datagram_ch_sender: Option>, - // datagram_ch_receiver: Option>, pub state: Option, } +///------------------------------------ code msg buf len static mut SEND_FN: Option = None; +static mut RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); + impl WebTransport { - pub(crate) fn new( + + pub(crate) unsafe fn new( port: u16, sender_fn: Option, - _runtime: &mut Runtime, + config: ServerConfig ) -> Result { - unsafe { - SEND_FN = sender_fn; - }; - let _guard = _runtime.enter(); - let config = ServerConfig::builder() - .with_bind_default(port) - .with_certificate(Certificate::load("cert.crt", "cert.key").unwrap()) - .keep_alive_interval(Some(Duration::from_secs(1))) - .build(); + SEND_FN = sender_fn; + + let _guard = RUNTIME.enter(); - let (conn_sender, conn_reciever) = flume::unbounded(); - // let (datagram_sender, datagram_receiver) = flume::unbounded(); + // let config = ServerConfig::builder() + // .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) + // .with_certificate(Certificate::load("cert.pem", "cert.pem").unwrap()) + // .keep_alive_interval(Some(Duration::from_secs(1))) + // .allow_migration(true) + // .max_idle_timeout(Some(Duration::from_secs(20))); + // .build(); + let (conn_sender, conn_reciever) = flume::unbounded(); + //get the ref of config let server = match Endpoint::server(config) { Ok(server) => server, Err(e) => { @@ -62,26 +63,21 @@ impl WebTransport { return Err(1); } }; - // - // self.handle_sess_in(_runtime); - // Ok(Self { server: Some(server), state: Some(true), conn_ch_sender: Some(conn_sender), conn_ch_receiver: Some(conn_reciever), - // datagram_ch_sender: Some(datagram_sender), - // datagram_ch_receiver: Some(datagram_receiver), }) } - pub(crate) unsafe fn handle_sess_in(&'static mut self, runtime: *mut Runtime) { + pub(crate) unsafe fn handle_sess_in(&'static mut self) { println!("Waiting for session request..."); - let rt = runtime.as_mut().unwrap(); + // let rt = runtime.as_mut().unwrap(); // let _ = rt.enter(); - let handle = rt.handle().clone(); + let handle = RUNTIME.handle().clone(); - rt.spawn(async move { + executor::spawn(async move { for id in 0.. { let sender = self.conn_ch_sender.as_ref().unwrap(); let incoming_session = self.server.as_mut().unwrap().accept().await; @@ -102,104 +98,64 @@ impl WebTransport { // self.conn_ch_sender.as_mut().unwrap().send(connection).unwrap(); }); } - // for _ in 0.. { - // // let receiver = self.conn_ch_receiver.as_mut().unwrap(); - // let sender = self.conn_ch_sender.as_ref().unwrap(); - // let next = { - // // let insess_receiver = receiver.recv_async().fuse(); - // let incoming_session = self.server.as_mut().unwrap().accept().fuse(); - - // // pin_mut!(sender); - // pin_mut!(incoming_session); - // select! { - // result = incoming_session => { - // let sess_req = result.await; - // Next::NewSessionRequest(sess_req) - // } - // } - // }; - // match next { - // Next::NewSessionRequest(sessreq) => { - // match sessreq { - // Ok(sessreq) => { - // println!( - // "Received Session Request from client: {:?}", - // sessreq.authority() - // ); - // // self.handle_session_impl(sessreq.accept().await); - // let conn = sessreq.accept().await.unwrap(); - // println!( - // "Accepted session request from client: {:?}", - // conn.remote_address() - // ); - - // let _ = sender.send(conn); - // // self.conn_ch_sender.as_mut().unwrap().send(conn).unwrap(); - // } - // Err(e) => { - // println!("Error accepting session request: {:?}", e); - // } - // } - // // insess_sender.send_async(sessreq).await.unwrap(); - // } - // } - // } - }); + }).detach(); } } +/// #[no_mangle] -pub extern "C" fn init_runtime() -> *mut Runtime { - Box::into_raw(Box::new(Runtime::new().unwrap())) +pub unsafe extern "C" fn proc_create_config(port: u16, timeout: u64, keepalive: u64, migration: bool, ) -> *mut ServerConfig { + // let config = ServerConfig::builder(); + let config = ServerConfig::builder() + .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) + .with_certificate(Certificate::load("cert.pem", "cert.pem").unwrap()) + .keep_alive_interval(Some(Duration::from_secs(keepalive))) + .max_idle_timeout(Some(Duration::from_secs(timeout))).unwrap() + .allow_migration(migration) + .build(); + let config_ptr = Box::into_raw(Box::new(config)); + config_ptr } #[no_mangle] -pub unsafe extern "C" fn start( +pub unsafe extern "C" fn proc_init( + port: u16, send_func: Option, - res: *mut u32, - rt_ptr: *mut Runtime, + configptr: *mut ServerConfig ) -> *mut WebTransport { - assert!(!rt_ptr.is_null()); - assert!(!res.is_null()); + assert!(!send_func.is_none()); + assert!(port > 0); + let config = &mut *configptr; - let _runtime = &mut *rt_ptr; - let server = WebTransport::new(4433, send_func, _runtime); + let server = WebTransport::new(port, send_func, config); match server { Ok(server) => { let server_ptr = Box::into_raw(Box::new(server)); - *res = 0; server_ptr } Err(_) => { - *res = 2; - std::ptr::null_mut() + panic!("Error creating server") } } } #[no_mangle] -pub unsafe extern "C" fn handle_session(server_ptr: *mut WebTransport, runtime: *mut Runtime) { +pub unsafe extern "C" fn proc_listen(server_ptr: *mut WebTransport) { assert!(!server_ptr.is_null()); - assert!(!runtime.is_null()); let server = &mut *server_ptr; - let _runtime = &mut *runtime; - server.handle_sess_in(_runtime); + server.handle_sess_in(); } #[no_mangle] -pub unsafe extern "C" fn proc_rec(srv: *mut WebTransport) -> *mut ClientConn { +pub unsafe extern "C" fn proc_newconn(srv: *mut WebTransport) -> *mut ClientConn { assert!(!srv.is_null()); let server = &mut *srv; - println!("DBG: RECEIVER READY"); match server.conn_ch_receiver.as_ref().unwrap().recv() { Ok(conn) => { - // println!("New client : {:?}", conn.remote_address()); - // let connptr = Box::into_raw(Box::new(conn)); let client_conn = ClientConn::new(conn); let clientptr = Box::into_raw(Box::new(client_conn)); - // Box::into_raw(Box::new(conn)) clientptr } _ => { @@ -208,26 +164,25 @@ pub unsafe extern "C" fn proc_rec(srv: *mut WebTransport) -> *mut ClientConn { } } -//create a method that creates a unbound channel and returns a pointer to the sender/receiver pair to the FFI - #[no_mangle] -pub unsafe extern "C" fn proc_rec_streams(srv: *mut WebTransport, rt: *mut Runtime, clientptr: *mut ClientConn) { +pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, clientptr: *mut ClientConn, _buffer : *mut u8) { assert!(!clientptr.is_null()); assert!(!srv.is_null()); + let client = &mut *clientptr; let _server = &mut *srv; - let _runtime = &mut *rt; - // let connection = client.get_conn(); + let sender = client.datagram_ch_sender.clone(); - println!("CONN RECEIVER PROC SET & READY"); - // let rthandle = _runtime.handle(); - _runtime.spawn(async move { - let mut buffer = vec![0; 65536].into_boxed_slice(); + println!("DBG: CONN RECEIVER PROC SET & READY"); + // let rthandle = RUNTIME.handle(); + // let mut buffer =::std::slice::from_raw_parts_mut(buffer, 65536); + executor::spawn(async move { + let mut buffer = vec![0; 65536].into_boxed_slice(); + //use the buffer from the args and set it to a box slice + let _ = RUNTIME.enter(); loop { - // let sender = sender; - // println!("Waiting for datagram from client"); tokio::select! { stream = client.conn.accept_bi() => { match stream { @@ -241,14 +196,11 @@ pub unsafe extern "C" fn proc_rec_streams(srv: *mut WebTransport, rt: *mut Runti stream.0.write_all(b"ACK").await.unwrap(); }, - _ => { - - } + _ => {} }; } stream = client.conn.accept_uni() => { - // let mut stream = stream; match stream { Ok(mut stream) => { println!("Accepted UNI stream"); @@ -264,40 +216,34 @@ pub unsafe extern "C" fn proc_rec_streams(srv: *mut WebTransport, rt: *mut Runti let mut stream = client.conn.open_uni().await.unwrap().await.unwrap(); stream.write_all(b"ACK").await.unwrap(); }, - _ => { - // println!("Error accepting UNI stream: {:?}", e); - } + _ => {} } } - dgram = client.conn.receive_datagram() => { - match dgram { - Ok(dgram) => { - - println!("Received datagram from client"); - sender.send(dgram).unwrap(); - // let str_data = std::str::from_utf8(&dgram).unwrap(); - // println!("Received (dgram) '{str_data}' from client"); - client.conn.send_datagram(b"ACK").unwrap(); - }, - _ => { - // break; - // println!("Error receiving datagram"); - } - } - }, + stream = client.conn.receive_datagram() => { + match stream { + Ok(dgram) => { + let _ = sender.send_async(dgram).await; + //TODO(hironichu): Remove this debug line + client.conn.send_datagram(b"ACK").unwrap(); + }, + _ => {} + } + }, } } - });//.detach(); + }).detach(); } #[no_mangle] -pub unsafe extern "C" fn proc_recv_ch_datagram(srv: *mut WebTransport, clientptr: *mut ClientConn, buff: *mut u8) -> usize { +pub unsafe extern "C" fn proc_recv_datagram(srv: *mut WebTransport, clientptr: *mut ClientConn, buff: *mut u8) -> usize { assert!(!clientptr.is_null()); assert!(!srv.is_null()); + let client = &mut *clientptr; let _server = &mut *srv; + match client.datagram_ch_receiver.recv() { Ok(dgram) => { ::std::slice::from_raw_parts_mut(buff, dgram.len()).copy_from_slice(&dgram); @@ -307,9 +253,54 @@ pub unsafe extern "C" fn proc_recv_ch_datagram(srv: *mut WebTransport, clientptr } } +#[no_mangle] +pub unsafe extern "C" fn proc_send_datagram(srv: *mut WebTransport, clientptr: *mut ClientConn, buf: *const u8, buflen: u32) { + assert!(!clientptr.is_null()); + assert!(!srv.is_null()); + + let client = &mut *clientptr; + let _server = &mut *srv; + let buf = ::std::slice::from_raw_parts(buf, buflen as usize); + + client.conn.send_datagram(buf).unwrap(); //TODO: Handle error +} + + #[no_mangle] pub unsafe extern "C" fn test_proc(client: *mut ClientConn) { assert!(!client.is_null()); let client = &mut *client; println!("TEST PROC {} ", client.conn.remote_address()); } + + +#[no_mangle] +pub extern "C" fn proc_gencert(buffpath: *mut u8) -> usize { + //get the underlying buffer and use it to return the path to the cert + let path = unsafe { std::ffi::CStr::from_ptr(buffpath as *const i8) }; + let path = path.to_str().unwrap(); + let cert = certificate::generate_certificate("localhost").unwrap(); + std::fs::write(format!("{}/cert.pem", path), cert.certificate).unwrap(); + std::fs::write(format!("{}/key.pem", path), cert.key).unwrap(); + path.len() +} + +//create a free method that frees the memory of every pointer that was allocated +#[no_mangle] +pub unsafe extern "C" fn free_webtransport(_: *mut WebTransport) {} + +#[no_mangle] +pub unsafe extern "C" fn free_clientconn(_: *mut ClientConn) {} + +#[no_mangle] +pub unsafe extern "C" fn free_runtime(_: *mut Runtime) {} + +//free all above once +#[no_mangle] +pub unsafe extern "C" fn free_all( + _a: *mut WebTransport, + _b: *mut ClientConn, + _c: *mut Runtime, +) {} + + diff --git a/src/old_runtime.rs b/src/old_runtime.rs.old similarity index 100% rename from src/old_runtime.rs rename to src/old_runtime.rs.old diff --git a/src/oldlib.rs b/src/oldlib.rs.old similarity index 100% rename from src/oldlib.rs rename to src/oldlib.rs.old From 1f66147d0aa81279c934ca8b8ae38041b6f21380 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sat, 16 Sep 2023 00:27:38 +0200 Subject: [PATCH 02/72] Working code --- deno.jsonc | 9 ++ deno.lock | 75 +++++++++++ examples/index.html | 12 +- examples/web_server.js | 8 ++ mod/deno.ts | 56 ++++++-- mod/lib.ts | 15 +-- src/lib.rs | 166 ++++++++++++------------ src/old_runtime.rs.old | 181 -------------------------- src/oldlib.rs.old | 287 ----------------------------------------- 9 files changed, 232 insertions(+), 577 deletions(-) create mode 100644 deno.jsonc create mode 100644 deno.lock create mode 100644 examples/web_server.js delete mode 100644 src/old_runtime.rs.old delete mode 100644 src/oldlib.rs.old diff --git a/deno.jsonc b/deno.jsonc new file mode 100644 index 0000000..22755c8 --- /dev/null +++ b/deno.jsonc @@ -0,0 +1,9 @@ +{ + "tasks": { + "examples:web": "deno run -A ./examples/web_server.js", + "start": "deno run -A --unstable ./mod/deno.ts" + }, + "compilerOptions": { + "checkJs": true + } +} \ No newline at end of file diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..7e6743c --- /dev/null +++ b/deno.lock @@ -0,0 +1,75 @@ +{ + "version": "3", + "remote": { + "https://deno.land/std@0.184.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", + "https://deno.land/std@0.184.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.184.0/encoding/hex.ts": "b4b1a7cb678745b0bf181ed8cf2498c7be00d121a7de244b752fbf9c7d9c48cd", + "https://deno.land/std@0.184.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", + "https://deno.land/std@0.184.0/fs/_util.ts": "579038bebc3bd35c43a6a7766f7d91fbacdf44bc03468e9d3134297bb99ed4f9", + "https://deno.land/std@0.184.0/fs/copy.ts": "14214efd94fc3aa6db1e4af2b4b9578e50f7362b7f3725d5a14ad259a5df26c8", + "https://deno.land/std@0.184.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", + "https://deno.land/std@0.184.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.184.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", + "https://deno.land/std@0.184.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", + "https://deno.land/std@0.184.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1", + "https://deno.land/std@0.184.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", + "https://deno.land/std@0.184.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", + "https://deno.land/std@0.184.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", + "https://deno.land/std@0.184.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", + "https://deno.land/std@0.184.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", + "https://deno.land/std@0.184.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", + "https://deno.land/std@0.184.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.184.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.184.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", + "https://deno.land/std@0.184.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.184.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.184.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", + "https://deno.land/std@0.184.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", + "https://deno.land/std@0.184.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.184.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", + "https://deno.land/x/byte_type@0.2.2/mod.ts": "f7f562a87f4211510335696c6be7143c7e015e05c58d0632b9f68add6d7fb599", + "https://deno.land/x/byte_type@0.2.2/types/array/array.ts": "08d0d4bf82e6e60dedfc2319d01128f1e756affee5547e021dda6321ba5b97e8", + "https://deno.land/x/byte_type@0.2.2/types/array/array_buffer.ts": "99dc5d135125c9d2cb3f689c266af098e71b6cad6c9ebecaec55fe96c7d216ce", + "https://deno.land/x/byte_type@0.2.2/types/array/mod.ts": "3bcf269bf777f8580bb1cf890e39541846fff0b73d0b867b850e58a9cd05f332", + "https://deno.land/x/byte_type@0.2.2/types/array/typed_array.ts": "e66e489799e83409695519fd446115c8bc532e3bee93c147e4bd085b32046905", + "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags16.ts": "126685d817d7c07b5eadb290db6d466dc5f6775b0404619c1dfc2ec8f8be23a0", + "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags32.ts": "9f658051e75aa70f617b90ab807a97321f1f5e1c0adaabb9134c1bd56b965b31", + "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags64.ts": "f7244243268a67938cb7cc6d002a8389eb3f1277825ca0d448972956fc003055", + "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags8.ts": "dec36625ce3e9ae927f06ef01b9cfa031bb626fd51b8ca4e4dcd48c33af21677", + "https://deno.land/x/byte_type@0.2.2/types/bitflags/mod.ts": "1897c05a4349ab257e80125968cab75103b57c90620e9cf5f0e6747c95f1541e", + "https://deno.land/x/byte_type@0.2.2/types/leb128/_i64leb128.ts": "6e257775fe3b7622bfed3badc558b1f73b1121616d157659837b3f41a4c18936", + "https://deno.land/x/byte_type@0.2.2/types/leb128/i32leb128.ts": "8e1746edb0492ed121b71c2432fe072eac44f06e425c5f13999070e063264b25", + "https://deno.land/x/byte_type@0.2.2/types/leb128/i64leb128.ts": "166d0c9058a2dc9e97dce7694b6c24eced21ef8a93925aa383594276fa861de1", + "https://deno.land/x/byte_type@0.2.2/types/leb128/mod.ts": "cdc37cc01da71fe7b264d86e9f24ec7cc0551b7cbfc0c8862bbdf8c3ae7e22c3", + "https://deno.land/x/byte_type@0.2.2/types/misc/expect.ts": "9c86ba2fad4151f4a25be58b2cdf259e3ef2baca5b0f7051720e82d39e985121", + "https://deno.land/x/byte_type@0.2.2/types/misc/mod.ts": "d0c4aff0831d8cb961f718e0ff9b34006daccb1a26dfc4300c17ff55a1d7486c", + "https://deno.land/x/byte_type@0.2.2/types/mod.ts": "d5152cdf0427cf6729f7f7102876f30092e8054261700097852a99162cb95b3c", + "https://deno.land/x/byte_type@0.2.2/types/primitive/bool.ts": "00c6f66ef35b07aaf304e479209f61d156ad4b61dd3c8558342c85a7610a44a4", + "https://deno.land/x/byte_type@0.2.2/types/primitive/f32.ts": "ee99020ba2ee2bcd8be7f8c36af3be6d780c4d6c5afa79c655731570b307187c", + "https://deno.land/x/byte_type@0.2.2/types/primitive/f64.ts": "430476c8e6de9aa987fa73532dabf82af594659744241c5b3eb5522d24f49816", + "https://deno.land/x/byte_type@0.2.2/types/primitive/i16.ts": "cc12a7d2fc6e0283d9b9fff86e4d4176a71254f122ddc47fe95e81d143f63c20", + "https://deno.land/x/byte_type@0.2.2/types/primitive/i32.ts": "7879b02ed250221c5d1889df9015a8040499e567f0fdc12c056478db0e18a253", + "https://deno.land/x/byte_type@0.2.2/types/primitive/i64.ts": "b9607f4979fd755cb09e70cc0cdfcf0faffb13ad61ae359059976c9463906c5d", + "https://deno.land/x/byte_type@0.2.2/types/primitive/i8.ts": "4cdf0f5cb77603c19dd440f9ad8f129c928860ca580af5a5707e3771524b087b", + "https://deno.land/x/byte_type@0.2.2/types/primitive/mod.ts": "97f7b9928e258ed5efaaf44e808f3162262096e04fadb960f4dacbdcd19d118c", + "https://deno.land/x/byte_type@0.2.2/types/primitive/u16.ts": "d9fd602a03d14fa4c6ccc1749c43f8e0a1d3122d54b8bb516ad41815df7a4c9c", + "https://deno.land/x/byte_type@0.2.2/types/primitive/u32.ts": "bf54b30f35fadf777ee3f472c06c61fe8db5940a28fee80a8d4c2755774c8eed", + "https://deno.land/x/byte_type@0.2.2/types/primitive/u64.ts": "7c71c4f422c719192ef1eb90fbe747c7c86417549f3558f17ba99e2312a29a93", + "https://deno.land/x/byte_type@0.2.2/types/primitive/u8.ts": "142fde887c55f96f3b737b845027d8b0b7e94e2f45627658a00457bcb5f5db88", + "https://deno.land/x/byte_type@0.2.2/types/string/fixed_length.ts": "8e7bda6308a1bad3f8c4b85f4c3c349b994f2a5dada8c270b51bea1d35dfe5ea", + "https://deno.land/x/byte_type@0.2.2/types/string/mod.ts": "0aed4493034a83c46e55ee630484d06ead804e32608e8f4b364e558d2d5b4655", + "https://deno.land/x/byte_type@0.2.2/types/string/null_terminated.ts": "5ce304e52e9bc03a185ecd217c5111ce02065ced6ea96bb531a8a806a3ce07a7", + "https://deno.land/x/byte_type@0.2.2/types/struct/aligned.ts": "cc5d7fb50f9a10443b569e4cc217132e693ea00cc74dd1452681d7dac3301897", + "https://deno.land/x/byte_type@0.2.2/types/struct/mod.ts": "17c32fe69560b2a4671fe60180387c7c3f9256f8ae58175a93f743084aef246c", + "https://deno.land/x/byte_type@0.2.2/types/struct/packed.ts": "f730b4335f7b405a2571da843e4e190323b7fb74b310b68c578c61cbb7ac5a96", + "https://deno.land/x/byte_type@0.2.2/types/struct/struct.ts": "725bc9c26bdc1d5887e714407df3a5cac2bea21ad379fad1d170f405b6163dfd", + "https://deno.land/x/byte_type@0.2.2/types/tuple/mod.ts": "3a11a652a33aef575fadd28d70b78c872f34caab93c39cbb311442c4496ab4d8", + "https://deno.land/x/byte_type@0.2.2/types/types.ts": "db9ba288d3ccaf99f4f5687c4a47d1bd98b92b1e3d4fe10853edf37d568e8577", + "https://deno.land/x/byte_type@0.2.2/utils.ts": "e1458363e86a289e1143785c5e4b08e0f9a50c980a0c782ba07e3a9a68b2b8d7", + "https://deno.land/x/plug@1.0.2/deps.ts": "36846a478fafaa1d8ca18aafd91584a748aa58de47ad0f45f008881dad82f374", + "https://deno.land/x/plug@1.0.2/download.ts": "b92bc1c1ae35fdb75828847f1ebfc7e51bf462f339729740e1cffe78384e1509", + "https://deno.land/x/plug@1.0.2/mod.ts": "32e0006ed6142e7becdb4103c2aa4e1e9ef28459d7243d6cb404a028f7c4eb7e", + "https://deno.land/x/plug@1.0.2/types.ts": "0490359117c53783138f2b6692a34f85bca9237314ba8cdef8ad682d81218d21", + "https://deno.land/x/plug@1.0.2/util.ts": "ded3db6e9bb16b8003899d9073fb310e13231ca617b35d6b7dfd53f38762cc76" + } +} diff --git a/examples/index.html b/examples/index.html index 73833d1..e353e17 100644 --- a/examples/index.html +++ b/examples/index.html @@ -33,12 +33,12 @@ window.serveurDEMO = null; window.writeDDBN = null; - const webtransport = new WebTransport('https://localhost:4433', { + const webtransport = new WebTransport('https://127.0.0.1:12345/unidirectional', { congestionControl: "throughput", - serverCertificateHashes: [{ - hash: "SHA-256", - value: "%BUFFER%", - }], + "serverCertificateHashes": [{ + "algorithm": "sha-256", + "value": new Uint8Array([0x7c, 0xda, 0x57, 0x35, 0xc8, 0xe1, 0x4c, 0x53, 0x2b, 0x9a, 0xd6, 0xf7, 0x6e, 0xe6, 0x54, 0x54, 0x39, 0xa8, 0x6a, 0xa3, 0xd7, 0xd7, 0xc6, 0x3, 0x93, 0xf7, 0xad, 0x7a, 0xc3, 0xcc, 0x32, 0x98]) + }] }) await webtransport.ready; window.serveurDEMO = webtransport; @@ -65,7 +65,7 @@ // }); readData(readable); writeData(writable); - + //send Hello world // await new Promise(resolve => setTimeout(resolve, 1000)); // } diff --git a/examples/web_server.js b/examples/web_server.js new file mode 100644 index 0000000..f01b98f --- /dev/null +++ b/examples/web_server.js @@ -0,0 +1,8 @@ + +Deno.serve((_) => { + return new Response(Deno.readTextFileSync('./examples/index.html'), { + headers: { + 'content-type': 'text/html' + } + }); +}) \ No newline at end of file diff --git a/mod/deno.ts b/mod/deno.ts index f75ec5c..3e678ba 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -3,7 +3,14 @@ import { PackedStruct, u32, u8, NullTerminatedString } from "https://deno.land/x import { LIB } from "./lib.ts"; const lib = LIB; - +export function encode(v: string | Uint8Array): Uint8Array { + if (typeof v !== "string") return v; + return new TextEncoder().encode(v); +} +export function encodeBuffPtr(v: string | Uint8Array): [Uint8Array, number] { + if (typeof v !== "string") return [v, v.length]; + return [new TextEncoder().encode(v), v.length]; +} const ptrstate = new Uint32Array(1); @@ -25,12 +32,42 @@ const sender = new Deno.UnsafeCallback( console.log(pointer); }, ); + +sender.ref(); +let serverPTR; try { - const resptr = lib.symbols.proc_init(4433, sender.pointer); - lib.symbols.proc_listen(resptr); + const certpath = encodeBuffPtr("./certs/cert.crt"); + const keypath = encodeBuffPtr("./certs/cert.key"); + serverPTR = lib.symbols.proc_init( sender.pointer, 12345 ,true, 20, 100, certpath[0], certpath[1], keypath[0], keypath[1]); + const new_connection = new Deno.UnsafeCallback( + { + parameters: [ "pointer" ], + result: "void", + }, + (client) => { + console.log("DENO : New connection"); + lib.symbols.proc_init_client_streams(serverPTR!, client, new Uint32Array(1)); + const encodedmesg = encodeBuffPtr("Hello from deno"); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + }, + ); + new_connection.ref(); + lib.symbols.proc_listen(serverPTR, new_connection.pointer); } catch (e) { - // console.error(e); + sender.unref(); + console.error(e); } +if (!serverPTR) throw new Error("Server is not running"); + // // Promise.all([(async () => { @@ -62,14 +99,5 @@ try { // } // })()]); -// // setInterval(() => { -// // console.log(ptrstate[0]); -// // }, 5000); -// Deno.serve((_req: Request) => { -// return new Response(Deno.readTextFileSync("./index.html"), { -// headers: { -// "content-type": "text/html" -// } -// }); -// }) + diff --git a/mod/lib.ts b/mod/lib.ts index 21170de..e049855 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -2,20 +2,18 @@ import { FetchOptions, dlopen } from "./deps.ts"; const symbols = { proc_init: { - parameters: ["u16", "function"], + parameters: ["function", "u16", "bool", "u64", "u64","buffer","usize","buffer","usize"], result: "pointer", callback: true, }, proc_listen: { - parameters: ["pointer"], + parameters: ["pointer", "function"], result: "pointer", - nonblocking: true, - }, - proc_newconn: { - parameters: ["pointer"], - result: "pointer", - nonblocking: true, }, + // proc_newconn: { + // parameters: ["pointer"], + // result: "void", + // }, proc_init_client_streams: { parameters: ["pointer", "pointer", "buffer"], result: "void", @@ -43,6 +41,7 @@ const symbols = { //TODO(hironichu): Make this works from internet path const options: FetchOptions = { name: "ftlt", + cache: "reloadAll", url: "./target/release/", }; diff --git a/src/lib.rs b/src/lib.rs index 77b9cd2..4083609 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,60 +1,43 @@ use client::ClientConn; -use flume::Receiver; -use flume::Sender; +// use flume::Receiver; +// use flume::Sender; use once_cell::sync::Lazy; -use wtransport::config::ServerConfigBuilder; -use wtransport::config::WantsBindAddress; +use std::path::Path; use std::time::Duration; use tokio::runtime::Runtime; use wtransport::endpoint; use wtransport::tls::Certificate; -use wtransport::Connection; +// use wtransport::Connection; use wtransport::Endpoint; use wtransport::ServerConfig; mod client; mod executor; mod certificate; -#[repr(C)] -pub struct ErrorMessage { - pub code: i32, - pub message: String, -} pub struct WebTransport { pub server: Option>, - conn_ch_sender: Option>, - conn_ch_receiver: Option>, + // conn_ch_sender: Option>, + // conn_ch_receiver: Option>, + pub conn_cb: Option, pub state: Option, } ///------------------------------------ code msg buf len static mut SEND_FN: Option = None; - +static mut CONN_FN: Option = None; static mut RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); impl WebTransport { pub(crate) unsafe fn new( - port: u16, sender_fn: Option, config: ServerConfig ) -> Result { - SEND_FN = sender_fn; - let _guard = RUNTIME.enter(); - - // let config = ServerConfig::builder() - // .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) - // .with_certificate(Certificate::load("cert.pem", "cert.pem").unwrap()) - // .keep_alive_interval(Some(Duration::from_secs(1))) - // .allow_migration(true) - // .max_idle_timeout(Some(Duration::from_secs(20))); - // .build(); - - let (conn_sender, conn_reciever) = flume::unbounded(); + // let (conn_sender, conn_reciever) = flume::unbounded(); //get the ref of config let server = match Endpoint::server(config) { Ok(server) => server, @@ -64,70 +47,90 @@ impl WebTransport { } }; Ok(Self { + conn_cb: None, server: Some(server), state: Some(true), - conn_ch_sender: Some(conn_sender), - conn_ch_receiver: Some(conn_reciever), + // conn_ch_sender: Some(conn_sender), + // conn_ch_receiver: Some(conn_reciever), }) } pub(crate) unsafe fn handle_sess_in(&'static mut self) { - println!("Waiting for session request..."); - // let rt = runtime.as_mut().unwrap(); - // let _ = rt.enter(); - let handle = RUNTIME.handle().clone(); + + let handle = RUNTIME.handle(); executor::spawn(async move { - for id in 0.. { - let sender = self.conn_ch_sender.as_ref().unwrap(); + println!("Started thread"); + loop { let incoming_session = self.server.as_mut().unwrap().accept().await; - println!("SEssion Number {}", id); + handle.spawn(async move { - let _test = sender.capacity(); let _buffer = vec![0; 65536].into_boxed_slice(); - println!("Waiting for session request..."); - let session_request = incoming_session.await.unwrap(); + println!("DBG: Waiting for session request..."); + let session_request = incoming_session.await; + let accepted_session = match session_request { + Ok(session_request) => session_request, + Err(e) => { + //TODO(hironichu): Handle error with callback SENDER_FN + println!("Error accepting session: {:?}", e); + return ; + } + }; println!( - "New session: Authority: '{}', Path: '{}'", - session_request.authority(), - session_request.path() + "DBG: New session: Authority: '{}', Path: '{}'", + accepted_session.authority(), + accepted_session.path() ); - let connection = session_request.accept().await.unwrap(); - println!("Waiting for data from client..."); - let _ = sender.send_async(connection).await; - // self.conn_ch_sender.as_mut().unwrap().send(connection).unwrap(); + match accepted_session.accept().await { + Ok(conn) => { + println!("DBG: Sending connection to channel."); + let client = ClientConn::new(conn); + let client_ptr = Box::into_raw(Box::new(client)); + assert!(!CONN_FN.is_none()); //TODO(hironichu): Handle this better. + CONN_FN.unwrap()(client_ptr); + }, + _ => { + println!("Error accepting connection"); + } + } + }); } }).detach(); } } -/// -#[no_mangle] -pub unsafe extern "C" fn proc_create_config(port: u16, timeout: u64, keepalive: u64, migration: bool, ) -> *mut ServerConfig { - // let config = ServerConfig::builder(); - let config = ServerConfig::builder() - .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) - .with_certificate(Certificate::load("cert.pem", "cert.pem").unwrap()) - .keep_alive_interval(Some(Duration::from_secs(keepalive))) - .max_idle_timeout(Some(Duration::from_secs(timeout))).unwrap() - .allow_migration(migration) - .build(); - let config_ptr = Box::into_raw(Box::new(config)); - config_ptr -} #[no_mangle] pub unsafe extern "C" fn proc_init( + send_func: Option, port: u16, - send_func: Option, - configptr: *mut ServerConfig + migration: bool, + keepalive: u64, + timeout: u64, + cert_path: *const u8, + cert_path_len: usize, + key_path: *const u8, + key_path_len: usize, ) -> *mut WebTransport { assert!(!send_func.is_none()); assert!(port > 0); - let config = &mut *configptr; - let server = WebTransport::new(port, send_func, config); + let cert_path = ::std::slice::from_raw_parts(cert_path, cert_path_len); + let key_path = ::std::slice::from_raw_parts(key_path, key_path_len); + let cert_path = Path::new(std::str::from_utf8(cert_path).unwrap()); + let key_path = Path::new(std::str::from_utf8(key_path).unwrap()); + + let certificates = Certificate::load(cert_path, key_path).unwrap(); + //print the paths for debug + let config = ServerConfig::builder() + .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) + .with_certificate(certificates) + .keep_alive_interval(Some(Duration::from_secs(keepalive))) + .max_idle_timeout(Some(Duration::from_secs(timeout))).unwrap() + .allow_migration(migration) + .build(); + let server = WebTransport::new(send_func, config); match server { Ok(server) => { let server_ptr = Box::into_raw(Box::new(server)); @@ -140,29 +143,30 @@ pub unsafe extern "C" fn proc_init( } #[no_mangle] -pub unsafe extern "C" fn proc_listen(server_ptr: *mut WebTransport) { +pub unsafe extern "C" fn proc_listen(server_ptr: *mut WebTransport, cb: Option) { assert!(!server_ptr.is_null()); let server = &mut *server_ptr; + server.conn_cb = cb; + CONN_FN = cb; server.handle_sess_in(); } -#[no_mangle] -pub unsafe extern "C" fn proc_newconn(srv: *mut WebTransport) -> *mut ClientConn { - assert!(!srv.is_null()); - - let server = &mut *srv; - - match server.conn_ch_receiver.as_ref().unwrap().recv() { - Ok(conn) => { - let client_conn = ClientConn::new(conn); - let clientptr = Box::into_raw(Box::new(client_conn)); - clientptr - } - _ => { - panic!("Error receiving connection"); - } - } -} +// #[no_mangle] +// pub unsafe extern "C" fn proc_newconn(srv: *mut WebTransport, cb: Option) { +// assert!(!srv.is_null()); +// assert!(!cb.is_none()); +// let server = &mut *srv; +// match server.conn_ch_receiver.as_ref().unwrap().recv() { +// Ok(conn) => { +// let client_conn = ClientConn::new(conn); +// let client_ptr = Box::into_raw(Box::new(client_conn)); +// cb.unwrap()(client_ptr); +// } +// _ => { +// //panic!("Error receiving connection"); +// } +// } +// } #[no_mangle] pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, clientptr: *mut ClientConn, _buffer : *mut u8) { diff --git a/src/old_runtime.rs.old b/src/old_runtime.rs.old deleted file mode 100644 index 4f447b6..0000000 --- a/src/old_runtime.rs.old +++ /dev/null @@ -1,181 +0,0 @@ -rt.spawn(async move{ - for _id in 0.. { - let incoming_session = self.server.as_mut().unwrap().accept().await; - let mut buffer = vec![0; 65536].into_boxed_slice(); - - println!("Waiting for session request..."); - let session_request = incoming_session.await; - // println!("Session request received ID : {}", id); - let session_request = match session_request { - Ok(sessreq) => sessreq, - Err(e) => { - println!("Error accepting session request: {:?}", e); - continue ; - } - }; - println!( - "New session: Authority: '{}', Path: '{}'", - session_request.authority(), - session_request.path() - ); - - let connection = session_request.accept().await.unwrap(); - - println!("Waiting for data from client..."); - - executor::spawn(async move { - loop { - tokio::select! { - // stream = connection.accept_bi() => { - // match stream { - // Ok(mut stream) => { - - // println!("Accepted BI stream"); - // let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); - // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // println!("Received (bi) '{str_data}' from client"); - - // stream.0.write_all(b"ACK").await.unwrap(); - // }, - // Err(e) => { - // break ; - // } - // }; - - // } - // stream = connection.accept_uni() => { - // // let mut stream = stream; - // match stream { - // Ok(mut stream) => { - // println!("Accepted UNI stream"); - // let bytes_read = match stream.read(&mut buffer).await.unwrap() { - // Some(bytes_read) => bytes_read, - // None => continue, - // }; - - // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // println!("Received (uni) '{str_data}' from client"); - - // let mut stream = connection.open_uni().await.unwrap().await.unwrap(); - // stream.write_all(b"ACK").await.unwrap(); - // }, - // _ => { - // // println!("Error accepting UNI stream: {:?}", e); - // break ; - // } - // } - - // } - dgram = connection.receive_datagram() => { - match dgram { - Ok(dgram) => { - println!("Received datagram from client"); - let str_data = std::str::from_utf8(&dgram).unwrap(); - println!("Received (dgram) '{str_data}' from client"); - connection.send_datagram(b"ACK").unwrap(); - }, - _ => { - // break; - break ; - } - } - } - } - } - }).detach(); - - } -}); - -pub fn handle_incoming(&'static mut self, connection: Connection) { - // executor::spawn(async move { - // loop { - // let sender = self.datagram_ch_sender.as_ref().unwrap(); - // tokio::select! { - // // stream = connection.accept_bi() => { - // // match stream { - // // Ok(mut stream) => { - - // // println!("Accepted BI stream"); - // // let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); - // // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // // println!("Received (bi) '{str_data}' from client"); - - // // stream.0.write_all(b"ACK").await.unwrap(); - // // }, - // // Err(e) => { - // // break ; - // // } - // // }; - - // // } - // // stream = connection.accept_uni() => { - // // // let mut stream = stream; - // // match stream { - // // Ok(mut stream) => { - // // println!("Accepted UNI stream"); - // // let bytes_read = match stream.read(&mut buffer).await.unwrap() { - // // Some(bytes_read) => bytes_read, - // // None => continue, - // // }; - - // // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // // println!("Received (uni) '{str_data}' from client"); - - // // let mut stream = connection.open_uni().await.unwrap().await.unwrap(); - // // stream.write_all(b"ACK").await.unwrap(); - // // }, - // // _ => { - // // // println!("Error accepting UNI stream: {:?}", e); - // // break ; - // // } - // // } - - // // } - // dgram = connection.receive_datagram() => { - // match dgram { - // Ok(dgram) => { - // sender.send_async(dgram).await.unwrap(); - // // println!("Received datagram from client"); - // // let str_data = std::str::from_utf8(&dgram).unwrap(); - // // println!("Received (dgram) '{str_data}' from client"); - // // connection.send_datagram(b"ACK").unwrap(); - // }, - // _ => { - // // break; - // break ; - // } - // } - // } - // } - // } - // }).detach(); - // println!("Waiting for session request..."); - // executor::spawn(async move { - // let receiver = self.conn_ch_receiver.as_mut().unwrap(); - // let next = { - // let to_client_receiver_next = receiver.recv_async().fuse(); - // let session_request = incoming_session.fuse(); - - // pin_mut!(to_client_receiver_next); - // pin_mut!(session_request); - // select! { - // from_client_result = session_request => { - // let sess_req = from_client_result.unwrap(); - // println!("Session request received {}", sess_req.authority()); - // Next::NewSessionRequest(sess_req) - // } - // } - // }; - // match next { - // Next::NewSessionRequest(sessreq) => { - // println!("Received datagram from client: {:?}", sessreq.authority()); - // } - - // } - // }).detach(); -} \ No newline at end of file diff --git a/src/oldlib.rs.old b/src/oldlib.rs.old deleted file mode 100644 index 171fe6a..0000000 --- a/src/oldlib.rs.old +++ /dev/null @@ -1,287 +0,0 @@ -use client::ClientConn; -use flume::Receiver; -use flume::Sender; -use futures_util::{pin_mut, select, FutureExt}; -use std::time::Duration; -use tokio::runtime::Runtime; -use wtransport::datagram::Datagram; -use wtransport::endpoint; - -use wtransport::endpoint::SessionRequest; -use wtransport::tls::Certificate; -use wtransport::Connection; -use wtransport::Endpoint; -use wtransport::ServerConfig; - -mod client; -mod executor; - -#[repr(C)] -pub struct ErrorMessage { - pub code: i32, - pub message: String, -} - -pub struct WebTransport { - /// The innter Server object - pub server: Option>, - // - conn_ch_sender: Option>, - conn_ch_receiver: Option>, - pub state: Option, -} -static mut SEND_FN: Option = None; -enum Next { - NewSessionRequest(Result), -} -impl WebTransport { - pub(crate) fn new( - port: u16, - sender_fn: Option, - _runtime: &mut Runtime, - ) -> Result { - unsafe { - SEND_FN = sender_fn; - }; - let _guard = _runtime.enter(); - - let config = ServerConfig::builder() - .with_bind_default(port) - .with_certificate(Certificate::load("cert.crt", "cert.key").unwrap()) - .keep_alive_interval(Some(Duration::from_secs(1))) - .build(); - - let (conn_sender, conn_reciever) = flume::unbounded(); - // let (datagram_sender, datagram_receiver) = flume::unbounded(); - - let server = match Endpoint::server(config) { - Ok(server) => server, - Err(e) => { - println!("Error creating server: {:?}", e); - return Err(1); - } - }; - // - // self.handle_sess_in(_runtime); - // - Ok(Self { - server: Some(server), - state: Some(true), - conn_ch_sender: Some(conn_sender), - conn_ch_receiver: Some(conn_reciever), - // datagram_ch_sender: Some(datagram_sender), - // datagram_ch_receiver: Some(datagram_receiver), - }) - } - - pub(crate) unsafe fn handle_sess_in(&'static mut self, runtime: *mut Runtime) { - println!("Waiting for session request..."); - let rt = runtime.as_mut().unwrap(); - rt.spawn(async move { - loop { - // let receiver = self.conn_ch_receiver.as_mut().unwrap(); - let sender = self.conn_ch_sender.as_ref().unwrap(); - let next = { - // let insess_receiver = receiver.recv_async().fuse(); - let incoming_session = self.server.as_mut().unwrap().accept().fuse(); - - // pin_mut!(sender); - pin_mut!(incoming_session); - select! { - result = incoming_session => { - let sess_req = result.await; - Next::NewSessionRequest(sess_req) - } - } - }; - match next { - Next::NewSessionRequest(sessreq) => { - match sessreq { - Ok(sessreq) => { - println!( - "Received Session Request from client: {:?}", - sessreq.authority() - ); - // self.handle_session_impl(sessreq.accept().await); - let conn = sessreq.accept().await.unwrap(); - println!( - "Accepted session request from client: {:?}", - conn.remote_address() - ); - - let _ = sender.send_async(conn).await; - // self.conn_ch_sender.as_mut().unwrap().send(conn).unwrap(); - } - Err(e) => { - println!("Error accepting session request: {:?}", e); - } - } - // insess_sender.send_async(sessreq).await.unwrap(); - } - } - } - }); - } -} - -#[no_mangle] -pub extern "C" fn init_runtime() -> *mut Runtime { - Box::into_raw(Box::new(Runtime::new().unwrap())) -} - -#[no_mangle] -pub unsafe extern "C" fn start( - send_func: Option, - res: *mut u32, - rt_ptr: *mut Runtime, -) -> *mut WebTransport { - assert!(!rt_ptr.is_null()); - assert!(!res.is_null()); - let _runtime = &mut *rt_ptr; - let server = WebTransport::new(4433, send_func, _runtime); - match server { - Ok(server) => { - let server_ptr = Box::into_raw(Box::new(server)); - *res = 0; - server_ptr - } - Err(_) => { - *res = 2; - std::ptr::null_mut() - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn handle_session(server_ptr: *mut WebTransport, runtime: *mut Runtime) { - assert!(!server_ptr.is_null()); - assert!(!runtime.is_null()); - let server = &mut *server_ptr; - let _runtime = &mut *runtime; - server.handle_sess_in(_runtime); -} - -#[no_mangle] -pub unsafe extern "C" fn proc_rec(srv: *mut WebTransport) -> *mut ClientConn { - assert!(!srv.is_null()); - - let server = &mut *srv; - println!("DBG: RECEIVER READY"); - - match server.conn_ch_receiver.as_ref().unwrap().recv() { - Ok(conn) => { - println!("New client : {:?}", conn.remote_address()); - let connptr = Box::into_raw(Box::new(conn)); - let client_conn = ClientConn::new(connptr); - let clientptr = Box::into_raw(Box::new(client_conn)); - clientptr - } - _ => { - panic!("Error receiving connection"); - } - } -} - -//create a method that creates a unbound channel and returns a pointer to the sender/receiver pair to the FFI - -#[no_mangle] -pub unsafe extern "C" fn proc_rec_streams(client: *mut ClientConn) { - assert!(!client.is_null()); - let client = &mut *client; - let connection = client.get_conn(); - let sender = client.datagram_ch_sender.as_ref().unwrap(); - - println!("CONN RECEIVER PROC SET & READY"); - executor::spawn(async move { - let mut buffer = vec![0; 65536].into_boxed_slice(); - - loop { - // let sender = sender; - println!("Waiting for datagram from client"); - tokio::select! { - stream = connection.accept_bi() => { - match stream { - Ok(mut stream) => { - - println!("Accepted BI stream"); - let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); - let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - println!("Received (bi) '{str_data}' from client"); - - stream.0.write_all(b"ACK").await.unwrap(); - }, - Err(e) => { - break ; - } - }; - - } - stream = connection.accept_uni() => { - // let mut stream = stream; - match stream { - Ok(mut stream) => { - println!("Accepted UNI stream"); - let bytes_read = match stream.read(&mut buffer).await.unwrap() { - Some(bytes_read) => bytes_read, - None => continue, - }; - - let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - println!("Received (uni) '{str_data}' from client"); - - let mut stream = connection.open_uni().await.unwrap().await.unwrap(); - stream.write_all(b"ACK").await.unwrap(); - }, - _ => { - // println!("Error accepting UNI stream: {:?}", e); - break ; - } - } - - } - dgram = connection.receive_datagram() => { - match dgram { - Ok(dgram) => { - - println!("Received datagram from client"); - sender.send(dgram).unwrap(); - // let str_data = std::str::from_utf8(&dgram).unwrap(); - // println!("Received (dgram) '{str_data}' from client"); - connection.send_datagram(b"ACK").unwrap(); - }, - _ => { - // break; - println!("Error receiving datagram"); - - } - } - }, - - } - } - }) - .detach(); -} - -#[no_mangle] -pub unsafe extern "C" fn proc_recv_ch_datagram(client: *mut ClientConn, buff: *mut u8) -> usize { - assert!(!client.is_null()); - let client = &mut *client; - - match client.datagram_ch_receiver.as_ref().unwrap().recv() { - Ok(dgram) => { - ::std::slice::from_raw_parts_mut(buff, dgram.len()).copy_from_slice(&dgram); - dgram.len() - } - Err(_) => 0, - } -} - -#[no_mangle] -pub unsafe extern "C" fn test_proc(client: *mut ClientConn) { - assert!(!client.is_null()); - let client = &mut *client; - println!("TEST PROC {} ", client.get_conn().remote_address()); -} From a4e926fc47981d79e3dd94607d6024c85fae144d Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sat, 16 Sep 2023 01:53:32 +0200 Subject: [PATCH 03/72] Working demo recv,send --- examples/index.html | 49 +++++++++++++++++---------------------------- mod/deno.ts | 46 +++++++++++++++++++++++++++++++----------- src/lib.rs | 47 +++++++++++++++++++++++++------------------ 3 files changed, 79 insertions(+), 63 deletions(-) diff --git a/examples/index.html b/examples/index.html index e353e17..06fab06 100644 --- a/examples/index.html +++ b/examples/index.html @@ -16,25 +16,16 @@ if (done) { break; } - // value is a Uint8Array. - console.log(value); + // // value is a Uint8Array. + // console.log(value); } } - async function writeData(writable) { - const writer = writable.getWriter(); - await writer.ready; - const data1 = new Uint8Array([65, 66, 67]); - const data2 = new Uint8Array([68, 69, 70]); - writer.write(data1); - await writer.ready; - console.log("data1"); - await writer.write(data2); - } window.serveurDEMO = null; window.writeDDBN = null; - const webtransport = new WebTransport('https://127.0.0.1:12345/unidirectional', { - congestionControl: "throughput", + const webtransport = new WebTransport('https://localhost:4433/', { + congestionControl: "low-latency", + "requireUnreliable": true, "serverCertificateHashes": [{ "algorithm": "sha-256", "value": new Uint8Array([0x7c, 0xda, 0x57, 0x35, 0xc8, 0xe1, 0x4c, 0x53, 0x2b, 0x9a, 0xd6, 0xf7, 0x6e, 0xe6, 0x54, 0x54, 0x39, 0xa8, 0x6a, 0xa3, 0xd7, 0xd7, 0xc6, 0x3, 0x93, 0xf7, 0xad, 0x7a, 0xc3, 0xcc, 0x32, 0x98]) @@ -49,32 +40,28 @@ // const stream = await webtransport.createBidirectionalStream(); const writable = await webtransport.datagrams.writable; const readable = await webtransport.datagrams.readable; - // window.writeDDBN = webtransport.datagrams.writable.getWriter(); + window.writeDDBN = await webtransport.datagrams.writable.getWriter(); // const readable = streams.readable.getReader(); //send Hello world // await writeable.write(new TextEncoder().encode("Hello world")); // while(1) { + readData(readable); let int2 = 0; - performance.mark('start'); + const encoder = new TextEncoder(); + console.log("start sending") + performance.mark('start-sending'); // while(1) { - // if (int2 === 1000) { + // if (int2 === 50000) { + // performance.mark('start-sending'); + // performance.measure('result-sending', 'start-sending', 'start-sending'); + // const measure = performance.getEntriesByName('result-sending')[0]; + // //get the duration in seconds + // const duration = measure.duration / 1000; + // console.log("duration : ", duration) // break ; // } - // window.writeDDBN.write(new TextEncoder().encode(int2++)).then(() => { - // console.log("writeable"); - // }); - readData(readable); - writeData(writable); - - //send Hello world - // await new Promise(resolve => setTimeout(resolve, 1000)); + // window.writeDDBN.write(encoder.encode(int2++)); // } - performance.mark('end'); - performance.measure('test', 'start', 'end'); - const measure = performance.getEntriesByName('test')[0]; - //get the duration in seconds with millisecond precision - const duration = measure.duration / 1000; - console.log(duration) } catch (e) { console.error(e); diff --git a/mod/deno.ts b/mod/deno.ts index 3e678ba..207b469 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -35,10 +35,11 @@ const sender = new Deno.UnsafeCallback( sender.ref(); let serverPTR; +const decoder = new TextDecoder(); try { const certpath = encodeBuffPtr("./certs/cert.crt"); const keypath = encodeBuffPtr("./certs/cert.key"); - serverPTR = lib.symbols.proc_init( sender.pointer, 12345 ,true, 20, 100, certpath[0], certpath[1], keypath[0], keypath[1]); + serverPTR = lib.symbols.proc_init( sender.pointer, 4433 ,true, 0, 100, certpath[0], certpath[1], keypath[0], keypath[1]); const new_connection = new Deno.UnsafeCallback( { parameters: [ "pointer" ], @@ -47,21 +48,42 @@ try { (client) => { console.log("DENO : New connection"); lib.symbols.proc_init_client_streams(serverPTR!, client, new Uint32Array(1)); - const encodedmesg = encodeBuffPtr("Hello from deno"); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + + // const encodedmesg = encodeBuffPtr("Hello from deno"); + // lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); + const buffer = new Uint8Array(65536); + Promise.all([(async () => { + console.log("DENO : Reading Datagrams"); + let res = await lib.symbols.proc_recv_datagram(serverPTR!, client, buffer); + performance.clearMarks("start"); + performance.clearMarks("end"); + performance.clearMeasures("DENO"); + while (res > 0) { + const ress = buffer.subarray(0, res as number); + console.log(ress) + const msg = parseInt(decoder.decode(ress)); + if (msg === 0) { + console.log("DENO: Start timing"); + performance.mark("start"); + } + if (msg === 49999) { + console.log("DENO: End timing"); + performance.mark("end"); + performance.measure("DENO", "start", "end"); + const measure = performance.getEntriesByName("DENO")[0]; + console.log("Last message : " + msg); + console.log(measure); + + } + lib.symbols.proc_send_datagram(serverPTR!, client, ress, res); + res = await lib.symbols.proc_recv_datagram(serverPTR!, client, buffer); + } + })()]); }, ); new_connection.ref(); lib.symbols.proc_listen(serverPTR, new_connection.pointer); + console.info("Server is running"); } catch (e) { sender.unref(); console.error(e); diff --git a/src/lib.rs b/src/lib.rs index 4083609..f124a83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,16 +57,16 @@ impl WebTransport { pub(crate) unsafe fn handle_sess_in(&'static mut self) { - let handle = RUNTIME.handle(); + // let handle = RUNTIME.handle(); executor::spawn(async move { - println!("Started thread"); + // println!("Started thread"); loop { let incoming_session = self.server.as_mut().unwrap().accept().await; - handle.spawn(async move { + RUNTIME.spawn(async move { let _buffer = vec![0; 65536].into_boxed_slice(); - println!("DBG: Waiting for session request..."); + // println!("DBG: Waiting for session request..."); let session_request = incoming_session.await; let accepted_session = match session_request { Ok(session_request) => session_request, @@ -76,14 +76,14 @@ impl WebTransport { return ; } }; - println!( - "DBG: New session: Authority: '{}', Path: '{}'", - accepted_session.authority(), - accepted_session.path() - ); + // println!( + // "DBG: New session: Authority: '{}', Path: '{}'", + // accepted_session.authority(), + // accepted_session.path() + // ); match accepted_session.accept().await { Ok(conn) => { - println!("DBG: Sending connection to channel."); + // println!("DBG: Sending connection to channel."); let client = ClientConn::new(conn); let client_ptr = Box::into_raw(Box::new(client)); assert!(!CONN_FN.is_none()); //TODO(hironichu): Handle this better. @@ -122,11 +122,13 @@ pub unsafe extern "C" fn proc_init( let key_path = Path::new(std::str::from_utf8(key_path).unwrap()); let certificates = Certificate::load(cert_path, key_path).unwrap(); + + let keepalive = if keepalive == 0 { None } else { Some(Duration::from_secs(keepalive)) }; //print the paths for debug let config = ServerConfig::builder() .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) .with_certificate(certificates) - .keep_alive_interval(Some(Duration::from_secs(keepalive))) + .keep_alive_interval(keepalive) .max_idle_timeout(Some(Duration::from_secs(timeout))).unwrap() .allow_migration(migration) .build(); @@ -178,7 +180,7 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client let sender = client.datagram_ch_sender.clone(); - println!("DBG: CONN RECEIVER PROC SET & READY"); + // println!("DBG: CONN RECEIVER PROC SET & READY"); // let rthandle = RUNTIME.handle(); // let mut buffer =::std::slice::from_raw_parts_mut(buffer, 65536); @@ -227,9 +229,9 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client stream = client.conn.receive_datagram() => { match stream { Ok(dgram) => { - let _ = sender.send_async(dgram).await; - //TODO(hironichu): Remove this debug line - client.conn.send_datagram(b"ACK").unwrap(); + let _ = sender.send(dgram); + // //TODO(hironichu): Remove this debug line + // client.conn.send_datagram(b"ACK").unwrap(); }, _ => {} } @@ -241,11 +243,11 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client } #[no_mangle] -pub unsafe extern "C" fn proc_recv_datagram(srv: *mut WebTransport, clientptr: *mut ClientConn, buff: *mut u8) -> usize { - assert!(!clientptr.is_null()); +pub unsafe extern "C" fn proc_recv_datagram(srv: *mut WebTransport, client_ptr: *mut ClientConn, buff: *mut u8) -> usize { + assert!(!client_ptr.is_null()); assert!(!srv.is_null()); - let client = &mut *clientptr; + let client = &mut *client_ptr; let _server = &mut *srv; match client.datagram_ch_receiver.recv() { @@ -265,8 +267,13 @@ pub unsafe extern "C" fn proc_send_datagram(srv: *mut WebTransport, clientptr: * let client = &mut *clientptr; let _server = &mut *srv; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); - - client.conn.send_datagram(buf).unwrap(); //TODO: Handle error + match client.conn.send_datagram(buf) { + Ok(_) => {}, + Err(e) => { + //TODO: Handle error better + println!("Error sending datagram: {:?}", e); + } + } } From 12ec1fa8c3652fd4d248234a516af68278925616 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sat, 16 Sep 2023 02:16:15 +0200 Subject: [PATCH 04/72] Starting bench and fixes --- examples/index.html | 24 ++++++------ mod/deno.ts | 32 +++++++-------- src/lib.rs | 95 ++++++++++++++++++++++++--------------------- 3 files changed, 79 insertions(+), 72 deletions(-) diff --git a/examples/index.html b/examples/index.html index 06fab06..4d8530f 100644 --- a/examples/index.html +++ b/examples/index.html @@ -50,18 +50,18 @@ const encoder = new TextEncoder(); console.log("start sending") performance.mark('start-sending'); - // while(1) { - // if (int2 === 50000) { - // performance.mark('start-sending'); - // performance.measure('result-sending', 'start-sending', 'start-sending'); - // const measure = performance.getEntriesByName('result-sending')[0]; - // //get the duration in seconds - // const duration = measure.duration / 1000; - // console.log("duration : ", duration) - // break ; - // } - // window.writeDDBN.write(encoder.encode(int2++)); - // } + while(1) { + if (int2 === 50000) { + performance.mark('start-sending'); + performance.measure('result-sending', 'start-sending', 'start-sending'); + const measure = performance.getEntriesByName('result-sending')[0]; + //get the duration in seconds + const duration = measure.duration / 1000; + console.log("duration : ", duration) + break ; + } + window.writeDDBN.write(encoder.encode(int2++)); + } } catch (e) { console.error(e); diff --git a/mod/deno.ts b/mod/deno.ts index 207b469..7158fcc 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -60,22 +60,22 @@ try { performance.clearMeasures("DENO"); while (res > 0) { const ress = buffer.subarray(0, res as number); - console.log(ress) - const msg = parseInt(decoder.decode(ress)); - if (msg === 0) { - console.log("DENO: Start timing"); - performance.mark("start"); - } - if (msg === 49999) { - console.log("DENO: End timing"); - performance.mark("end"); - performance.measure("DENO", "start", "end"); - const measure = performance.getEntriesByName("DENO")[0]; - console.log("Last message : " + msg); - console.log(measure); - - } - lib.symbols.proc_send_datagram(serverPTR!, client, ress, res); + // console.log(ress) + // const msg = parseInt(decoder.decode(ress)); + // if (msg === 0) { + // console.log("DENO: Start timing"); + // performance.mark("start"); + // } + // if (msg === 49999) { + // console.log("DENO: End timing"); + // performance.mark("end"); + // performance.measure("DENO", "start", "end"); + // const measure = performance.getEntriesByName("DENO")[0]; + // console.log("Last message : " + msg); + // console.log(measure); + // } + console.log("MSG : " +decoder.decode(ress)); + // lib.symbols.proc_send_datagram(serverPTR!, client, ress, res); res = await lib.symbols.proc_recv_datagram(serverPTR!, client, buffer); } })()]); diff --git a/src/lib.rs b/src/lib.rs index f124a83..20dd781 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,12 +59,12 @@ impl WebTransport { // let handle = RUNTIME.handle(); - executor::spawn(async move { + RUNTIME.spawn(async move { // println!("Started thread"); loop { let incoming_session = self.server.as_mut().unwrap().accept().await; - RUNTIME.spawn(async move { + // RUNTIME.spawn(async move { let _buffer = vec![0; 65536].into_boxed_slice(); // println!("DBG: Waiting for session request..."); let session_request = incoming_session.await; @@ -94,9 +94,9 @@ impl WebTransport { } } - }); + // }); } - }).detach(); + });//.detach(); } } @@ -184,62 +184,69 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client // let rthandle = RUNTIME.handle(); // let mut buffer =::std::slice::from_raw_parts_mut(buffer, 65536); - executor::spawn(async move { + RUNTIME.spawn(async move { let mut buffer = vec![0; 65536].into_boxed_slice(); //use the buffer from the args and set it to a box slice - let _ = RUNTIME.enter(); + // let _ = RUNTIME.enter(); loop { tokio::select! { - stream = client.conn.accept_bi() => { - match stream { - Ok(mut stream) => { - - println!("Accepted BI stream"); - let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); - let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - println!("Received (bi) '{str_data}' from client"); - - stream.0.write_all(b"ACK").await.unwrap(); - }, - _ => {} - }; - - } - stream = client.conn.accept_uni() => { - match stream { - Ok(mut stream) => { - println!("Accepted UNI stream"); - let bytes_read = match stream.read(&mut buffer).await.unwrap() { - Some(bytes_read) => bytes_read, - None => continue, - }; - - let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - println!("Received (uni) '{str_data}' from client"); - - let mut stream = client.conn.open_uni().await.unwrap().await.unwrap(); - stream.write_all(b"ACK").await.unwrap(); - }, - _ => {} - } - - } + // stream = client.conn.accept_bi() => { + // match stream { + // Ok(mut stream) => { + + // println!("Accepted BI stream"); + // let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); + // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); + + // println!("Received (bi) '{str_data}' from client"); + + // stream.0.write_all(b"ACK").await.unwrap(); + // }, + // _ => { + // client.conn.closed().await; + // } + // }; + + // } + // stream = client.conn.accept_uni() => { + // match stream { + // Ok(mut stream) => { + // println!("Accepted UNI stream"); + // let bytes_read = match stream.read(&mut buffer).await.unwrap() { + // Some(bytes_read) => bytes_read, + // None => continue, + // }; + + // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); + + // println!("Received (uni) '{str_data}' from client"); + + // let mut stream = client.conn.open_uni().await.unwrap().await.unwrap(); + // stream.write_all(b"ACK").await.unwrap(); + // }, + // _ => { + // client.conn.closed().await; + // } + // } + + // } stream = client.conn.receive_datagram() => { match stream { Ok(dgram) => { + println!("Received datagram"); let _ = sender.send(dgram); // //TODO(hironichu): Remove this debug line // client.conn.send_datagram(b"ACK").unwrap(); }, - _ => {} + _ => { + break; + } } }, } } - }).detach(); + });//.detach(); } #[no_mangle] From 916f07e0a5065c45da4d2f679d2ac8f5c143b2ab Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sat, 16 Sep 2023 17:23:09 +0200 Subject: [PATCH 05/72] Fixes and speed --- examples/index.html | 11 +++-- examples/web_server.js | 10 ++++- mod/crypto.ts | 8 +--- mod/deno.ts | 91 +++++++++++------------------------------- mod/lib.ts | 4 +- src/certificate.rs | 17 ++++++-- src/client.rs | 2 + src/executor.rs | 2 +- src/lib.rs | 38 ++++++++---------- 9 files changed, 75 insertions(+), 108 deletions(-) diff --git a/examples/index.html b/examples/index.html index 4d8530f..ea693f7 100644 --- a/examples/index.html +++ b/examples/index.html @@ -24,11 +24,10 @@ window.serveurDEMO = null; window.writeDDBN = null; const webtransport = new WebTransport('https://localhost:4433/', { - congestionControl: "low-latency", - "requireUnreliable": true, - "serverCertificateHashes": [{ - "algorithm": "sha-256", - "value": new Uint8Array([0x7c, 0xda, 0x57, 0x35, 0xc8, 0xe1, 0x4c, 0x53, 0x2b, 0x9a, 0xd6, 0xf7, 0x6e, 0xe6, 0x54, 0x54, 0x39, 0xa8, 0x6a, 0xa3, 0xd7, 0xd7, 0xc6, 0x3, 0x93, 0xf7, 0xad, 0x7a, 0xc3, 0xcc, 0x32, 0x98]) + congestionControl: "throughput", + serverCertificateHashes: [{ + algorithm: "sha-256", + value: new Uint8Array(CERT_HASH) }] }) await webtransport.ready; @@ -51,7 +50,7 @@ console.log("start sending") performance.mark('start-sending'); while(1) { - if (int2 === 50000) { + if (int2 === 100_000) { performance.mark('start-sending'); performance.measure('result-sending', 'start-sending', 'start-sending'); const measure = performance.getEntriesByName('result-sending')[0]; diff --git a/examples/web_server.js b/examples/web_server.js index f01b98f..6489561 100644 --- a/examples/web_server.js +++ b/examples/web_server.js @@ -1,6 +1,14 @@ +import { readCertFile } from "../mod/crypto.ts"; + +const certFile = readCertFile("./certs/cert.pem"); +const certHash = Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256', certFile))); + +let indexHTML =Deno.readTextFileSync('./examples/index.html'); + +indexHTML = indexHTML.replace('CERT_HASH', "[" + certHash + "]"); Deno.serve((_) => { - return new Response(Deno.readTextFileSync('./examples/index.html'), { + return new Response(indexHTML, { headers: { 'content-type': 'text/html' } diff --git a/mod/crypto.ts b/mod/crypto.ts index 5521354..fd8a10c 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -5,15 +5,9 @@ export function base64ToArrayBuffer(base64: string) { for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } - return bytes.buffer; + return bytes; } -export async function calculateSHA256(buffer: Uint8Array) { - const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); - return hashHex; -} export function readCertFile(certpath: string) { try { diff --git a/mod/deno.ts b/mod/deno.ts index 7158fcc..fa95d27 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -1,7 +1,5 @@ -import { calculateSHA256, readCertFile } from "./crypto.ts"; -import { PackedStruct, u32, u8, NullTerminatedString } from "https://deno.land/x/byte_type@0.2.2/mod.ts"; - import { LIB } from "./lib.ts"; + const lib = LIB; export function encode(v: string | Uint8Array): Uint8Array { if (typeof v !== "string") return v; @@ -37,48 +35,38 @@ sender.ref(); let serverPTR; const decoder = new TextDecoder(); try { - const certpath = encodeBuffPtr("./certs/cert.crt"); - const keypath = encodeBuffPtr("./certs/cert.key"); + const certpath = encodeBuffPtr("./certs/cert.pem"); + const keypath = encodeBuffPtr("./certs/key.pem"); + serverPTR = lib.symbols.proc_init( sender.pointer, 4433 ,true, 0, 100, certpath[0], certpath[1], keypath[0], keypath[1]); const new_connection = new Deno.UnsafeCallback( { parameters: [ "pointer" ], result: "void", }, - (client) => { + async (client) => { console.log("DENO : New connection"); - lib.symbols.proc_init_client_streams(serverPTR!, client, new Uint32Array(1)); + // const DBuffer = new Uint8Array(65536); + const DBufferB = new Uint8Array(65536); + //fill the buffer with 0 + DBufferB.fill(0); + lib.symbols.proc_init_client_streams(serverPTR!, client, DBufferB, DBufferB.byteLength); + const StreamBuffer = new ReadableStream({ - // const encodedmesg = encodeBuffPtr("Hello from deno"); - // lib.symbols.proc_send_datagram(serverPTR!, client, encodedmesg[0], encodedmesg[1]); - const buffer = new Uint8Array(65536); - Promise.all([(async () => { - console.log("DENO : Reading Datagrams"); - let res = await lib.symbols.proc_recv_datagram(serverPTR!, client, buffer); - performance.clearMarks("start"); - performance.clearMarks("end"); - performance.clearMeasures("DENO"); - while (res > 0) { - const ress = buffer.subarray(0, res as number); - // console.log(ress) - // const msg = parseInt(decoder.decode(ress)); - // if (msg === 0) { - // console.log("DENO: Start timing"); - // performance.mark("start"); - // } - // if (msg === 49999) { - // console.log("DENO: End timing"); - // performance.mark("end"); - // performance.measure("DENO", "start", "end"); - // const measure = performance.getEntriesByName("DENO")[0]; - // console.log("Last message : " + msg); - // console.log(measure); - // } - console.log("MSG : " +decoder.decode(ress)); - // lib.symbols.proc_send_datagram(serverPTR!, client, ress, res); - res = await lib.symbols.proc_recv_datagram(serverPTR!, client, buffer); + async pull(controller) { + const nread = await lib.symbols.proc_recv_datagram(serverPTR!, client); + if (nread > 0) { + controller.enqueue(DBufferB.slice(0, nread as number)); + } + }, + cancel(_) { + console.error("[Error] Stream cancelled"); } - })()]); + }); + for await (const chunk of StreamBuffer) { + const msg = parseInt(decoder.decode(chunk)); + console.log(msg); + } }, ); new_connection.ref(); @@ -90,36 +78,5 @@ try { } if (!serverPTR) throw new Error("Server is not running"); -// - -// Promise.all([(async () => { -// let client = await lib.symbols.proc_rec(resptr); - -// while(client !== null) { -// console.log("New connection"); -// await lib.symbols.proc_rec_streams(resptr, runtime, client) -// // //start a new thread to handle the connection -// // lib.symbols.proc_rec_streams(resptr, client); -// // // -// // console.log("Connection handled"); - -// // // let mut buffer = vec![0; 65536].into_boxed_slice(); -// Promise.all([(async () => { -// let buffer = new Uint8Array(65536); -// let res = await lib.symbols.proc_recv_ch_datagram(resptr, client, buffer); -// while (res > 0) { -// const ress = buffer.subarray(0, res as number); -// console.log(ress); -// buffer = buffer.fill(0); -// res = await lib.symbols.proc_recv_ch_datagram(resptr, client, buffer); -// } -// })()]); - -// // // const buffview = new Deno.UnsafePointerView(res!); -// // // console.log(buffview.getBigInt64(0)); -// client = await lib.symbols.proc_rec(resptr); -// } -// })()]); - diff --git a/mod/lib.ts b/mod/lib.ts index e049855..d4d721a 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -15,12 +15,12 @@ const symbols = { // result: "void", // }, proc_init_client_streams: { - parameters: ["pointer", "pointer", "buffer"], + parameters: ["pointer", "pointer", "buffer", "usize"], result: "void", nonblocking: true, }, proc_recv_datagram: { - parameters: ["pointer", "pointer", "buffer"], + parameters: ["pointer", "pointer"], result: "usize", nonblocking: true, }, diff --git a/src/certificate.rs b/src/certificate.rs index dc9fdf4..fabc33f 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -1,9 +1,12 @@ use anyhow::Result; use base64::engine::general_purpose::STANDARD as Base64Engine; use base64::Engine; +use rcgen::BasicConstraints; use rcgen::CertificateParams; use rcgen::DistinguishedName; use rcgen::DnType; +use rcgen::ExtendedKeyUsagePurpose; +use rcgen::IsCa; use rcgen::KeyPair; use rcgen::PKCS_ECDSA_P256_SHA256; use ring::digest::digest; @@ -37,11 +40,18 @@ pub fn generate_certificate>(common_name: S) -> Result>(common_name: S) -> Result, pub datagram_ch_sender: Sender, pub datagram_ch_receiver: Receiver, } @@ -14,6 +15,7 @@ impl ClientConn { let (sender, receiver) = flume::unbounded(); Self { conn, + buffer: None, datagram_ch_sender: sender, datagram_ch_receiver: receiver, } diff --git a/src/executor.rs b/src/executor.rs index 9659362..f8f3667 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,7 +5,7 @@ use std::{future::Future, thread}; // Runs the given future on the global executor, spawning a new thread if necessary. -pub fn spawn(future: impl Future + Send + 'static) -> Task { +pub fn _spawn(future: impl Future + Send + 'static) -> Task { static GLOBAL: Lazy> = Lazy::new(|| { for n in 1..=num_cpus::get() { thread::Builder::new() diff --git a/src/lib.rs b/src/lib.rs index 20dd781..e64c1c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,7 +171,7 @@ pub unsafe extern "C" fn proc_listen(server_ptr: *mut WebTransport, cb: Option { + _ = client.conn.accept_bi() => { // match stream { // Ok(mut stream) => { @@ -206,9 +202,11 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client // client.conn.closed().await; // } // }; - - // } - // stream = client.conn.accept_uni() => { + client.conn.closed().await; + } + _ = client.conn.accept_uni() => { + //close the connection until we implement the uni stream + client.conn.closed().await; // match stream { // Ok(mut stream) => { // println!("Accepted UNI stream"); @@ -229,17 +227,15 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client // } // } - // } + } stream = client.conn.receive_datagram() => { match stream { + //write dgram into denoBUF and send the copied bytes len to deno Ok(dgram) => { - println!("Received datagram"); - let _ = sender.send(dgram); - // //TODO(hironichu): Remove this debug line - // client.conn.send_datagram(b"ACK").unwrap(); - }, + sender.send(dgram).unwrap(); + }, _ => { - break; + continue; } } }, @@ -250,7 +246,7 @@ pub unsafe extern "C" fn proc_init_client_streams(srv: *mut WebTransport, client } #[no_mangle] -pub unsafe extern "C" fn proc_recv_datagram(srv: *mut WebTransport, client_ptr: *mut ClientConn, buff: *mut u8) -> usize { +pub unsafe extern "C" fn proc_recv_datagram(srv: *mut WebTransport, client_ptr: *mut ClientConn) -> usize { assert!(!client_ptr.is_null()); assert!(!srv.is_null()); @@ -259,8 +255,8 @@ pub unsafe extern "C" fn proc_recv_datagram(srv: *mut WebTransport, client_ptr: match client.datagram_ch_receiver.recv() { Ok(dgram) => { - ::std::slice::from_raw_parts_mut(buff, dgram.len()).copy_from_slice(&dgram); - dgram.len() + // ::std::slice::from_raw_parts_mut(client.buffer.as_mut().unwrap().as_mut_ptr(), dgram.len()).clone_from_slice(&dgram); + dgram.len() } Err(_) => 0, } From 18bbd329fe6b461bf88015d4b6e5b688b41de468 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sat, 16 Sep 2023 21:56:12 +0200 Subject: [PATCH 06/72] Cleaning architecture for client integration --- .vscode/settings.json | 5 +- Cargo.toml | 7 +- examples/index.html | 3 +- mod/deno.ts | 31 ++-- mod/lib.ts | 24 ++-- src/certificate.rs | 23 +-- src/client.rs | 88 +++++++++--- src/connection.rs | 23 +++ src/executor.rs | 7 +- src/lib.rs | 324 ++---------------------------------------- src/server.rs | 216 ++++++++++++++++++++++++++++ src/shared.rs | 43 ++++++ 12 files changed, 416 insertions(+), 378 deletions(-) create mode 100644 src/connection.rs create mode 100644 src/server.rs create mode 100644 src/shared.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 010b04f..6957ab4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,8 @@ "deno.enable": true, "deno.lint": true, "deno.unstable": true, - "deno.cacheOnSave": true + "deno.cacheOnSave": true, + "rust-analyzer.linkedProjects": [ + ".\\Cargo.toml" + ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index b21d41f..51dad59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,13 +10,12 @@ crate-type = ["cdylib"] [dependencies] flume = "0.11.0" -futures-util = "0.3.28" num_cpus = "1.16.0" once_cell = "1.18.0" smol = "1.3.0" -tokio = { version = "1.28.1", features = ["rt", "rt-multi-thread", "macros"] } -wtransport = "0.1.4" -serde = { version = "1", features = ["derive"] } +tokio = { version = "1.32.0", features = ["rt", "rt-multi-thread", "macros"] } +# wtransport = "0.1.4" +wtransport = { git = "https://github.com/BiagioFesta/wtransport", branch = "master" } base64 = "0.21.4" rcgen = "0.11.1" ring = "0.16.20" diff --git a/examples/index.html b/examples/index.html index ea693f7..8131bf3 100644 --- a/examples/index.html +++ b/examples/index.html @@ -9,6 +9,7 @@ diff --git a/examples/web_server/web.js b/examples/web_server/web.js index 9e11d54..678cd51 100644 --- a/examples/web_server/web.js +++ b/examples/web_server/web.js @@ -1,4 +1,6 @@ import { readCertFile } from "../../mod/crypto.ts"; +//grab the first argument as the IP to connect to (defaults to localhost) +const ip = Deno.args[0] || "localhost"; const certFile = readCertFile("./certs/cert.pem"); const certHash = Array.from( @@ -7,7 +9,10 @@ const certHash = Array.from( let indexHTML = Deno.readTextFileSync("./examples/web_server/index.html"); -indexHTML = indexHTML.replace("CERT_HASH", "[" + certHash + "]"); +indexHTML = indexHTML.replace("CERT_HASH", "[" + certHash + "]").replaceAll( + "HOST_IP", + ip, +); Deno.serve((_) => { return new Response(indexHTML, { diff --git a/mod/deno.ts b/mod/deno.ts index 75905a0..140cc7e 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -34,7 +34,7 @@ const sender = new Deno.UnsafeCallback( sender.ref(); let serverPTR; let new_connection; -const decoder = new TextDecoder(); +const _decoder = new TextDecoder(); try { const certpath = encodeBuffPtr("./certs/cert.pem"); const keypath = encodeBuffPtr("./certs/key.pem"); @@ -43,8 +43,8 @@ try { sender.pointer, 4433, true, - 0, - 100, + 3, + 20, certpath[0], certpath[1], keypath[0], diff --git a/src/client.rs b/src/client.rs index 82b2e74..d9a1b1d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,8 +1,8 @@ -use flume::{Sender, Receiver}; +use std::{path::Path, time::Duration}; use tokio::runtime::Runtime; -use wtransport::{Connection, datagram::Datagram, Endpoint, endpoint, ClientConfig}; +use wtransport::{ Endpoint, endpoint, ClientConfig, tls::Certificate}; -use crate::{RUNTIME, CONN_FN, SEND_FN, connection::Conn}; +use crate::{RUNTIME, SEND_FN, connection::Conn}; pub struct WebTransportClient { pub client: Option>, @@ -73,5 +73,54 @@ impl WebTransportClient { // } } + +#[no_mangle] +pub unsafe extern "C" fn proc_client_init( + send_func: Option, + keepalive: u64, + timeout: u64, + cert_path: *const u8, + cert_path_len: usize, + key_path: *const u8, + key_path_len: usize, +) -> *mut WebTransportClient { + assert!(!send_func.is_none()); + + let cert_path = ::std::slice::from_raw_parts(cert_path, cert_path_len); + let key_path = ::std::slice::from_raw_parts(key_path, key_path_len); + let cert_path = Path::new(std::str::from_utf8(cert_path).unwrap()); + let key_path = Path::new(std::str::from_utf8(key_path).unwrap()); + + let _certificates = Certificate::load(cert_path, key_path).unwrap(); + + let keepalive = if keepalive == 0 { + None + } else { + Some(Duration::from_secs(keepalive)) + }; + let timeout = if timeout == 0 { + None + } else { + Some(Duration::from_secs(timeout)) + }; + //print the paths for debug + let config = ClientConfig::builder() + .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) + .with_native_certs() + .keep_alive_interval(keepalive) + .max_idle_timeout(timeout).unwrap() + .build(); + let server = WebTransportClient::new(send_func, config); + match server { + Ok(server) => { + let server_ptr = Box::into_raw(Box::new(server)); + server_ptr + } + Err(_) => { + panic!("Error creating server") + } + } +} + #[no_mangle] pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn, _c: *mut Runtime) {} \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index 6d7719a..8ba1c8e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -71,6 +71,8 @@ impl WebTransportServer { }); } } + + #[no_mangle] pub unsafe extern "C" fn proc_server_init( send_func: Option, @@ -143,9 +145,8 @@ pub unsafe extern "C" fn proc_server_init_streams( let sender = client.datagram_ch_sender.clone(); client.buffer = Some(from_raw_parts_mut(buffer, buflen)); - + executor::spawn(async move { - // let _ = RUNTIME.enter(); loop { tokio::select! { _ = client.conn.accept_bi() => { @@ -164,11 +165,11 @@ pub unsafe extern "C" fn proc_server_init_streams( // client.conn.closed().await; // } // }; - client.conn.closed().await; + return client.conn.closed().await; } _ = client.conn.accept_uni() => { //close the connection until we implement the uni stream - client.conn.closed().await; + return client.conn.closed().await; // match stream { // Ok(mut stream) => { // println!("Accepted UNI stream"); @@ -192,20 +193,18 @@ pub unsafe extern "C" fn proc_server_init_streams( } stream = client.conn.receive_datagram() => { match stream { - //write dgram into denoBUF and send the copied bytes len to deno - Ok(dgram) => { - sender.send(dgram).unwrap(); - }, + Ok(dgram) => sender.send_async(dgram).await.unwrap(), _ => { - // + client.conn.closed().await; + //TODO(hironichu): Send action to Deno to free the pointer and buffer + return ; } } }, } } - }) - .detach(); + }).detach(); } //free all above once From db3e72b1a4da5c7516968a59bab5cb925dc45ce8 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 02:35:37 +0200 Subject: [PATCH 10/72] feat: CI --- .github/workflows/ci.yml | 246 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..72b017e --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,246 @@ +on: + pull_request: + types: [review_requested, opened] + branches: + - 'releases/**' + push: + branches: + - 'main' + - 'releases/**' + - 'testing/**' + - 'feat/**' + - 'fix/**' +concurrency: + group: ${{ github.workflow }}-${{ !contains(github.event.pull_request.labels.*.name, 'test-flaky-ci') && github.head_ref || github.run_id }} + cancel-in-progress: true + +name: WebTransport CI +jobs: + build: + name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} + if: | + github.event_name == 'push' || + !startsWith(github.event.pull_request.head.label, 'hironichu:') + runs-on: ${{ matrix.os }} + timeout-minutes: 90 + strategy: + matrix: + include: + - os: macos-latest + job: test + profile: debug + - os: macos-latest + job: test + profile: release + - os: windows-latest + job: test + profile: debug + - os: windows-latest + job: test + profile: release + - os: 'ubuntu-22.04' + job: test + profile: release + use_sysroot: true + - os: 'ubuntu-22.04' + job: test + profile: debug + - os: 'ubuntu-22.04' + job: lint + profile: debug + - os: 'self-hosted' + job: test + profile: debug + - os: 'self-hosted' + job: test + profile: release + fail-fast: ${{ github.event_name == 'pull_request' || (github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/')) }} + env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + steps: + - name: Configure git + run: | + git config --global core.symlinks true + git config --global fetch.parallel 16 + - name: Clone repository + uses: actions/checkout@v3 + with: + fetch-depth: 2 + submodules: recursive + - name: Create source tarballs (release, linux) + if: | + startsWith(matrix.os, 'ubuntu') && + matrix.profile == 'release' && + matrix.job == 'test' && + github.repository == '${{ github.repository }}' && + startsWith(github.ref, 'refs/tags/') + run: | + mkdir -p target/release + tar --exclude=".git*" --exclude=target --exclude=third_party/prebuilt \ + -czvf target/release/webtransport.tar.gz -C .. webtransport + - name: Setting Up Rust + uses: dtolnay/rust-toolchain@nightly + if: matrix.job == 'test' + - name: Install Deno from .land + if: matrix.os != 'self-hosted' + uses: denoland/setup-deno@v1 + with: + deno-version: v1.x + - name: Install Deno from source + if: matrix.os == 'self-hosted' + run: | + echo "Check if Deno is already installed" + if ! type deno > /dev/null; then + echo "Deno is not installed, installing..." + curl -s https://gist.githubusercontent.com/LukeChannings/09d53f5c364391042186518c8598b85e/raw/ac8cd8c675b985edd4b3e16df63ffef14d1f0e24/deno_install.sh | sh + else + echo "Deno is already installed" + fi + - name: Error on warning + run: echo "RUSTFLAGS=-D warnings" >> $GITHUB_ENV + - name: Log versions + shell: bash + run: | + rustc --version + cargo --version + deno --version + - name: Cache Cargo home + if: (matrix.job == 'test' && (matrix.profile == 'release' || matrix.profile == 'debug')) + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + key: 16-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} + + - name: Cache build output (main) + uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b + if: (matrix.job == 'test' && matrix.profile == 'release') && + github.ref == 'refs/heads/main' + with: + path: | + ./target + !./target/*/gn_out + !./target/*/*.dll + !./target/*/*.so + !./target/*/*.dylib + !./target/*/*.tar.gz + key: | + 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} + - name: Cache build output (PR) + uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b + if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') && + matrix.job == 'test' && matrix.profile == 'release' + with: + path: | + ./target + !./dist + !./target/*/gn_out + !./target/*/*.dll + !./target/*/*.so + !./target/*/*.dylib + !./target/*/*.tar.gz + key: never_saved + restore-keys: | + 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- + + - name: Skip save cache (PR) + run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV + shell: bash + if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') && + matrix.job == 'test' && matrix.profile == 'release' + + - name: Shallow clone crates.io index + if: matrix.job == 'test' && matrix.profile == 'release' + shell: bash + run: | + if [ ! -d ~/.cargo/registry/index/github.com-1ecc6299db9ec823/.git ] + then + git clone --depth 1 --no-checkout \ + https://github.com/rust-lang/crates.io-index \ + ~/.cargo/registry/index/github.com-1ecc6299db9ec823 + fi + - name: Test FMT + if: matrix.job == 'lint' + run: deno task fmt + - name: Test LINT + if: matrix.job == 'lint' + run: deno task lint + + - name: Build debug unix x64 + if: | + (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && + (matrix.job == 'test' && matrix.profile == 'debug') + run: deno task cargo-build --all-targets + - name: Build debug unix arch64 + if: | + (startsWith(matrix.os, 'self-hosted')) && + (matrix.job == 'test' && matrix.profile == 'debug') + run: | + deno task cargo-build-arm + - name: Build debug Windows + if: | + runner.os == 'Windows' && + matrix.job == 'test' && + matrix.profile == 'debug' + env: + CARGO_PROFILE_DEV_DEBUG: 0 + run: | + deno task cargo-build --all-targets + + - name: Build release Unix x64 + if: | + (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && + (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || + (github.repository == '${{ github.repository }}' && + (github.ref == 'refs/heads/main' || + startsWith(github.ref, 'refs/tags/')))) + run: deno task cargo-build --release + - name: Build release Unix Aarch64 + if: | + (startsWith(matrix.os, 'self-hosted')) && + (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || + (github.repository == '${{ github.repository }}' && + (github.ref == 'refs/heads/main' || + startsWith(github.ref, 'refs/tags/')))) + run: | + deno task cargo-release-arm + - name: Build release Windows + if: | + runner.os == 'Windows' && + matrix.job == 'test' && + matrix.profile == 'release' + run: | + deno task cargo-build + + - name: Run deno test (debug) + if: | + matrix.job == 'test' && matrix.profile == 'debug' && + !startsWith(github.ref, 'refs/tags/') + run: | + deno task test + - name: Run deno test (release) + if: | + (matrix.job == 'test' && matrix.profile == 'release') && + (matrix.use_sysroot || ( + github.repository == '${{ github.repository }}' && + github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) + run: | + deno task test + - name: Upload release to GitHub + uses: softprops/action-gh-release@59c3b4891632ff9a897f99a91d7bc557467a3a22 + if: | + (matrix.job == 'test' && matrix.profile == 'release') && + github.repository == '${{ github.repository }}' && + github.ref == 'refs/heads/main' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: | + dist/webtransport.dll + dist/libwebtransport.so + dist/libwebtransport_aarch64.so + dist/libwebtransport.dylib + draft: true \ No newline at end of file From 1ac433831ab6854032d2517a993b1ddc6efc8244 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 02:37:15 +0200 Subject: [PATCH 11/72] fix: remove deno.lock file --- deno.lock | 75 ------------------------------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 deno.lock diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 7e6743c..0000000 --- a/deno.lock +++ /dev/null @@ -1,75 +0,0 @@ -{ - "version": "3", - "remote": { - "https://deno.land/std@0.184.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462", - "https://deno.land/std@0.184.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", - "https://deno.land/std@0.184.0/encoding/hex.ts": "b4b1a7cb678745b0bf181ed8cf2498c7be00d121a7de244b752fbf9c7d9c48cd", - "https://deno.land/std@0.184.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", - "https://deno.land/std@0.184.0/fs/_util.ts": "579038bebc3bd35c43a6a7766f7d91fbacdf44bc03468e9d3134297bb99ed4f9", - "https://deno.land/std@0.184.0/fs/copy.ts": "14214efd94fc3aa6db1e4af2b4b9578e50f7362b7f3725d5a14ad259a5df26c8", - "https://deno.land/std@0.184.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", - "https://deno.land/std@0.184.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", - "https://deno.land/std@0.184.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", - "https://deno.land/std@0.184.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", - "https://deno.land/std@0.184.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1", - "https://deno.land/std@0.184.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", - "https://deno.land/std@0.184.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", - "https://deno.land/std@0.184.0/fs/expand_glob.ts": "e4f56259a0a70fe23f05215b00de3ac5e6ba46646ab2a06ebbe9b010f81c972a", - "https://deno.land/std@0.184.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", - "https://deno.land/std@0.184.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", - "https://deno.land/std@0.184.0/fs/walk.ts": "920be35a7376db6c0b5b1caf1486fb962925e38c9825f90367f8f26b5e5d0897", - "https://deno.land/std@0.184.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", - "https://deno.land/std@0.184.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", - "https://deno.land/std@0.184.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", - "https://deno.land/std@0.184.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", - "https://deno.land/std@0.184.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", - "https://deno.land/std@0.184.0/path/mod.ts": "bf718f19a4fdd545aee1b06409ca0805bd1b68ecf876605ce632e932fe54510c", - "https://deno.land/std@0.184.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", - "https://deno.land/std@0.184.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", - "https://deno.land/std@0.184.0/path/win32.ts": "d186344e5583bcbf8b18af416d13d82b35a317116e6460a5a3953508c3de5bba", - "https://deno.land/x/byte_type@0.2.2/mod.ts": "f7f562a87f4211510335696c6be7143c7e015e05c58d0632b9f68add6d7fb599", - "https://deno.land/x/byte_type@0.2.2/types/array/array.ts": "08d0d4bf82e6e60dedfc2319d01128f1e756affee5547e021dda6321ba5b97e8", - "https://deno.land/x/byte_type@0.2.2/types/array/array_buffer.ts": "99dc5d135125c9d2cb3f689c266af098e71b6cad6c9ebecaec55fe96c7d216ce", - "https://deno.land/x/byte_type@0.2.2/types/array/mod.ts": "3bcf269bf777f8580bb1cf890e39541846fff0b73d0b867b850e58a9cd05f332", - "https://deno.land/x/byte_type@0.2.2/types/array/typed_array.ts": "e66e489799e83409695519fd446115c8bc532e3bee93c147e4bd085b32046905", - "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags16.ts": "126685d817d7c07b5eadb290db6d466dc5f6775b0404619c1dfc2ec8f8be23a0", - "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags32.ts": "9f658051e75aa70f617b90ab807a97321f1f5e1c0adaabb9134c1bd56b965b31", - "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags64.ts": "f7244243268a67938cb7cc6d002a8389eb3f1277825ca0d448972956fc003055", - "https://deno.land/x/byte_type@0.2.2/types/bitflags/bitflags8.ts": "dec36625ce3e9ae927f06ef01b9cfa031bb626fd51b8ca4e4dcd48c33af21677", - "https://deno.land/x/byte_type@0.2.2/types/bitflags/mod.ts": "1897c05a4349ab257e80125968cab75103b57c90620e9cf5f0e6747c95f1541e", - "https://deno.land/x/byte_type@0.2.2/types/leb128/_i64leb128.ts": "6e257775fe3b7622bfed3badc558b1f73b1121616d157659837b3f41a4c18936", - "https://deno.land/x/byte_type@0.2.2/types/leb128/i32leb128.ts": "8e1746edb0492ed121b71c2432fe072eac44f06e425c5f13999070e063264b25", - "https://deno.land/x/byte_type@0.2.2/types/leb128/i64leb128.ts": "166d0c9058a2dc9e97dce7694b6c24eced21ef8a93925aa383594276fa861de1", - "https://deno.land/x/byte_type@0.2.2/types/leb128/mod.ts": "cdc37cc01da71fe7b264d86e9f24ec7cc0551b7cbfc0c8862bbdf8c3ae7e22c3", - "https://deno.land/x/byte_type@0.2.2/types/misc/expect.ts": "9c86ba2fad4151f4a25be58b2cdf259e3ef2baca5b0f7051720e82d39e985121", - "https://deno.land/x/byte_type@0.2.2/types/misc/mod.ts": "d0c4aff0831d8cb961f718e0ff9b34006daccb1a26dfc4300c17ff55a1d7486c", - "https://deno.land/x/byte_type@0.2.2/types/mod.ts": "d5152cdf0427cf6729f7f7102876f30092e8054261700097852a99162cb95b3c", - "https://deno.land/x/byte_type@0.2.2/types/primitive/bool.ts": "00c6f66ef35b07aaf304e479209f61d156ad4b61dd3c8558342c85a7610a44a4", - "https://deno.land/x/byte_type@0.2.2/types/primitive/f32.ts": "ee99020ba2ee2bcd8be7f8c36af3be6d780c4d6c5afa79c655731570b307187c", - "https://deno.land/x/byte_type@0.2.2/types/primitive/f64.ts": "430476c8e6de9aa987fa73532dabf82af594659744241c5b3eb5522d24f49816", - "https://deno.land/x/byte_type@0.2.2/types/primitive/i16.ts": "cc12a7d2fc6e0283d9b9fff86e4d4176a71254f122ddc47fe95e81d143f63c20", - "https://deno.land/x/byte_type@0.2.2/types/primitive/i32.ts": "7879b02ed250221c5d1889df9015a8040499e567f0fdc12c056478db0e18a253", - "https://deno.land/x/byte_type@0.2.2/types/primitive/i64.ts": "b9607f4979fd755cb09e70cc0cdfcf0faffb13ad61ae359059976c9463906c5d", - "https://deno.land/x/byte_type@0.2.2/types/primitive/i8.ts": "4cdf0f5cb77603c19dd440f9ad8f129c928860ca580af5a5707e3771524b087b", - "https://deno.land/x/byte_type@0.2.2/types/primitive/mod.ts": "97f7b9928e258ed5efaaf44e808f3162262096e04fadb960f4dacbdcd19d118c", - "https://deno.land/x/byte_type@0.2.2/types/primitive/u16.ts": "d9fd602a03d14fa4c6ccc1749c43f8e0a1d3122d54b8bb516ad41815df7a4c9c", - "https://deno.land/x/byte_type@0.2.2/types/primitive/u32.ts": "bf54b30f35fadf777ee3f472c06c61fe8db5940a28fee80a8d4c2755774c8eed", - "https://deno.land/x/byte_type@0.2.2/types/primitive/u64.ts": "7c71c4f422c719192ef1eb90fbe747c7c86417549f3558f17ba99e2312a29a93", - "https://deno.land/x/byte_type@0.2.2/types/primitive/u8.ts": "142fde887c55f96f3b737b845027d8b0b7e94e2f45627658a00457bcb5f5db88", - "https://deno.land/x/byte_type@0.2.2/types/string/fixed_length.ts": "8e7bda6308a1bad3f8c4b85f4c3c349b994f2a5dada8c270b51bea1d35dfe5ea", - "https://deno.land/x/byte_type@0.2.2/types/string/mod.ts": "0aed4493034a83c46e55ee630484d06ead804e32608e8f4b364e558d2d5b4655", - "https://deno.land/x/byte_type@0.2.2/types/string/null_terminated.ts": "5ce304e52e9bc03a185ecd217c5111ce02065ced6ea96bb531a8a806a3ce07a7", - "https://deno.land/x/byte_type@0.2.2/types/struct/aligned.ts": "cc5d7fb50f9a10443b569e4cc217132e693ea00cc74dd1452681d7dac3301897", - "https://deno.land/x/byte_type@0.2.2/types/struct/mod.ts": "17c32fe69560b2a4671fe60180387c7c3f9256f8ae58175a93f743084aef246c", - "https://deno.land/x/byte_type@0.2.2/types/struct/packed.ts": "f730b4335f7b405a2571da843e4e190323b7fb74b310b68c578c61cbb7ac5a96", - "https://deno.land/x/byte_type@0.2.2/types/struct/struct.ts": "725bc9c26bdc1d5887e714407df3a5cac2bea21ad379fad1d170f405b6163dfd", - "https://deno.land/x/byte_type@0.2.2/types/tuple/mod.ts": "3a11a652a33aef575fadd28d70b78c872f34caab93c39cbb311442c4496ab4d8", - "https://deno.land/x/byte_type@0.2.2/types/types.ts": "db9ba288d3ccaf99f4f5687c4a47d1bd98b92b1e3d4fe10853edf37d568e8577", - "https://deno.land/x/byte_type@0.2.2/utils.ts": "e1458363e86a289e1143785c5e4b08e0f9a50c980a0c782ba07e3a9a68b2b8d7", - "https://deno.land/x/plug@1.0.2/deps.ts": "36846a478fafaa1d8ca18aafd91584a748aa58de47ad0f45f008881dad82f374", - "https://deno.land/x/plug@1.0.2/download.ts": "b92bc1c1ae35fdb75828847f1ebfc7e51bf462f339729740e1cffe78384e1509", - "https://deno.land/x/plug@1.0.2/mod.ts": "32e0006ed6142e7becdb4103c2aa4e1e9ef28459d7243d6cb404a028f7c4eb7e", - "https://deno.land/x/plug@1.0.2/types.ts": "0490359117c53783138f2b6692a34f85bca9237314ba8cdef8ad682d81218d21", - "https://deno.land/x/plug@1.0.2/util.ts": "ded3db6e9bb16b8003899d9073fb310e13231ca617b35d6b7dfd53f38762cc76" - } -} From def6c0aad2af43140c4ce95036e3485cb6b3854c Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 03:10:40 +0200 Subject: [PATCH 12/72] feat: more config and stuff --- .github/SECURITY.yml | 0 .gitignore | 3 ++- Cargo.toml | 51 +++++++++++++++++++++++++++++++++----------- deno.jsonc | 10 +++++++-- mod/lib.ts | 2 +- utils/clean.js | 15 +++++++++++++ 6 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 .github/SECURITY.yml create mode 100644 utils/clean.js diff --git a/.github/SECURITY.yml b/.github/SECURITY.yml new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index a0ff32d..fb274c3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,11 @@ debug/ target/ certs/ +dist/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock -deno.lock +*.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Cargo.toml b/Cargo.toml index 155aca1..1afd77f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,48 @@ [package] -name = "FTL-2" +name = "Webtransport" +description = "Webtransport FFI library for Deno" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] -name = "ftlt" +name = "webtransport" crate-type = ["cdylib"] +test = false +bench = false + +[profile.release] +strip = true +opt-level = 3 +debug = false +debug-assertions = false +overflow-checks = true +lto = "fat" +panic = "unwind" +incremental = false +codegen-units = 16 +rpath = false + +[profile.dev] +opt-level = 0 +lto = false +debug = true +debug-assertions = true +overflow-checks = true +panic = "abort" +incremental = true +codegen-units = 1 [dependencies] -flume = "0.11.0" -num_cpus = "1.16.0" -once_cell = "1.18.0" -smol = "1.3.0" -tokio = { version = "1.32.0", features = ["rt", "rt-multi-thread", "macros"] } -# wtransport = "0.1.4" +flume = "=0.11.0" +num_cpus = "=1.16.0" +once_cell = "=1.18.0" +smol = "=1.3.0" +tokio = { version = "=1.32.0", default-features = false, features = ["rt", "rt-multi-thread", "macros"] } +# wtransport = "0.1.4" # TODO: Replace this once the fix for arm is merged. wtransport = { git = "https://github.com/hironichu/wtransport", branch = "master" } -base64 = "0.21.4" -rcgen = "0.11.1" -ring = "0.16.20" -time = "0.3.28" -anyhow = "1.0.75" +base64 = "=0.21.4" +rcgen = "=0.11.1" +ring = "=0.16.20" +time = "=0.3.28" +anyhow = "=1.0.75" diff --git a/deno.jsonc b/deno.jsonc index c0d0f1c..85ef36d 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,9 +1,15 @@ { "tasks": { + //Examples "example:web": "deno run -A ./examples/web_server/web.js", "example:server": "deno run -A --unstable ./examples/deno/wt_server.ts", - "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts" - }, + "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", + // Build task + "build:release-arm": "cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/webtransport.so ./dist/webtransport_aarch64.so", + "build:release":"", + //Utils + "util:clean": "deno run --allow-write --allow-read ./utils/clean.ts", + }, "compilerOptions": { "checkJs": true, "strict": true diff --git a/mod/lib.ts b/mod/lib.ts index 25887ac..9726b45 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -40,7 +40,7 @@ const symbols = { //TODO(hironichu): Make this works from internet path const options: FetchOptions = { - name: "ftlt", + name: "webtransport", cache: "reloadAll", url: "./target/release/", }; diff --git a/utils/clean.js b/utils/clean.js new file mode 100644 index 0000000..a67ad82 --- /dev/null +++ b/utils/clean.js @@ -0,0 +1,15 @@ +if (import.meta.main) { + try { + Deno.statSync("./dist"); + const rule = /^(.*)\.so$|^(.*)\.dll$|^(.*)\.dylib$/; + const files = Deno.readDirSync("./dist"); + for (const file of files) { + if (!rule.test(file.name)) { + Deno.removeSync("./dist/" + file.name); + } + } + console.info(`Cleaned up ./dist`); + } catch { + console.error(`Count not find ./dist`); + } + } \ No newline at end of file From 852327fdb48838c56fd31a1bf059f29995da589a Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 03:51:39 +0200 Subject: [PATCH 13/72] Fixed configuration for CI (not tested) --- .github/SECURITY.md | 1 + .github/SECURITY.yml | 0 .github/workflows/ci.yml | 25 +++++++++++++------------ Cargo.toml | 4 ++-- README.md | 6 ++++++ deno.jsonc | 10 ++++++++-- examples/deno/wt_server_test.ts | 1 + 7 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 .github/SECURITY.md delete mode 100644 .github/SECURITY.yml create mode 100644 README.md create mode 100644 examples/deno/wt_server_test.ts diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..9862d01 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1 @@ +# Security Policy diff --git a/.github/SECURITY.yml b/.github/SECURITY.yml deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72b017e..c5ff827 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: group: ${{ github.workflow }}-${{ !contains(github.event.pull_request.labels.*.name, 'test-flaky-ci') && github.head_ref || github.run_id }} cancel-in-progress: true -name: WebTransport CI +name: DWT CI jobs: build: name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} @@ -162,24 +162,24 @@ jobs: https://github.com/rust-lang/crates.io-index \ ~/.cargo/registry/index/github.com-1ecc6299db9ec823 fi - - name: Test FMT + - name: Deno Format if: matrix.job == 'lint' - run: deno task fmt - - name: Test LINT + run: deno task util:fmt + - name: Deno lint if: matrix.job == 'lint' - run: deno task lint + run: deno task util:lint - name: Build debug unix x64 if: | (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && (matrix.job == 'test' && matrix.profile == 'debug') - run: deno task cargo-build --all-targets + run: deno task build:debug - name: Build debug unix arch64 if: | (startsWith(matrix.os, 'self-hosted')) && (matrix.job == 'test' && matrix.profile == 'debug') run: | - deno task cargo-build-arm + deno task build:debug-arm - name: Build debug Windows if: | runner.os == 'Windows' && @@ -188,7 +188,7 @@ jobs: env: CARGO_PROFILE_DEV_DEBUG: 0 run: | - deno task cargo-build --all-targets + deno task build:debug - name: Build release Unix x64 if: | @@ -197,8 +197,9 @@ jobs: (github.repository == '${{ github.repository }}' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) - run: deno task cargo-build --release - - name: Build release Unix Aarch64 + run: deno task build:release + + - name: Build release Unix aarch64 if: | (startsWith(matrix.os, 'self-hosted')) && (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || @@ -206,14 +207,14 @@ jobs: (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) run: | - deno task cargo-release-arm + deno task build:release-arm - name: Build release Windows if: | runner.os == 'Windows' && matrix.job == 'test' && matrix.profile == 'release' run: | - deno task cargo-build + deno task build:debug - name: Run deno test (debug) if: | diff --git a/Cargo.toml b/Cargo.toml index 1afd77f..6339e08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "Webtransport" -description = "Webtransport FFI library for Deno" +name = "webtransport" +description = "Deno WebTransport FFI library for Deno" version = "0.1.0" edition = "2021" diff --git a/README.md b/README.md new file mode 100644 index 0000000..f6f69bc --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Deno Webtransport + + +This library implements a very WIP version of the [WebTransport](https://w3c.github.io/webtransport/) API for Deno. + +> This does not follow standards and is not production ready. basically just a PoC \ No newline at end of file diff --git a/deno.jsonc b/deno.jsonc index 85ef36d..1fe6584 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -5,10 +5,16 @@ "example:server": "deno run -A --unstable ./examples/deno/wt_server.ts", "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", // Build task - "build:release-arm": "cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/webtransport.so ./dist/webtransport_aarch64.so", - "build:release":"", + "build:release":"deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release", + "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", + "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", + "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", //Utils "util:clean": "deno run --allow-write --allow-read ./utils/clean.ts", + "util:fmt" : "deno fmt --ignore=./target --ignore=./.git --ignore=./.github --ignore=./.vscode", + "util:lint": "deno lint --unstable --ignore=./target --ignore=./.git --ignore=./.github --ignore=./.vscode", + //Tests + "test": "deno test -A --unstable examples/deno/wt_server_test.ts" }, "compilerOptions": { "checkJs": true, diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts new file mode 100644 index 0000000..0e33247 --- /dev/null +++ b/examples/deno/wt_server_test.ts @@ -0,0 +1 @@ +console.info("TBD"); \ No newline at end of file From d7d7b3286ffb51d4db1dc1b01971b0667ea28573 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 03:58:19 +0200 Subject: [PATCH 14/72] fix clean, ci --- .github/workflows/ci.yml | 7 ------- deno.jsonc | 2 +- utils/clean.js | 5 +++-- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5ff827..6fb4bd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,12 +121,9 @@ jobs: github.ref == 'refs/heads/main' with: path: | - ./target - !./target/*/gn_out !./target/*/*.dll !./target/*/*.so !./target/*/*.dylib - !./target/*/*.tar.gz key: | 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} - name: Cache build output (PR) @@ -135,13 +132,9 @@ jobs: matrix.job == 'test' && matrix.profile == 'release' with: path: | - ./target - !./dist - !./target/*/gn_out !./target/*/*.dll !./target/*/*.so !./target/*/*.dylib - !./target/*/*.tar.gz key: never_saved restore-keys: | 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- diff --git a/deno.jsonc b/deno.jsonc index 1fe6584..2648db6 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -10,7 +10,7 @@ "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", //Utils - "util:clean": "deno run --allow-write --allow-read ./utils/clean.ts", + "util:clean": "deno run --allow-write --allow-read ./utils/clean.js", "util:fmt" : "deno fmt --ignore=./target --ignore=./.git --ignore=./.github --ignore=./.vscode", "util:lint": "deno lint --unstable --ignore=./target --ignore=./.git --ignore=./.github --ignore=./.vscode", //Tests diff --git a/utils/clean.js b/utils/clean.js index a67ad82..848e5a7 100644 --- a/utils/clean.js +++ b/utils/clean.js @@ -4,9 +4,10 @@ if (import.meta.main) { const rule = /^(.*)\.so$|^(.*)\.dll$|^(.*)\.dylib$/; const files = Deno.readDirSync("./dist"); for (const file of files) { - if (!rule.test(file.name)) { + console.log(rule.test(file.name)) + // if (rule.test(file.name)) { Deno.removeSync("./dist/" + file.name); - } + // } } console.info(`Cleaned up ./dist`); } catch { From 92127656c0ce83874450e020d4ab966a664d3879 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 03:59:36 +0200 Subject: [PATCH 15/72] fix fmt cmd --- README.md | 7 ++++--- deno.jsonc | 26 +++++++++++++------------- examples/deno/wt_server_test.ts | 2 +- utils/clean.js | 30 +++++++++++++++--------------- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index f6f69bc..ab53031 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Deno Webtransport +This library implements a very WIP version of the +[WebTransport](https://w3c.github.io/webtransport/) API for Deno. -This library implements a very WIP version of the [WebTransport](https://w3c.github.io/webtransport/) API for Deno. - -> This does not follow standards and is not production ready. basically just a PoC \ No newline at end of file +> This does not follow standards and is not production ready. basically just a +> PoC diff --git a/deno.jsonc b/deno.jsonc index 2648db6..ffa8411 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,21 +1,21 @@ { "tasks": { - //Examples + //Examples "example:web": "deno run -A ./examples/web_server/web.js", "example:server": "deno run -A --unstable ./examples/deno/wt_server.ts", "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", - // Build task - "build:release":"deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release", - "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", - "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", - "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", - //Utils - "util:clean": "deno run --allow-write --allow-read ./utils/clean.js", - "util:fmt" : "deno fmt --ignore=./target --ignore=./.git --ignore=./.github --ignore=./.vscode", - "util:lint": "deno lint --unstable --ignore=./target --ignore=./.git --ignore=./.github --ignore=./.vscode", - //Tests - "test": "deno test -A --unstable examples/deno/wt_server_test.ts" - }, + // Build task + "build:release": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release", + "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", + "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", + "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", + //Utils + "util:clean": "deno run --allow-write --allow-read ./utils/clean.js", + "util:fmt": "deno fmt --unstable", + "util:lint": "deno lint --unstable", + //Tests + "test": "deno test -A --unstable examples/deno/wt_server_test.ts" + }, "compilerOptions": { "checkJs": true, "strict": true diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 0e33247..87407fc 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1 +1 @@ -console.info("TBD"); \ No newline at end of file +console.info("TBD"); diff --git a/utils/clean.js b/utils/clean.js index 848e5a7..e5dafd5 100644 --- a/utils/clean.js +++ b/utils/clean.js @@ -1,16 +1,16 @@ if (import.meta.main) { - try { - Deno.statSync("./dist"); - const rule = /^(.*)\.so$|^(.*)\.dll$|^(.*)\.dylib$/; - const files = Deno.readDirSync("./dist"); - for (const file of files) { - console.log(rule.test(file.name)) - // if (rule.test(file.name)) { - Deno.removeSync("./dist/" + file.name); - // } - } - console.info(`Cleaned up ./dist`); - } catch { - console.error(`Count not find ./dist`); - } - } \ No newline at end of file + try { + Deno.statSync("./dist"); + const rule = /^(.*)\.so$|^(.*)\.dll$|^(.*)\.dylib$/; + const files = Deno.readDirSync("./dist"); + for (const file of files) { + console.log(rule.test(file.name)); + // if (rule.test(file.name)) { + Deno.removeSync("./dist/" + file.name); + // } + } + console.info(`Cleaned up ./dist`); + } catch { + console.error(`Count not find ./dist`); + } +} From c28c831252674723d707e24d64e6d9ca5d903952 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:03:13 +0200 Subject: [PATCH 16/72] fix ci syntax --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6fb4bd6..2a65f12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,9 @@ jobs: tar --exclude=".git*" --exclude=target --exclude=third_party/prebuilt \ -czvf target/release/webtransport.tar.gz -C .. webtransport - name: Setting Up Rust - uses: dtolnay/rust-toolchain@nightly + uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly if: matrix.job == 'test' - name: Install Deno from .land if: matrix.os != 'self-hosted' From 6ae11012b596ffb5a6e15ec62f90d67fedfa8e03 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:08:26 +0200 Subject: [PATCH 17/72] fix ci syntax --- .github/workflows/ci.yml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a65f12..35f6a57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,12 +45,15 @@ jobs: - os: 'ubuntu-22.04' job: test profile: debug + - os: 'ubuntu-22.04' job: lint profile: debug + - os: 'self-hosted' job: test profile: debug + - os: 'self-hosted' job: test profile: release @@ -73,22 +76,24 @@ jobs: startsWith(matrix.os, 'ubuntu') && matrix.profile == 'release' && matrix.job == 'test' && - github.repository == '${{ github.repository }}' && + github.repository == 'hironichu/webtransport' && startsWith(github.ref, 'refs/tags/') run: | mkdir -p target/release tar --exclude=".git*" --exclude=target --exclude=third_party/prebuilt \ -czvf target/release/webtransport.tar.gz -C .. webtransport + - name: Setting Up Rust uses: dtolnay/rust-toolchain@master with: toolchain: nightly - if: matrix.job == 'test' + - name: Install Deno from .land if: matrix.os != 'self-hosted' uses: denoland/setup-deno@v1 with: deno-version: v1.x + - name: Install Deno from source if: matrix.os == 'self-hosted' run: | @@ -101,6 +106,7 @@ jobs: fi - name: Error on warning run: echo "RUSTFLAGS=-D warnings" >> $GITHUB_ENV + - name: Log versions shell: bash run: | @@ -157,9 +163,11 @@ jobs: https://github.com/rust-lang/crates.io-index \ ~/.cargo/registry/index/github.com-1ecc6299db9ec823 fi + - name: Deno Format if: matrix.job == 'lint' run: deno task util:fmt + - name: Deno lint if: matrix.job == 'lint' run: deno task util:lint @@ -169,6 +177,7 @@ jobs: (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && (matrix.job == 'test' && matrix.profile == 'debug') run: deno task build:debug + - name: Build debug unix arch64 if: | (startsWith(matrix.os, 'self-hosted')) && @@ -189,7 +198,7 @@ jobs: if: | (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || - (github.repository == '${{ github.repository }}' && + (github.repository == 'hironichu/webtransport' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) run: deno task build:release @@ -198,7 +207,7 @@ jobs: if: | (startsWith(matrix.os, 'self-hosted')) && (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || - (github.repository == '${{ github.repository }}' && + (github.repository == 'hironichu/webtransport' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) run: | @@ -221,7 +230,7 @@ jobs: if: | (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || ( - github.repository == '${{ github.repository }}' && + github.repository == 'hironichu/webtransport' && github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) run: | deno task test @@ -229,7 +238,7 @@ jobs: uses: softprops/action-gh-release@59c3b4891632ff9a897f99a91d7bc557467a3a22 if: | (matrix.job == 'test' && matrix.profile == 'release') && - github.repository == '${{ github.repository }}' && + github.repository == 'hironichu/webtransport' && github.ref == 'refs/heads/main' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From f676908570fa982824d042d93929d1db1933fb92 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:11:01 +0200 Subject: [PATCH 18/72] fix ci --- .github/workflows/ci.yml | 2 +- deno.jsonc | 7 +++++-- utils/clean.js | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35f6a57..0583347 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: group: ${{ github.workflow }}-${{ !contains(github.event.pull_request.labels.*.name, 'test-flaky-ci') && github.head_ref || github.run_id }} cancel-in-progress: true -name: DWT CI +name: Webtransport CI jobs: build: name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} diff --git a/deno.jsonc b/deno.jsonc index ffa8411..434cc65 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,4 +1,7 @@ { + "test": { + + }, "tasks": { //Examples "example:web": "deno run -A ./examples/web_server/web.js", @@ -6,9 +9,9 @@ "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", // Build task "build:release": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release", - "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", + "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/webtransport.so ./dist/webtransport_aarch64.so", "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", - "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/dwt.so ./dist/webtransport_aarch64.so", + "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/webtransport.so ./dist/webtransport_aarch64.so", //Utils "util:clean": "deno run --allow-write --allow-read ./utils/clean.js", "util:fmt": "deno fmt --unstable", diff --git a/utils/clean.js b/utils/clean.js index e5dafd5..d35fe85 100644 --- a/utils/clean.js +++ b/utils/clean.js @@ -5,9 +5,9 @@ if (import.meta.main) { const files = Deno.readDirSync("./dist"); for (const file of files) { console.log(rule.test(file.name)); - // if (rule.test(file.name)) { - Deno.removeSync("./dist/" + file.name); - // } + if (rule.test(file.name)) { + Deno.removeSync("./dist/" + file.name); + } } console.info(`Cleaned up ./dist`); } catch { From 87f087bfc7fa57c82987a8bc2cf595e798b3d5bb Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:13:57 +0200 Subject: [PATCH 19/72] fix ci --- .github/workflows/ci.yml | 17 ----------------- deno.jsonc | 4 ++-- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0583347..2f603b8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,23 +147,6 @@ jobs: restore-keys: | 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- - - name: Skip save cache (PR) - run: echo "CACHE_SKIP_SAVE=true" >> $GITHUB_ENV - shell: bash - if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') && - matrix.job == 'test' && matrix.profile == 'release' - - - name: Shallow clone crates.io index - if: matrix.job == 'test' && matrix.profile == 'release' - shell: bash - run: | - if [ ! -d ~/.cargo/registry/index/github.com-1ecc6299db9ec823/.git ] - then - git clone --depth 1 --no-checkout \ - https://github.com/rust-lang/crates.io-index \ - ~/.cargo/registry/index/github.com-1ecc6299db9ec823 - fi - - name: Deno Format if: matrix.job == 'lint' run: deno task util:fmt diff --git a/deno.jsonc b/deno.jsonc index 434cc65..82b88d6 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -9,9 +9,9 @@ "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", // Build task "build:release": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release", - "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/webtransport.so ./dist/webtransport_aarch64.so", + "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/libwebtransport.so ./dist/libwebtransport_aarch64.so", "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", - "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/webtransport.so ./dist/webtransport_aarch64.so", + "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/libwebtransport.so ./dist/libwebtransport_aarch64.so", //Utils "util:clean": "deno run --allow-write --allow-read ./utils/clean.js", "util:fmt": "deno fmt --unstable", From 0cd3cd9a398dd3d1abceaeea9c6156ecad483931 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:21:25 +0200 Subject: [PATCH 20/72] fix ci --- .github/workflows/ci.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f603b8..dea90a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,18 +195,30 @@ jobs: startsWith(github.ref, 'refs/tags/')))) run: | deno task build:release-arm - - name: Build release Windows + + - name: Build Debug Windows if: | - runner.os == 'Windows' && + (runner.os == 'Windows' && matrix.job == 'test' && - matrix.profile == 'release' + matrix.profile == 'release' && + (github.repository == 'hironichu/webtransport' && + (github.ref != 'refs/heads/main' || + startsWith(github.ref, 'refs/tags/')))) run: | deno task build:debug - + - name: Build Release Windows + if: | + (runner.os == 'Windows' && + matrix.job == 'test' && + matrix.profile == 'release' && + (github.repository == 'hironichu/webtransport' && + (github.ref == 'refs/heads/main' || + startsWith(github.ref, 'refs/tags/')))) + run: | + deno task build:debug - name: Run deno test (debug) if: | - matrix.job == 'test' && matrix.profile == 'debug' && - !startsWith(github.ref, 'refs/tags/') + matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') run: | deno task test - name: Run deno test (release) From 51726f89eba38da6a2170c251b353a42315e79ec Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:22:15 +0200 Subject: [PATCH 21/72] fix ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dea90a1..69f56aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -214,7 +214,7 @@ jobs: (github.repository == 'hironichu/webtransport' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) - run: | + run: | deno task build:debug - name: Run deno test (debug) if: | From 442dee0c5a0c295d7f06200f0d6843ce0478b9f4 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:27:35 +0200 Subject: [PATCH 22/72] fix ci windows/linux builds --- .github/workflows/ci.yml | 41 ++++++---------------------------------- 1 file changed, 6 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69f56aa..a0ab7bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,38 +155,29 @@ jobs: if: matrix.job == 'lint' run: deno task util:lint - - name: Build debug unix x64 + - name: Build Debug if: | - (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && + (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && (matrix.job == 'test' && matrix.profile == 'debug') run: deno task build:debug - - name: Build debug unix arch64 + - name: Build Debug ARM if: | (startsWith(matrix.os, 'self-hosted')) && (matrix.job == 'test' && matrix.profile == 'debug') run: | deno task build:debug-arm - - name: Build debug Windows - if: | - runner.os == 'Windows' && - matrix.job == 'test' && - matrix.profile == 'debug' - env: - CARGO_PROFILE_DEV_DEBUG: 0 - run: | - deno task build:debug - - name: Build release Unix x64 + - name: Build release if: | - (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos')) && + (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || (github.repository == 'hironichu/webtransport' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) run: deno task build:release - - name: Build release Unix aarch64 + - name: Build release Linux ARM if: | (startsWith(matrix.os, 'self-hosted')) && (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || @@ -196,26 +187,6 @@ jobs: run: | deno task build:release-arm - - name: Build Debug Windows - if: | - (runner.os == 'Windows' && - matrix.job == 'test' && - matrix.profile == 'release' && - (github.repository == 'hironichu/webtransport' && - (github.ref != 'refs/heads/main' || - startsWith(github.ref, 'refs/tags/')))) - run: | - deno task build:debug - - name: Build Release Windows - if: | - (runner.os == 'Windows' && - matrix.job == 'test' && - matrix.profile == 'release' && - (github.repository == 'hironichu/webtransport' && - (github.ref == 'refs/heads/main' || - startsWith(github.ref, 'refs/tags/')))) - run: | - deno task build:debug - name: Run deno test (debug) if: | matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') From 2a6b3cbe738ab2a6f1fc3c866dc5883d53b35e66 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:35:22 +0200 Subject: [PATCH 23/72] fix ci windows/linux builds --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0ab7bb..4248be6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,7 +115,7 @@ jobs: deno --version - name: Cache Cargo home if: (matrix.job == 'test' && (matrix.profile == 'release' || matrix.profile == 'debug')) - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: | ~/.cargo/registry/index @@ -124,7 +124,7 @@ jobs: key: 16-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} - name: Cache build output (main) - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b + uses: actions/cache@v3 if: (matrix.job == 'test' && matrix.profile == 'release') && github.ref == 'refs/heads/main' with: @@ -135,7 +135,7 @@ jobs: key: | 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} - name: Cache build output (PR) - uses: actions/cache@03e00da99d75a2204924908e1cca7902cafce66b + uses: actions/cache@v3 if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') && matrix.job == 'test' && matrix.profile == 'release' with: @@ -173,8 +173,7 @@ jobs: (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || (github.repository == 'hironichu/webtransport' && - (github.ref == 'refs/heads/main' || - startsWith(github.ref, 'refs/tags/')))) + (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) run: deno task build:release - name: Build release Linux ARM From b358a96d2720577e52e4d37a10edaebd18f829e1 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:38:59 +0200 Subject: [PATCH 24/72] fix ci syntax --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4248be6..220090b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,7 +157,7 @@ jobs: - name: Build Debug if: | - (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && + (!startsWith(matrix.os, 'self-hosted')) && (matrix.job == 'test' && matrix.profile == 'debug') run: deno task build:debug @@ -170,10 +170,9 @@ jobs: - name: Build release if: | - (startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && - (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || - (github.repository == 'hironichu/webtransport' && - (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) + (!startsWith(matrix.os, 'self-hosted')) && + (matrix.job == 'test' && matrix.profile == 'release') && + (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) run: deno task build:release - name: Build release Linux ARM From e909cfa022c9b684eeec17ce0de0176c6e36ed2e Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 04:54:59 +0200 Subject: [PATCH 25/72] added dev/ to CI --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 220090b..8c93b97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ on: - 'testing/**' - 'feat/**' - 'fix/**' + - 'dev/**' concurrency: group: ${{ github.workflow }}-${{ !contains(github.event.pull_request.labels.*.name, 'test-flaky-ci') && github.head_ref || github.run_id }} cancel-in-progress: true From 6aab9943f22958b065797a50692db86cd3f2732e Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 18:06:10 +0200 Subject: [PATCH 26/72] lot of work, restructure client/server code, types and other stuff --- .github/SECURITY.md | 14 ++++++ .github/workflows/ci.yml | 11 +---- .gitignore | 4 ++ .vscode/settings.json | 2 +- Cargo.toml | 9 +++- cert.pem | 11 +++++ examples/deno/wt_client_test.ts | 29 +++++++++++ examples/deno/wt_server_test.ts | 4 +- examples/web_server/index.html | 8 ++-- mod/client.ts | 35 ++++++++++++++ mod/code.ts | 59 +++++++++++++++++++++++ mod/crypto.ts | 67 ++++++++++++++++++++++++++ mod/deno.ts | 16 ++----- mod/deps.ts | 2 + mod/interface.ts | 67 ++++++++++++++++++++++++++ mod/lib.ts | 47 +++--------------- mod/mod.ts | 24 ++++++++++ mod/server.ts | 34 +++++++++++++ mod/utils.ts | 15 ++++++ src/certificate.rs | 85 +++++++++++++++++++-------------- src/server.rs | 33 ++++++++----- utils/clean.js | 3 +- utils/download_lib.ts | 66 +++++++++++++++++++++++++ 23 files changed, 524 insertions(+), 121 deletions(-) create mode 100644 cert.pem create mode 100644 examples/deno/wt_client_test.ts create mode 100644 mod/code.ts create mode 100644 mod/interface.ts create mode 100644 mod/utils.ts create mode 100644 utils/download_lib.ts diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 9862d01..b6f0ebe 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1 +1,15 @@ # Security Policy + +## Supported Versions + +The following version of the project are currently being supported with security updates. + +| Version | Supported | +| ------- | ------------------ | +| 0.1.x | :white_check_mark: | + + +## Reporting a Vulnerability + +To report a vulnerability, please open an issue with the label "security". + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c93b97..777205f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: - name: Configure git run: | git config --global core.symlinks true - git config --global fetch.parallel 16 + git config --global fetch.parallel 4 - name: Clone repository uses: actions/checkout@v3 with: @@ -114,15 +114,6 @@ jobs: rustc --version cargo --version deno --version - - name: Cache Cargo home - if: (matrix.job == 'test' && (matrix.profile == 'release' || matrix.profile == 'debug')) - uses: actions/cache@v3 - with: - path: | - ~/.cargo/registry/index - ~/.cargo/registry/cache - ~/.cargo/git/db - key: 16-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }} - name: Cache build output (main) uses: actions/cache@v3 diff --git a/.gitignore b/.gitignore index fb274c3..93c03d0 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,7 @@ Cargo.lock /target /Cargo.lock + + +.env +*.env \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 04853a1..712aeed 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "deno.enable": true, "deno.lint": true, "deno.unstable": true, - "deno.cacheOnSave": true, + "editor.formatOnSave": true, "rust-analyzer.linkedProjects": [ ".\\Cargo.toml" ] diff --git a/Cargo.toml b/Cargo.toml index 6339e08..b3d92cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,11 +38,16 @@ flume = "=0.11.0" num_cpus = "=1.16.0" once_cell = "=1.18.0" smol = "=1.3.0" -tokio = { version = "=1.32.0", default-features = false, features = ["rt", "rt-multi-thread", "macros"] } +tokio = { version = "=1.32.0", default-features = false, features = [ + "rt", + "rt-multi-thread", + "macros", +] } # wtransport = "0.1.4" # TODO: Replace this once the fix for arm is merged. -wtransport = { git = "https://github.com/hironichu/wtransport", branch = "master" } +wtransport = { git = "https://github.com/BiagioFesta/wtransport", branch = "master" } base64 = "=0.21.4" rcgen = "=0.11.1" ring = "=0.16.20" time = "=0.3.28" anyhow = "=1.0.75" +serde = { version = "=1.0", features = ["derive"] } diff --git a/cert.pem b/cert.pem new file mode 100644 index 0000000..59c14ff --- /dev/null +++ b/cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBozCCAUigAwIBAgIVANhzUl5zO49zrDG2wNoidtbafMyAMAoGCCqGSM49BAMC +MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA5MTkxMzU0NDRaFw0yMzA5Mjcx +MzU0NDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49 +AwEHA0IABFk/kl3aUTZAinswctqd8xF/pZBbrtrWzfxn3ODfQnlcDp1VFxMVzUzK +qsRbcz3kSt1P0ftnYXw0weJ0+BqghGejdzB1MBQGA1UdEQQNMAuCCWxvY2FsaG9z +dDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC +MB0GA1UdDgQWBBTYc1JeczuPc6wxtsDaInbW2nzMgDAPBgNVHRMBAf8EBTADAQH/ +MAoGCCqGSM49BAMCA0kAMEYCIQDW1Vnu8auqOwdhTMywpGI0WVYZjYJv9j9G7k6G +UVZeqgIhAO38ccY6omSYXO7x0v7zzEMgTTssAfCOfdcqtUzQnRSU +-----END CERTIFICATE----- diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts new file mode 100644 index 0000000..efeea99 --- /dev/null +++ b/examples/deno/wt_client_test.ts @@ -0,0 +1,29 @@ +import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts"; +import { getAnyEdgeLatest } from "npm:edge-paths"; + +Deno.test( + { name: "runEdgePuppeteer", ignore: Deno.build.os != "windows" }, + async () => { + const browser = await puppeteer.launch({ + headless: true, + defaultViewport: null, + executablePath: getAnyEdgeLatest(), + // no extension + args: [ + "--enable-automation", + "--disable-gpu", + "--disable-extensions", + ], + }); + //TODO(hironichu): Setup valid testing context for client + const page = await browser.newPage(); + + await page.goto("https://google.com", { + waitUntil: "networkidle2", + }); + + // await page.pdf({ path: "hn.pdf", format: "A4" }); + + await browser.close(); + }, +); diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 87407fc..a2a5283 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1 +1,3 @@ -console.info("TBD"); +Deno.test({ name: "Server startup" }, async () => { + // +}); diff --git a/examples/web_server/index.html b/examples/web_server/index.html index 25e07cd..6ba64f6 100644 --- a/examples/web_server/index.html +++ b/examples/web_server/index.html @@ -62,10 +62,10 @@

Server IP : HOST_IP

let int2 = 0; const encoder = new TextEncoder(); console.log("start sending") - while(1) { - if (int2 === 100_000) { - break ; - } + while (1) { + if (int2 === 100_000) { + break; + } await window.writeDDBN.write(encoder.encode(int2++)); } diff --git a/mod/client.ts b/mod/client.ts index e69de29..487695c 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -0,0 +1,35 @@ +if (import.meta.main) { + throw new Error("This module is not meant to be imported."); +} +import { EventEmitter } from "./deps.ts"; +import { symbols, WebTransportOptions } from "./interface.ts"; + +type WebTransportEvents = { + event: [MessageEvent]; + // Error Event + error: [ErrorEvent | string]; + // Close Event + close: [CloseEvent]; +}; +export class WebTransport extends EventEmitter { + #CONN_PTR: number | undefined; + #LIB: Deno.DynamicLibrary = window.WTLIB; + + constructor( + _client: string, + _options: typeof WebTransportOptions = WebTransportOptions, + ) { + super(); + } + + async connect(): Promise { + throw new Error("Method not implemented."); + } + async close() { + // return this; + } +} + +export default WebTransport; + +// Path: mod/client.ts diff --git a/mod/code.ts b/mod/code.ts new file mode 100644 index 0000000..878d7c3 --- /dev/null +++ b/mod/code.ts @@ -0,0 +1,59 @@ +export const C_TYPES = { + "PROC_OK": 0, + "PROC_ERR": 1, + "PROC_CONN_CLOSED": 2, + "PROC_CONN_TIMEOUT": 3, + "PROC_CONN_H3_ERROR": 4, + "PROC_CONN_QUIC_ERROR": 5, + "PROC_CONN_CLOSED_BY_APP": 6, + "PROC_CONN_CLOSED_LOCALLY": 7, + "PROC_CONN_TIMEDOUT": 8, +} as const; +export type C_TYPES = typeof C_TYPES[keyof typeof C_TYPES]; +//make so i can get the KEY from the VALUE of C_TYPES +const C_TYPES_KEYS = Object.keys(C_TYPES) as (keyof typeof C_TYPES)[]; + +export const FFI_CODES = { + 0: { + type: C_TYPES.PROC_OK, + name: C_TYPES_KEYS[C_TYPES.PROC_OK], + desc: "Success", + }, + 1: { + type: C_TYPES.PROC_ERR, + name: C_TYPES_KEYS[C_TYPES.PROC_ERR], + desc: "Generic error", + }, + 2: { + type: C_TYPES.PROC_CONN_CLOSED, + name: C_TYPES_KEYS[C_TYPES.PROC_CONN_CLOSED], + desc: "Connection closed by peer", + }, + 3: { + type: C_TYPES.PROC_CONN_TIMEOUT, + name: C_TYPES_KEYS[C_TYPES.PROC_CONN_TIMEOUT], + desc: "Connection timed out", + }, + 4: { + type: C_TYPES.PROC_CONN_H3_ERROR, + name: C_TYPES_KEYS[C_TYPES.PROC_CONN_H3_ERROR], + desc: "Connection closed by peer due to H3 error", + }, + 5: { + type: C_TYPES.PROC_CONN_QUIC_ERROR, + name: C_TYPES_KEYS[C_TYPES.PROC_CONN_QUIC_ERROR], + desc: "Connection closed by peer due to QUIC error", + }, + 6: { + type: C_TYPES.PROC_CONN_CLOSED_BY_APP, + name: C_TYPES_KEYS[C_TYPES.PROC_CONN_CLOSED_BY_APP], + desc: "Connection closed by application", + }, + 7: { + type: C_TYPES.PROC_CONN_CLOSED_LOCALLY, + name: C_TYPES_KEYS[C_TYPES.PROC_CONN_CLOSED_LOCALLY], + desc: "Connection closed locally", + }, +} as const; + +export type FFI_CODES = typeof FFI_CODES[keyof typeof FFI_CODES]; diff --git a/mod/crypto.ts b/mod/crypto.ts index 31d6555..9218fe4 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -1,3 +1,9 @@ +if (import.meta.main) { + throw new Error("This module is not meant to be imported."); +} +import { symbols } from "./interface.ts"; +import { join } from "https://deno.land/std@0.184.0/path/mod.ts"; +import { encodeBuf } from "./utils.ts"; export function base64ToArrayBuffer(base64: string) { const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); @@ -21,3 +27,64 @@ export function readCertFile(certpath: string) { const certBuffer = base64ToArrayBuffer(certBase64); return certBuffer; } + +export function GenerateCertKey( + domainStr: string, + start: number, + end: number, + lib: Deno.DynamicLibrary, +) { + if (start > end) throw new Error("Invalid date range"); + if (domainStr.length === 0) throw new Error("Invalid domain name"); + + const domain = encodeBuf(domainStr); + const certBUFF = new Uint8Array(1024); + const keyBUFF = new Uint8Array(1024); + const certLenPTR = new Uint32Array(1); + const keyLenPTR = new Uint32Array(1); + try { + const struct = lib.symbols.proc_gencert( + domain[0], + domain[1], + 2, + 10, + certBUFF, + certLenPTR, + keyBUFF, + keyLenPTR, + ); + if (!struct) { + throw new Error("Failed to generate certificate"); + } + + const cert = certBUFF.subarray(0, certLenPTR[0]); + const key = keyBUFF.subarray(0, keyLenPTR[0]); + + return [cert, key]; + } catch (e) { + console.error(e); + Deno.exit(1); + } +} + +/// Generate certificate and key file +/// GenerateCertKeyFile("localhost", 0, 10); +export function GenerateCertKeyFile( + domainStr: string, + start: number, + end: number, + path = "./certs/", + lib: Deno.DynamicLibrary, +) { + const [cert, key] = GenerateCertKey(domainStr, start, end, lib); + try { + Deno.writeFileSync(join(path, `${domainStr}.crt`), cert, { + mode: 0o444, + }); + Deno.writeFileSync(join(path, `${domainStr}.key`), key, { + mode: 0o444, + }); + } catch { + throw new Error("Failed to write certificate file"); + } +} diff --git a/mod/deno.ts b/mod/deno.ts index 140cc7e..37f2d64 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -1,14 +1,6 @@ -import { LIB } from "./lib.ts"; - +import LIB from "./lib.ts"; +import { encodeBuf } from "./utils.ts"; const lib = LIB; -export function encode(v: string | Uint8Array): Uint8Array { - if (typeof v !== "string") return v; - return new TextEncoder().encode(v); -} -export function encodeBuffPtr(v: string | Uint8Array): [Uint8Array, number] { - if (typeof v !== "string") return [v, v.length]; - return [new TextEncoder().encode(v), v.length]; -} const ptrstate = new Uint32Array(1); @@ -36,8 +28,8 @@ let serverPTR; let new_connection; const _decoder = new TextDecoder(); try { - const certpath = encodeBuffPtr("./certs/cert.pem"); - const keypath = encodeBuffPtr("./certs/key.pem"); + const certpath = encodeBuf("./certs/cert.pem"); + const keypath = encodeBuf("./certs/key.pem"); serverPTR = lib.symbols.proc_server_init( sender.pointer, diff --git a/mod/deps.ts b/mod/deps.ts index 22cc11a..eed783d 100644 --- a/mod/deps.ts +++ b/mod/deps.ts @@ -2,3 +2,5 @@ export { dlopen, type FetchOptions, } from "https://deno.land/x/plug@1.0.2/mod.ts"; + +export { EventEmitter } from "https://deno.land/x/event@2.0.1/mod.ts"; diff --git a/mod/interface.ts b/mod/interface.ts new file mode 100644 index 0000000..fb9bdaa --- /dev/null +++ b/mod/interface.ts @@ -0,0 +1,67 @@ +import WebTransport from "./client.ts"; +import { WebTransportServer } from "./server.ts"; + +export const WebTransportOptions = { + maxTimeout: 10, + keepAlive: 3, +} as const; +export const WebTransportServerOptions = { + maxTimeout: 10, + keepAlive: 3, +} as const; + +export const symbols = { + // Server symbols + proc_server_init: { + parameters: [ + "function", + "u16", + "bool", + "u64", + "u64", + "buffer", + "usize", + "buffer", + "usize", + ], + result: "pointer", + callback: true, + }, + proc_server_listen: { + parameters: ["pointer", "function"], + result: "pointer", + }, + proc_server_init_streams: { + parameters: ["pointer", "buffer", "usize"], + result: "void", + nonblocking: true, + }, + // Client symbols + // Shared symbols + proc_recv_datagram: { + parameters: ["pointer"], + result: "usize", + nonblocking: true, + }, + proc_send_datagram: { + parameters: ["pointer", "buffer", "usize"], + result: "void", + nonblocking: false, + }, + // Crypto symbols + proc_gencert: { + parameters: [ + "buffer", + "usize", + "i64", + "i64", + "buffer", + "buffer", + "buffer", + "buffer", + ], + result: "bool", + }, +} as const; + +//change the type of window so we add Webtransport and WebtransportServer diff --git a/mod/lib.ts b/mod/lib.ts index 9726b45..25d2b55 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -1,48 +1,13 @@ import { dlopen, FetchOptions } from "./deps.ts"; +import LIB_URL from "../utils/download_lib.ts"; +import { symbols } from "./interface.ts"; -const symbols = { - proc_server_init: { - parameters: [ - "function", - "u16", - "bool", - "u64", - "u64", - "buffer", - "usize", - "buffer", - "usize", - ], - result: "pointer", - callback: true, - }, - proc_server_listen: { - parameters: ["pointer", "function"], - result: "pointer", - }, - - proc_server_init_streams: { - parameters: ["pointer", "buffer", "usize"], - result: "void", - nonblocking: true, - }, - proc_recv_datagram: { - parameters: ["pointer"], - result: "usize", - nonblocking: true, - }, - proc_send_datagram: { - parameters: ["pointer", "buffer", "usize"], - result: "void", - nonblocking: false, - }, -} as const; - -//TODO(hironichu): Make this works from internet path const options: FetchOptions = { name: "webtransport", cache: "reloadAll", - url: "./target/release/", + url: LIB_URL!, }; -export const LIB = await dlopen(options, symbols); +export const LIB = async () => await dlopen(options, symbols); + +export default { LIB, symbols }; diff --git a/mod/mod.ts b/mod/mod.ts index e69de29..6649840 100644 --- a/mod/mod.ts +++ b/mod/mod.ts @@ -0,0 +1,24 @@ +import { LIB } from "./lib.ts"; + +window.WTLIB = await LIB(); + +import WebTransportServer from "./server.ts"; +import WebTransport from "./client.ts"; +import { symbols } from "./interface.ts"; + +window.WebTransport = WebTransport; +window.WebTransportServer = WebTransportServer; +declare global { + interface Window { + WebTransport: typeof WebTransport; + WebTransportServer: typeof WebTransportServer; + WTLIB: Deno.DynamicLibrary; + } +} + +export default { + WebTransportServer, + WebTransport, +}; + +// Path: mod/mod.ts diff --git a/mod/server.ts b/mod/server.ts index e69de29..c45589a 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -0,0 +1,34 @@ +if (import.meta.main) { + throw new Error("This module is not meant to be imported."); +} +import { EventEmitter } from "./deps.ts"; +import { WebTransportServerOptions } from "./interface.ts"; +//refuse improt of we dont come from main module + +type WebTransportServerEvents = { + event: [MessageEvent]; + // Error Event + error: [ErrorEvent | string]; + // Close Event + close: [CloseEvent]; +}; + +export class WebTransportServer extends EventEmitter { + constructor( + _port: number, + _options: typeof WebTransportServerOptions = WebTransportServerOptions, + ) { + super(); + } + + async listen() { + throw new Error("Method not implemented."); + } + + async close() { + // + } +} +export default WebTransportServer; + +// Path: mod/client.ts diff --git a/mod/utils.ts b/mod/utils.ts new file mode 100644 index 0000000..04e0b70 --- /dev/null +++ b/mod/utils.ts @@ -0,0 +1,15 @@ +if (import.meta.main) { + throw new Error("This module is not meant to be imported."); +} +export const encoder = new TextEncoder(); + +export function encodeBuf(v: string | Uint8Array): [Uint8Array, number] { + if (typeof v !== "string") return [v, v.byteLength]; + const encoded = encoder.encode(v); + return [encoded, encoded.byteLength]; +} + +export default { + encoder, + encodeBuf, +}; diff --git a/src/certificate.rs b/src/certificate.rs index b393942..b708a72 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -1,6 +1,4 @@ use anyhow::Result; -use base64::engine::general_purpose::STANDARD as Base64Engine; -use base64::Engine; use rcgen::BasicConstraints; use rcgen::CertificateParams; use rcgen::DistinguishedName; @@ -9,28 +7,22 @@ use rcgen::ExtendedKeyUsagePurpose; use rcgen::IsCa; use rcgen::KeyPair; use rcgen::PKCS_ECDSA_P256_SHA256; -use ring::digest::digest; -use ring::digest::SHA256; + use time::Duration; use time::OffsetDateTime; -use std::ffi::c_char; -#[derive(Clone)] + pub struct SelfCertificate { /// DER certificate. - pub certificate: Vec, - - /// DER private key. - pub key: Vec, - - /// Base64 SHA256 public key. - pub fingerprint: String, + pub certificate: rcgen::Certificate, } /// Generates a self-signed certificate for WebTransport connections. -pub fn generate_certificate>(common_name: S, start: OffsetDateTime, end: OffsetDateTime) -> Result { +pub fn generate_certificate>( + common_name: S, + start: OffsetDateTime, + end: OffsetDateTime, +) -> Result { let keypair = KeyPair::generate(&PKCS_ECDSA_P256_SHA256)?; - let digest = digest(&SHA256, &keypair.public_key_der()); - let fingerprint = Base64Engine.encode(digest); let mut dname = DistinguishedName::new(); dname.push(DnType::CommonName, common_name.as_ref()); @@ -41,31 +33,50 @@ pub fn generate_certificate>(common_name: S, start: OffsetDateTime cert_params.key_pair = Some(keypair); cert_params.not_before = start; cert_params.not_after = end; - cert_params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ServerAuth, ExtendedKeyUsagePurpose::ClientAuth]; + cert_params.extended_key_usages = vec![ + ExtendedKeyUsagePurpose::ServerAuth, + ExtendedKeyUsagePurpose::ClientAuth, + ]; cert_params.key_usages = vec![ - rcgen::KeyUsagePurpose::DigitalSignature, - rcgen::KeyUsagePurpose::KeyCertSign, - ]; - cert_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + rcgen::KeyUsagePurpose::DigitalSignature, + rcgen::KeyUsagePurpose::KeyCertSign, + ]; + cert_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); let certificate = rcgen::Certificate::from_params(cert_params)?; - Ok(SelfCertificate { - certificate: certificate.serialize_der()?, - key: certificate.serialize_private_key_der(), - fingerprint, - }) + Ok(SelfCertificate { certificate }) } - #[no_mangle] -pub extern "C" fn proc_gencert(buffpath: *mut u8) -> usize { - //get the underlying buffer and use it to return the path to the cert - let path = unsafe { std::ffi::CStr::from_ptr(buffpath as *const c_char) }; - let path = path.to_str().unwrap(); - let start = OffsetDateTime::now_utc().checked_add(Duration::days(2)).unwrap(); - let cert = generate_certificate("localhost", start, start).unwrap(); - std::fs::write(format!("{}/cert.pem", path), cert.certificate).unwrap(); - std::fs::write(format!("{}/key.pem", path), cert.key).unwrap(); - path.len() -} \ No newline at end of file +pub unsafe extern "C" fn proc_gencert( + domain_buf: *const u8, + domain_buf_len: usize, + start: i64, + end: i64, + cert_buf: *mut u8, + cert_buf_len: *mut usize, + key_buf: *mut u8, + key_buf_len: *mut usize, +) -> bool { + let buf = ::std::slice::from_raw_parts(domain_buf, domain_buf_len); + let domain = String::from_utf8_lossy(buf); + let start = OffsetDateTime::now_utc() + .checked_add(Duration::days(start)) + .unwrap(); + let end = OffsetDateTime::now_utc() + .checked_add(Duration::days(end)) + .unwrap(); + let cert = generate_certificate(domain, start, end).unwrap(); + let certbuffer = cert.certificate.serialize_pem().unwrap(); + let keybuff = cert.certificate.serialize_private_key_pem(); + + let cert_len = certbuffer.len(); + let key_len = keybuff.len(); + + ::std::slice::from_raw_parts_mut(cert_buf, cert_len).copy_from_slice(certbuffer.as_bytes()); + ::std::slice::from_raw_parts_mut(key_buf, key_len).copy_from_slice(keybuff.as_bytes()); + *cert_buf_len = cert_len; + *key_buf_len = key_len; + true +} diff --git a/src/server.rs b/src/server.rs index 8ba1c8e..c212d73 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,8 +1,8 @@ -use std::{slice::from_raw_parts_mut, path::Path, time::Duration}; +use std::{path::Path, slice::from_raw_parts_mut, time::Duration}; use tokio::runtime::Runtime; -use wtransport::{ Endpoint, endpoint, ServerConfig, tls::Certificate}; +use wtransport::{endpoint, tls::Certificate, Endpoint, ServerConfig}; -use crate::{RUNTIME, CONN_FN, SEND_FN, connection::Conn, executor}; +use crate::{connection::Conn, executor, CONN_FN, RUNTIME, SEND_FN}; pub struct WebTransportServer { pub server: Option>, @@ -72,7 +72,6 @@ impl WebTransportServer { } } - #[no_mangle] pub unsafe extern "C" fn proc_server_init( send_func: Option, @@ -100,12 +99,17 @@ pub unsafe extern "C" fn proc_server_init( } else { Some(Duration::from_secs(keepalive)) }; + let timeout = if timeout == 0 { + None + } else { + Some(Duration::from_secs(timeout)) + }; //print the paths for debug let config = ServerConfig::builder() .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual, port) .with_certificate(certificates) .keep_alive_interval(keepalive) - .max_idle_timeout(Some(Duration::from_secs(timeout))) + .max_idle_timeout(timeout) .unwrap() .allow_migration(migration) .build(); @@ -145,7 +149,7 @@ pub unsafe extern "C" fn proc_server_init_streams( let sender = client.datagram_ch_sender.clone(); client.buffer = Some(from_raw_parts_mut(buffer, buflen)); - + executor::spawn(async move { loop { tokio::select! { @@ -195,21 +199,28 @@ pub unsafe extern "C" fn proc_server_init_streams( match stream { Ok(dgram) => sender.send_async(dgram).await.unwrap(), _ => { - client.conn.closed().await; - //TODO(hironichu): Send action to Deno to free the pointer and buffer - return ; + client.conn.closed().await; + //TODO(hironichu): Send action to Deno to free the pointer and buffer + // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); + return ; } } }, } } - }).detach(); + }) + .detach(); } //free all above once #[no_mangle] -pub unsafe extern "C" fn free_all_server(_a: *mut WebTransportServer, _b: *mut Conn, _c: *mut Runtime) {} +pub unsafe extern "C" fn free_all_server( + _a: *mut WebTransportServer, + _b: *mut Conn, + _c: *mut Runtime, +) { +} #[no_mangle] pub unsafe extern "C" fn free_server(_: *mut WebTransportServer) {} diff --git a/utils/clean.js b/utils/clean.js index d35fe85..c459be9 100644 --- a/utils/clean.js +++ b/utils/clean.js @@ -4,9 +4,8 @@ if (import.meta.main) { const rule = /^(.*)\.so$|^(.*)\.dll$|^(.*)\.dylib$/; const files = Deno.readDirSync("./dist"); for (const file of files) { - console.log(rule.test(file.name)); if (rule.test(file.name)) { - Deno.removeSync("./dist/" + file.name); + Deno.removeSync("./dist/" + file.name); } } console.info(`Cleaned up ./dist`); diff --git a/utils/download_lib.ts b/utils/download_lib.ts new file mode 100644 index 0000000..b8ec7f6 --- /dev/null +++ b/utils/download_lib.ts @@ -0,0 +1,66 @@ +import "https://deno.land/std@0.201.0/dotenv/load.ts"; +const LIB_NAME = "webtransport"; +let LIB_URL: URL | undefined; + +if (!Deno.env.get("DEVELOPMENT")) { + if (!Deno.env.get("DENO_AUTH_TOKENS")) { + throw new Error("Please set DENO_AUTH_TOKENS to your auth tokens"); + } + const data = await fetch( + `https://api.github.com/repos/hironichu/${LIB_NAME}/releases`, + { + headers: { + Authorization: `token ${ + Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0].split( + ":", + )[1] + }`, + Accept: "application/vnd.github.v3+json", + }, + }, + ); + + const json = await data.json() as Array<{ + assets: { + [key: string]: Array<{ + url: string; + }>; + }; + }>; + if (json.length === 0) { + throw new Error("No release found"); + } + + switch (Deno.build.os) { + case "windows": + LIB_URL = new URL( + // json[0].assets.filter((item: { name: string }) => + // item.name.endsWith(".dll") + // )[0].url, + json[0].assets[`${LIB_NAME}.dll`][0].url, + ); + break; + case "linux": + { + let filename = LIB_NAME; + if (Deno.build.arch === "aarch64") { + filename = `lib${LIB_NAME}_aarch64.so`; + } else { + filename = `lib${LIB_NAME}.so`; + } + LIB_URL = new URL( + json[0].assets[filename][0].url, + ); + } + break; + case "darwin": + LIB_URL = new URL( + json[0].assets[`${LIB_NAME}.dylib`][0].url, + ); + break; + } + LIB_URL!.username = Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]!; +} + +LIB_URL = LIB_URL ?? new URL("../target/release/", import.meta.url); +export default LIB_URL; From 7c6b39295e79af5d249c26b741a9b4e85d1405e6 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 18:06:42 +0200 Subject: [PATCH 27/72] Removed trash --- cert.pem | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 cert.pem diff --git a/cert.pem b/cert.pem deleted file mode 100644 index 59c14ff..0000000 --- a/cert.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBozCCAUigAwIBAgIVANhzUl5zO49zrDG2wNoidtbafMyAMAoGCCqGSM49BAMC -MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMzA5MTkxMzU0NDRaFw0yMzA5Mjcx -MzU0NDRaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDBZMBMGByqGSM49AgEGCCqGSM49 -AwEHA0IABFk/kl3aUTZAinswctqd8xF/pZBbrtrWzfxn3ODfQnlcDp1VFxMVzUzK -qsRbcz3kSt1P0ftnYXw0weJ0+BqghGejdzB1MBQGA1UdEQQNMAuCCWxvY2FsaG9z -dDAOBgNVHQ8BAf8EBAMCAoQwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC -MB0GA1UdDgQWBBTYc1JeczuPc6wxtsDaInbW2nzMgDAPBgNVHRMBAf8EBTADAQH/ -MAoGCCqGSM49BAMCA0kAMEYCIQDW1Vnu8auqOwdhTMywpGI0WVYZjYJv9j9G7k6G -UVZeqgIhAO38ccY6omSYXO7x0v7zzEMgTTssAfCOfdcqtUzQnRSU ------END CERTIFICATE----- From 2557fb333a915d759910c0d1746645058a030b23 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 18:08:15 +0200 Subject: [PATCH 28/72] fmt --- deno.jsonc | 5 ++--- mod/client.ts | 2 +- mod/interface.ts | 3 --- mod/server.ts | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index 82b88d6..c7c98b4 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,7 +1,6 @@ { - "test": { - - }, + "test": { + }, "tasks": { //Examples "example:web": "deno run -A ./examples/web_server/web.js", diff --git a/mod/client.ts b/mod/client.ts index 487695c..294bdba 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -22,7 +22,7 @@ export class WebTransport extends EventEmitter { super(); } - async connect(): Promise { + connect(): Promise { throw new Error("Method not implemented."); } async close() { diff --git a/mod/interface.ts b/mod/interface.ts index fb9bdaa..3bf0d96 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -1,6 +1,3 @@ -import WebTransport from "./client.ts"; -import { WebTransportServer } from "./server.ts"; - export const WebTransportOptions = { maxTimeout: 10, keepAlive: 3, diff --git a/mod/server.ts b/mod/server.ts index c45589a..c1accb6 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -21,7 +21,7 @@ export class WebTransportServer extends EventEmitter { super(); } - async listen() { + listen() { throw new Error("Method not implemented."); } From 57182e4c10832ef9dbb8d49665f60f98822e95d2 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 20:29:34 +0200 Subject: [PATCH 29/72] Working code! --- examples/deno/wt_server.ts | 19 ++++ mod/client.ts | 2 +- mod/connection.ts | 68 ++++++++++++++ mod/crypto.ts | 16 ++-- mod/deno.ts | 176 ++++++++++++++++++------------------- mod/interface.ts | 25 +++++- mod/mod.ts | 31 ++++--- mod/server.ts | 156 ++++++++++++++++++++++++++++++-- src/server.rs | 7 ++ 9 files changed, 384 insertions(+), 116 deletions(-) create mode 100644 mod/connection.ts diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index 8983ed9..ca82118 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -1 +1,20 @@ //TO BE IMPLEMENTED +import "../../mod/mod.ts"; + +const server = new Deno.WebTransportServer(4433, { + certFile: "./certs/cert.pem", + keyFile: "./certs/key.pem", + maxTimeout: 10, + keepAlive: 3, +}); +console.log("Server created"); +server.on("listening", (e) => { + console.log("Listening OUT", e); +}); + +server.on("connection", async (connection) => { + console.log("Connection "); + for await (const read of connection.datagrams.readable) { + console.log(read); + } +}); diff --git a/mod/client.ts b/mod/client.ts index 294bdba..b3c5440 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -13,7 +13,7 @@ type WebTransportEvents = { }; export class WebTransport extends EventEmitter { #CONN_PTR: number | undefined; - #LIB: Deno.DynamicLibrary = window.WTLIB; + #LIB: Deno.DynamicLibrary = Deno.WTLIB; constructor( _client: string, diff --git a/mod/connection.ts b/mod/connection.ts new file mode 100644 index 0000000..31011b0 --- /dev/null +++ b/mod/connection.ts @@ -0,0 +1,68 @@ +export class Datagram { + #READ_BUFFER: Uint8Array; + constructor( + private connection: WebTransportConnection, + _buffer: Uint8Array, + ) { + this.#READ_BUFFER = _buffer; + } + get writable() { + const connection = this.connection; + return new WritableStream({ + write(chunk) { + try { + Deno.WTLIB.symbols.proc_send_datagram( + connection.pointer!, + chunk, + chunk.byteLength, + ); + } catch (e) { + console.error(e); + } + }, + abort() { + console.error("[Error] Write aborted"); + }, + }); + } + get readable() { + const connection = this.connection; + const buffer = this.#READ_BUFFER; + const StreamBuffer = new ReadableStream({ + async pull(controller) { + try { + const nread = await Deno.WTLIB.symbols.proc_recv_datagram( + connection.pointer!, + ); + if (nread > 0) { + controller.enqueue( + buffer.subarray(0, nread as number), + ); + } + } catch (e) { + controller.error(e); + } + }, + cancel() { + console.error("[Error] Stream cancelled"); + }, + }); + return StreamBuffer; + } +} +export class WebTransportConnection { + #CONN_PTR: Deno.PointerValue; + public datagrams: Datagram; + constructor( + pointer: Deno.PointerValue, + buffer: Uint8Array, + ) { + this.#CONN_PTR = pointer; + this.datagrams = new Datagram(this, buffer); + } + + //TODO: add the rest of the methods for uni and bi streams + get pointer() { + return this.#CONN_PTR; + } +} diff --git a/mod/crypto.ts b/mod/crypto.ts index 9218fe4..17998ff 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -1,7 +1,6 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } -import { symbols } from "./interface.ts"; import { join } from "https://deno.land/std@0.184.0/path/mod.ts"; import { encodeBuf } from "./utils.ts"; export function base64ToArrayBuffer(base64: string) { @@ -32,7 +31,6 @@ export function GenerateCertKey( domainStr: string, start: number, end: number, - lib: Deno.DynamicLibrary, ) { if (start > end) throw new Error("Invalid date range"); if (domainStr.length === 0) throw new Error("Invalid domain name"); @@ -43,7 +41,7 @@ export function GenerateCertKey( const certLenPTR = new Uint32Array(1); const keyLenPTR = new Uint32Array(1); try { - const struct = lib.symbols.proc_gencert( + const struct = window.WTLIB.symbols.proc_gencert( domain[0], domain[1], 2, @@ -74,16 +72,20 @@ export function GenerateCertKeyFile( start: number, end: number, path = "./certs/", - lib: Deno.DynamicLibrary, ) { - const [cert, key] = GenerateCertKey(domainStr, start, end, lib); + const [cert, key] = GenerateCertKey(domainStr, start, end); try { - Deno.writeFileSync(join(path, `${domainStr}.crt`), cert, { + const certpath = join(path, `${domainStr}.crt`); + const keypath = join(path, `${domainStr}.key`); + Deno.writeFileSync(certpath, cert, { mode: 0o444, + createNew: true, }); - Deno.writeFileSync(join(path, `${domainStr}.key`), key, { + Deno.writeFileSync(keypath, key, { mode: 0o444, + createNew: true, }); + return [certpath, keypath]; } catch { throw new Error("Failed to write certificate file"); } diff --git a/mod/deno.ts b/mod/deno.ts index 37f2d64..7ce6748 100644 --- a/mod/deno.ts +++ b/mod/deno.ts @@ -1,93 +1,93 @@ -import LIB from "./lib.ts"; -import { encodeBuf } from "./utils.ts"; -const lib = LIB; +// import LIB from "./lib.ts"; +// import { encodeBuf } from "./utils.ts"; +// const lib = LIB; -const ptrstate = new Uint32Array(1); +// const ptrstate = new Uint32Array(1); -const sender = new Deno.UnsafeCallback( - { - parameters: ["u32", "pointer", "u32"], - result: "void", - }, - (_code: unknown | number, buffer, buflen) => { - const code = _code as typeof ptrstate[0]; - console.log(code); - if (buflen < 0) { - return; - } - const pointer = Deno.UnsafePointerView.getArrayBuffer( - buffer as unknown as NonNullable, - buflen, - ); - console.log(pointer); - }, -); +// const sender = new Deno.UnsafeCallback( +// { +// parameters: ["u32", "pointer", "u32"], +// result: "void", +// }, +// (_code: unknown | number, buffer, buflen) => { +// const code = _code as typeof ptrstate[0]; +// console.log(code); +// if (buflen < 0) { +// return; +// } +// const pointer = Deno.UnsafePointerView.getArrayBuffer( +// buffer as unknown as NonNullable, +// buflen, +// ); +// console.log(pointer); +// }, +// ); -sender.ref(); -let serverPTR; -let new_connection; -const _decoder = new TextDecoder(); -try { - const certpath = encodeBuf("./certs/cert.pem"); - const keypath = encodeBuf("./certs/key.pem"); +// sender.ref(); +// let serverPTR; +// let new_connection; +// const _decoder = new TextDecoder(); +// try { +// const certpath = encodeBuf("./certs/cert.pem"); +// const keypath = encodeBuf("./certs/key.pem"); - serverPTR = lib.symbols.proc_server_init( - sender.pointer, - 4433, - true, - 3, - 20, - certpath[0], - certpath[1], - keypath[0], - keypath[1], - ); - new_connection = new Deno.UnsafeCallback( - { - parameters: ["pointer"], - result: "void", - }, - async (client) => { - console.log("DENO : New connection"); - const DBufferB = new Uint8Array(65536); +// serverPTR = lib.symbols.proc_server_init( +// sender.pointer, +// 4433, +// true, +// 3, +// 20, +// certpath[0], +// certpath[1], +// keypath[0], +// keypath[1], +// ); +// new_connection = new Deno.UnsafeCallback( +// { +// parameters: ["pointer"], +// result: "void", +// }, +// async (client) => { +// console.log("DENO : New connection"); +// const DBufferB = new Uint8Array(65536); - lib.symbols.proc_server_init_streams( - client, - DBufferB, - DBufferB.byteLength, - ); - const StreamBuffer = new ReadableStream({ - async pull(controller) { - try { - const nread = await lib.symbols.proc_recv_datagram( - client, - ); - if (nread > 0) { - controller.enqueue( - DBufferB.subarray(0, nread as number), - ); - } - } catch (e) { - controller.error(e); - } - }, - cancel() { - console.error("[Error] Stream cancelled"); - }, - }); - for await (const chunk of StreamBuffer) { - // const msg = parseInt(decoder.decode(chunk)); - // console.log(decoder.decode(chunk)); - lib.symbols.proc_send_datagram(client, chunk, chunk.length); - } - }, - ); - new_connection.ref(); - lib.symbols.proc_server_listen(serverPTR, new_connection.pointer); - console.info("Server is running"); -} catch (e) { - sender.unref(); - new_connection?.unref(); - console.error(e); -} -if (!serverPTR) throw new Error("Server is not running"); +// lib.symbols.proc_server_init_streams( +// client, +// DBufferB, +// DBufferB.byteLength, +// ); +// const StreamBuffer = new ReadableStream({ +// async pull(controller) { +// try { +// const nread = await lib.symbols.proc_recv_datagram( +// client, +// ); +// if (nread > 0) { +// controller.enqueue( +// DBufferB.subarray(0, nread as number), +// ); +// } +// } catch (e) { +// controller.error(e); +// } +// }, +// cancel() { +// console.error("[Error] Stream cancelled"); +// }, +// }); +// for await (const chunk of StreamBuffer) { +// // const msg = parseInt(decoder.decode(chunk)); +// // console.log(decoder.decode(chunk)); +// lib.symbols.proc_send_datagram(client, chunk, chunk.length); +// } +// }, +// ); +// new_connection.ref(); +// lib.symbols.proc_server_listen(serverPTR, new_connection.pointer); +// console.info("Server is running"); +// } catch (e) { +// sender.unref(); +// new_connection?.unref(); +// console.error(e); +// } +// if (!serverPTR) throw new Error("Server is not running"); diff --git a/mod/interface.ts b/mod/interface.ts index 3bf0d96..ed91a26 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -2,10 +2,28 @@ export const WebTransportOptions = { maxTimeout: 10, keepAlive: 3, } as const; + +export const CertificateOptions = { + certFile: "", + keyFile: "", +}; + +export const CertificateGenParams = { + domain: Deno.hostname(), + notBefore: 0, + notAfter: 10, +}; +type CertificateOptions = + & typeof CertificateOptions + & typeof CertificateGenParams; + export const WebTransportServerOptions = { maxTimeout: 10, keepAlive: 3, -} as const; +}; +export type WebTransportServerOptions = + & typeof WebTransportServerOptions + & Partial; export const symbols = { // Server symbols @@ -27,12 +45,17 @@ export const symbols = { proc_server_listen: { parameters: ["pointer", "function"], result: "pointer", + callback: true, }, proc_server_init_streams: { parameters: ["pointer", "buffer", "usize"], result: "void", nonblocking: true, }, + proc_server_close: { + parameters: ["pointer"], + result: "void", + }, // Client symbols // Shared symbols proc_recv_datagram: { diff --git a/mod/mod.ts b/mod/mod.ts index 6649840..455fc02 100644 --- a/mod/mod.ts +++ b/mod/mod.ts @@ -1,24 +1,31 @@ +import { symbols } from "./interface.ts"; import { LIB } from "./lib.ts"; window.WTLIB = await LIB(); +Deno.WTLIB = window.WTLIB; +import { WebTransportServer as WtServer } from "./server.ts"; +import { WebTransport as WtClient } from "./client.ts"; -import WebTransportServer from "./server.ts"; -import WebTransport from "./client.ts"; -import { symbols } from "./interface.ts"; +// Deno.WebTransport = WebTransport; +// Deno.WebTransportServer = WebTransportServer; -window.WebTransport = WebTransport; -window.WebTransportServer = WebTransportServer; declare global { - interface Window { - WebTransport: typeof WebTransport; - WebTransportServer: typeof WebTransportServer; + namespace Deno { + export class WebTransport extends WtClient {} + export class WebTransportServer extends WtServer {} + export let WTLIB: Deno.DynamicLibrary; + } + export interface Window { + WebTransport: typeof Deno.WebTransport; + WebTransportServer: typeof Deno.WebTransportServer; WTLIB: Deno.DynamicLibrary; } } -export default { - WebTransportServer, - WebTransport, -}; +Deno.WebTransport = WtClient; +Deno.WebTransportServer = WtServer; +window.WebTransport = WtClient; +window.WebTransportServer = WtServer; +export { WtClient, WtServer }; // Path: mod/mod.ts diff --git a/mod/server.ts b/mod/server.ts index c1accb6..8ffce86 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -1,11 +1,18 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } +import { WebTransportConnection } from "./connection.ts"; +import { GenerateCertKeyFile } from "./crypto.ts"; import { EventEmitter } from "./deps.ts"; -import { WebTransportServerOptions } from "./interface.ts"; -//refuse improt of we dont come from main module +import { + type WebTransportServerOptions, + WebTransportServerOptions as ServerOpts, +} from "./interface.ts"; +import { encodeBuf } from "./utils.ts"; type WebTransportServerEvents = { + listening: [Event]; + connection: [WebTransportConnection]; event: [MessageEvent]; // Error Event error: [ErrorEvent | string]; @@ -14,19 +21,154 @@ type WebTransportServerEvents = { }; export class WebTransportServer extends EventEmitter { + public connections: Map = new Map(); + #SRV_PTR: Deno.PointerValue | undefined; + + #STATE_PTR = new Uint32Array(1); + #NOTIFY_PTR = new Deno.UnsafeCallback( + { + parameters: ["u32", "pointer", "u32"], + result: "void", + }, + this.notify.bind(this), + ); + #CONNECTION_CB = new Deno.UnsafeCallback( + { + parameters: ["pointer"], + result: "void", + }, + this.connection.bind(this), + ); constructor( _port: number, - _options: typeof WebTransportServerOptions = WebTransportServerOptions, + _options: WebTransportServerOptions = ServerOpts, ) { super(); + const [certificate, key] = this.checkArgs(_options); + + const certbuf = encodeBuf(certificate); + const keybuf = encodeBuf(key); + + this.#SRV_PTR = window.WTLIB.symbols.proc_server_init( + this.#NOTIFY_PTR.pointer, + _port, + true, + _options.keepAlive, + _options.maxTimeout, + certbuf[0], + certbuf[1], + keybuf[0], + keybuf[1], + ); + + if (!this.#SRV_PTR) { + throw new Error("Failed to initialize server"); + } + + window.WTLIB.symbols.proc_server_listen( + this.#SRV_PTR, + this.#CONNECTION_CB.pointer, + ); + this.#NOTIFY_PTR.ref(); + this.#CONNECTION_CB.ref(); + + this.emit("listening", new Event("ready")); } + /** + * @callback connection + * @param {Deno.PointerValue} client + * @returns {void} + * @description This function is called when a new connection is received from the server + */ + private connection(client: Deno.PointerValue) { + const CONN_BUFFER = new Uint8Array(65536); + window.WTLIB.symbols.proc_server_init_streams( + client, + CONN_BUFFER, + CONN_BUFFER.byteLength, + ); + //Setting up the stream for the new connection + const conn = new WebTransportConnection( + client, + CONN_BUFFER, + ); - listen() { - throw new Error("Method not implemented."); + this.connections.set( + this.connections.size, + conn, + ); + this.emit("connection", conn); } + /** + * @callback notify + * @param {number} code + * @param {Deno.PointerValue} buffer + * @param {number} buflen + * @returns {void} + * + * @description This function is called when a new event is received from the server + */ + private notify( + _code: unknown | number, + buffer: Deno.PointerValue, + buflen: number, + ) { + const code = _code as bigint; + console.log(code); + if (buflen < 0) { + return; + } + const pointer = Deno.UnsafePointerView.getArrayBuffer( + buffer as unknown as NonNullable, + buflen, + ); + const event = new MessageEvent("message", { + data: pointer, + }); - async close() { - // + //TODO(hironichu): Implement Error/event catching from rust to free the memory once a connection drop or if something else happens. + this.emit("event", event); + } + + close() { + this.#NOTIFY_PTR.unref(); + this.#CONNECTION_CB.unref(); + if (this.#SRV_PTR) { + Deno.WTLIB.symbols.proc_server_close(this.#SRV_PTR); + } + this.emit("close", new CloseEvent("close")); + } + private checkArgs(_options: WebTransportServerOptions) { + if ( + ((!_options.certFile || _options.certFile.length == 0) && + (!_options.keyFile || _options.keyFile.length == 0)) && + (!_options.notAfter && !_options.notBefore && !_options.domain) + ) { + throw new TypeError( + "Missing necessary parameters: certFile, keyFile or notAfter, notBefore to generate a new certificate", + ); + } + let certificate = ""; + let key = ""; + if (_options.certFile && _options.keyFile) { + Deno.statSync(_options.certFile); + Deno.statSync(_options.keyFile); + // + certificate = _options.certFile; + key = _options.keyFile; + } + if ( + ((!_options.certFile || _options.certFile.length == 0) && + (!_options.keyFile || _options.keyFile.length == 0)) && + (_options.notAfter && _options.notBefore && _options.domain) + ) { + [certificate, key] = GenerateCertKeyFile( + _options.domain, + _options.notBefore, + _options.notAfter, + ); + } + return [certificate, key]; } } export default WebTransportServer; diff --git a/src/server.rs b/src/server.rs index c212d73..b95d908 100644 --- a/src/server.rs +++ b/src/server.rs @@ -213,6 +213,13 @@ pub unsafe extern "C" fn proc_server_init_streams( .detach(); } +#[no_mangle] +pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) { + assert!(!server_ptr.is_null()); + let server = &mut *server_ptr; + server.state = Some(false); +} + //free all above once #[no_mangle] pub unsafe extern "C" fn free_all_server( From 6f7fe2ef6eab724ce42e01b90d1c1614e7def923 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 22:12:26 +0200 Subject: [PATCH 30/72] Added working client (first try btw) --- Cargo.toml | 5 +- examples/deno/wt_client.ts | 16 +++ examples/web_server/index.html | 1 + mod/client.ts | 158 ++++++++++++++++++++-- mod/connection.ts | 25 +++- mod/interface.ts | 52 ++++++-- mod/lib.ts | 2 +- mod/server.ts | 1 + src/client.rs | 236 +++++++++++++++++++++++---------- src/connection.rs | 37 +++--- src/lib.rs | 10 +- src/server.rs | 8 +- src/shared.rs | 54 ++++++-- 13 files changed, 470 insertions(+), 135 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b3d92cb..fa5e159 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,8 +44,9 @@ tokio = { version = "=1.32.0", default-features = false, features = [ "macros", ] } # wtransport = "0.1.4" # TODO: Replace this once the fix for arm is merged. -wtransport = { git = "https://github.com/BiagioFesta/wtransport", branch = "master" } -base64 = "=0.21.4" +wtransport = { git = "https://github.com/BiagioFesta/wtransport", branch = "master", features = [ + "dangerous-configuration", +] } rcgen = "=0.11.1" ring = "=0.16.20" time = "=0.3.28" diff --git a/examples/deno/wt_client.ts b/examples/deno/wt_client.ts index 8983ed9..3cc89bd 100644 --- a/examples/deno/wt_client.ts +++ b/examples/deno/wt_client.ts @@ -1 +1,17 @@ //TO BE IMPLEMENTED +import "../../mod/mod.ts"; + +const client = new Deno.WebTransport("https://localhost:4433", { + validateCertificate: false, + maxTimeout: 10, + keepAlive: 3, +}); + +await client.ready; +console.log("Client connected"); +const writer = client.datagrams.writable.getWriter(); +writer?.write(new Uint8Array([1, 2, 3, 4, 5])); +writer?.write(new Uint8Array([1, 2, 3, 4, 5])); +writer?.write(new Uint8Array([1, 2, 3, 4, 5])); +writer?.write(new Uint8Array([1, 2, 3, 4, 5])); +writer?.write(new Uint8Array([1, 2, 3, 4, 5])); diff --git a/examples/web_server/index.html b/examples/web_server/index.html index 6ba64f6..812740e 100644 --- a/examples/web_server/index.html +++ b/examples/web_server/index.html @@ -51,6 +51,7 @@

Server IP : HOST_IP

}) await webtransport.ready; window.serveurDEMO = webtransport; + console.log(webtransport) webtransport.onstatechange = (event) => { console.log("EVENT : ", event); } diff --git a/mod/client.ts b/mod/client.ts index b3c5440..71695b8 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -1,10 +1,22 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } +import { + WebTransportConnection, + WebTransportDatagramDuplexStream, +} from "./connection.ts"; import { EventEmitter } from "./deps.ts"; -import { symbols, WebTransportOptions } from "./interface.ts"; +import { + symbols, + type WebTransportOptions, + WebTransportOptions as ServerOpts, +} from "./interface.ts"; +import { encodeBuf } from "./utils.ts"; type WebTransportEvents = { + connected: [WebTransportConnection]; + ready: [WebTransportConnection]; + // Other Event event: [MessageEvent]; // Error Event error: [ErrorEvent | string]; @@ -12,21 +24,149 @@ type WebTransportEvents = { close: [CloseEvent]; }; export class WebTransport extends EventEmitter { - #CONN_PTR: number | undefined; - #LIB: Deno.DynamicLibrary = Deno.WTLIB; + #CONN_PTR: Deno.PointerValue | undefined; + #STATE_PTR = new Uint32Array(1); + public datagrams!: WebTransportDatagramDuplexStream; + conn?: WebTransportConnection; + #NOTIFY_PTR = new Deno.UnsafeCallback( + { + parameters: ["u32", "pointer", "u32"], + result: "void", + }, + this.notify.bind(this), + ); + #CONNECTION_CB = new Deno.UnsafeCallback( + { + parameters: ["pointer"], + result: "void", + }, + this.connection.bind(this), + ); constructor( - _client: string, - _options: typeof WebTransportOptions = WebTransportOptions, + _client: URL | string, + _options: typeof WebTransportOptions = ServerOpts, ) { super(); + const [certificate, key] = this.checkArgs(_options); + + const certbuf = encodeBuf(certificate); + const keybuf = encodeBuf(key); + this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( + this.#NOTIFY_PTR.pointer, + _options.keepAlive, + _options.maxTimeout, + _options.validateCertificate, + certbuf[0], + certbuf[1], + keybuf[0], + keybuf[1], + ); + + if (!this.#CONN_PTR) { + throw new Error("Failed to initialize client"); + } + const addr = encodeBuf(_client.toString()); + window.WTLIB.symbols.proc_client_connect( + this.#CONN_PTR, + this.#CONNECTION_CB.pointer, + addr[0], + addr[1], + ); + this.#NOTIFY_PTR.ref(); + this.#CONNECTION_CB.ref(); } + /** + * @callback connection + * @param {Deno.PointerValue} client + * @returns {void} + * @description This function is called when a new connection is received from the server + */ + private connection(client: Deno.PointerValue) { + const CONN_BUFFER = new Uint8Array(65536); + // + window.WTLIB.symbols.proc_client_init_streams( + client, + CONN_BUFFER, + CONN_BUFFER.byteLength, + ); + //Setting up the stream for the new connection + const conn = new WebTransportConnection( + client, + CONN_BUFFER, + ); + + this.datagrams = new WebTransportDatagramDuplexStream( + conn, + CONN_BUFFER, + ); + this.emit("ready", conn); + } + /** + * @callback notify + * @param {number} code + * @param {Deno.PointerValue} buffer + * @param {number} buflen + * @returns {void} + * + * @description This function is called when a new event is received from the server + */ + private notify( + _code: unknown | number, + buffer: Deno.PointerValue, + buflen: number, + ) { + const code = _code as bigint; + console.log(code); + if (buflen < 0) { + return; + } + const pointer = Deno.UnsafePointerView.getArrayBuffer( + buffer as unknown as NonNullable, + buflen, + ); + const event = new MessageEvent("message", { + data: pointer, + }); - connect(): Promise { - throw new Error("Method not implemented."); + //TODO(hironichu): Implement Error/event catching from rust to free the memory once a connection drop or if something else happens. + this.emit("event", event); } - async close() { - // return this; + ready = new Promise((resolve) => { + this.once("ready", (conn) => { + conn.state = "connected"; + this.conn = conn; + resolve(); + }); + }); + + close() { + this.#NOTIFY_PTR.unref(); + this.#CONNECTION_CB.unref(); + if (this.#CONN_PTR) { + Deno.WTLIB.symbols.proc_client_close(this.#CONN_PTR); + } + + this.emit("close", new CloseEvent("close")); + } + + private checkArgs(_options: WebTransportOptions) { + let certificate = ""; + let key = ""; + if (_options.certFile && _options.keyFile) { + Deno.statSync(_options.certFile); + Deno.statSync(_options.keyFile); + // + certificate = _options.certFile; + key = _options.keyFile; + return [certificate, key]; + } + if (_options.validateCertificate == false) { + return ["", ""]; + } + throw new TypeError( + "Missing necessary parameters", + ); } } diff --git a/mod/connection.ts b/mod/connection.ts index 31011b0..393145a 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -1,5 +1,10 @@ -export class Datagram { +export class WebTransportDatagramDuplexStream { #READ_BUFFER: Uint8Array; + readonly incomingHighWaterMark = 1; + readonly incomingMaxAge = null; + readonly maxDatagramSize = 1024; + readonly outgoingHighWaterMark = 1; + readonly outgoingMaxAge = null; constructor( private connection: WebTransportConnection, _buffer: Uint8Array, @@ -49,19 +54,31 @@ export class Datagram { }); return StreamBuffer; } + + //TODO(hironichu): implement the rest of the methods + public incomingBidirectionalStreams?: ReadableStream< + ReadableStream + > = undefined; + public incomingUnidirectionalStreams?: ReadableStream< + ReadableStream + > = undefined; } + export class WebTransportConnection { + state: "connected" | "closed" | "draining" | "failed" | "connecting" = + "connected" as const; + #CONN_PTR: Deno.PointerValue; - public datagrams: Datagram; + public datagrams: WebTransportDatagramDuplexStream; constructor( pointer: Deno.PointerValue, buffer: Uint8Array, ) { + this.state = "closed"; this.#CONN_PTR = pointer; - this.datagrams = new Datagram(this, buffer); + this.datagrams = new WebTransportDatagramDuplexStream(this, buffer); } - //TODO: add the rest of the methods for uni and bi streams get pointer() { return this.#CONN_PTR; } diff --git a/mod/interface.ts b/mod/interface.ts index ed91a26..7988541 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -1,7 +1,8 @@ export const WebTransportOptions = { maxTimeout: 10, keepAlive: 3, -} as const; + validateCertificate: true, +}; export const CertificateOptions = { certFile: "", @@ -25,19 +26,22 @@ export type WebTransportServerOptions = & typeof WebTransportServerOptions & Partial; +export type WebTransportOptions = + & typeof WebTransportOptions + & Partial; export const symbols = { // Server symbols proc_server_init: { parameters: [ - "function", - "u16", - "bool", - "u64", - "u64", - "buffer", - "usize", - "buffer", - "usize", + "function", //Callback + "u16", //Port + "bool", //Migration + "u64", //KeepAlive + "u64", //MaxTimeout + "buffer", //Cert + "usize", //CertLen + "buffer", //Key + "usize", //KeyLen ], result: "pointer", callback: true, @@ -57,6 +61,34 @@ export const symbols = { result: "void", }, // Client symbols + proc_client_init: { + parameters: [ + "function", + "u64", //KeepAlive + "u64", //MaxTimeout + "bool", //Certcheck + "buffer", //Cert + "usize", //CertLen + "buffer", //Key + "usize", //KeyLen + ], + result: "pointer", + callback: true, + }, + proc_client_connect: { + parameters: ["pointer", "function", "buffer", "usize"], + result: "pointer", + callback: true, + }, + proc_client_init_streams: { + parameters: ["pointer", "buffer", "usize"], + result: "void", + nonblocking: true, + }, + proc_client_close: { + parameters: ["pointer"], + result: "void", + }, // Shared symbols proc_recv_datagram: { parameters: ["pointer"], diff --git a/mod/lib.ts b/mod/lib.ts index 25d2b55..74c78f7 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -4,7 +4,7 @@ import { symbols } from "./interface.ts"; const options: FetchOptions = { name: "webtransport", - cache: "reloadAll", + cache: "only", url: LIB_URL!, }; diff --git a/mod/server.ts b/mod/server.ts index 8ffce86..8e01ba8 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -138,6 +138,7 @@ export class WebTransportServer extends EventEmitter { } this.emit("close", new CloseEvent("close")); } + private checkArgs(_options: WebTransportServerOptions) { if ( ((!_options.certFile || _options.certFile.length == 0) && diff --git a/src/client.rs b/src/client.rs index d9a1b1d..38bce4a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,8 +1,8 @@ -use std::{path::Path, time::Duration}; +use std::{path::Path, slice::from_raw_parts_mut, time::Duration}; use tokio::runtime::Runtime; -use wtransport::{ Endpoint, endpoint, ClientConfig, tls::Certificate}; +use wtransport::{endpoint, tls::Certificate, ClientConfig, Endpoint}; -use crate::{RUNTIME, SEND_FN, connection::Conn}; +use crate::{connection::Conn, executor, CLIENT_CONN_FN, RUNTIME, SEND_FN}; pub struct WebTransportClient { pub client: Option>, @@ -10,7 +10,6 @@ pub struct WebTransportClient { pub state: Option, } - impl WebTransportClient { pub(crate) unsafe fn new( sender_fn: Option, @@ -22,8 +21,8 @@ impl WebTransportClient { let client = match Endpoint::client(config) { Ok(server) => server, Err(e) => { - println!("Error creating server: {:?}", e); - return Err(1); + println!("Error creating client: {:?}", e); + return Err(2); } }; Ok(Self { @@ -33,52 +32,29 @@ impl WebTransportClient { }) } - // pub(crate) unsafe fn handle_sess_in(&'static mut self) { - // RUNTIME.spawn(async move { - // loop { - // let incoming_session = self.client.as_mut().unwrap().accept().await; - - // let session_request = incoming_session.await; - - // let accepted_session = match session_request { - // Ok(session_request) => session_request, - // Err(e) => { - // //TODO(hironichu): Handle error with callback SENDER_FN - // println!("Error accepting session: {:?}", e); - // return; - // } - // }; - // // accepted_session. - // //TODO(hironichu): We should handle every step of the handshake with callbacks. - - // // println!( - // // "DBG: New session: Authority: '{}', Path: '{}'", - // // accepted_session.authority(), - // // accepted_session.path() - // // ); - // match accepted_session.accept().await { - // Ok(conn) => { - // // println!("DBG: Sending connection to channel."); - // let client = ServerConn::new(conn); - // let client_ptr = Box::into_raw(Box::new(client)); - // assert!(!CONN_FN.is_none()); //TODO(hironichu): Handle this better. - // CONN_FN.unwrap()(client_ptr); - // } - // _ => { - // println!("Error accepting connection"); - // } - // } - // } - // }); - // } + pub(crate) unsafe fn connect(&'static mut self, url: String) { + RUNTIME.block_on(async move { + match self.client.as_mut().unwrap().connect(url).await { + Ok(conn) => { + let client = Conn::new(conn); + let client_ptr = Box::into_raw(Box::new(client)); + assert!(!CLIENT_CONN_FN.is_none()); + CLIENT_CONN_FN.unwrap()(client_ptr); + } + _ => { + println!("DBG: Error connecting to server."); + } + } + }); + } } - #[no_mangle] pub unsafe extern "C" fn proc_client_init( send_func: Option, keepalive: u64, timeout: u64, + certcheck: bool, cert_path: *const u8, cert_path_len: usize, key_path: *const u8, @@ -86,41 +62,159 @@ pub unsafe extern "C" fn proc_client_init( ) -> *mut WebTransportClient { assert!(!send_func.is_none()); - let cert_path = ::std::slice::from_raw_parts(cert_path, cert_path_len); - let key_path = ::std::slice::from_raw_parts(key_path, key_path_len); - let cert_path = Path::new(std::str::from_utf8(cert_path).unwrap()); - let key_path = Path::new(std::str::from_utf8(key_path).unwrap()); - - let _certificates = Certificate::load(cert_path, key_path).unwrap(); - let keepalive = if keepalive == 0 { None } else { Some(Duration::from_secs(keepalive)) }; - let timeout = if timeout == 0 { - None - } else { - Some(Duration::from_secs(timeout)) - }; + let timeout = if timeout == 0 { + None + } else { + Some(Duration::from_secs(timeout)) + }; //print the paths for debug - let config = ClientConfig::builder() - .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) - .with_native_certs() - .keep_alive_interval(keepalive) - .max_idle_timeout(timeout).unwrap() - .build(); - let server = WebTransportClient::new(send_func, config); - match server { - Ok(server) => { - let server_ptr = Box::into_raw(Box::new(server)); - server_ptr - } + let config = if certcheck { + let cert_path = ::std::slice::from_raw_parts(cert_path, cert_path_len); + let key_path = ::std::slice::from_raw_parts(key_path, key_path_len); + let cert_path = Path::new(std::str::from_utf8(cert_path).unwrap()); + let key_path = Path::new(std::str::from_utf8(key_path).unwrap()); + + let _certificates = Certificate::load(cert_path, key_path).unwrap(); + ClientConfig::builder() + .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) + .with_native_certs() + .keep_alive_interval(keepalive) + .max_idle_timeout(timeout) + .unwrap() + .build() + } else { + ClientConfig::builder() + .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) + .with_no_cert_validation() + .keep_alive_interval(keepalive) + .max_idle_timeout(timeout) + .unwrap() + .build() + }; + let client = WebTransportClient::new(send_func, config); + match client { + Ok(client) => Box::into_raw(Box::new(client)), Err(_) => { - panic!("Error creating server") + panic!("Error creating client") } } } +#[no_mangle] +pub unsafe extern "C" fn proc_client_connect( + client: *mut WebTransportClient, + cb: Option, + url: *const u8, + url_len: usize, +) { + let url = ::std::slice::from_raw_parts(url, url_len); + let url = std::str::from_utf8(url).unwrap(); + let client = &mut *client; + client.conn_cb = cb; + CLIENT_CONN_FN = cb; + client.connect(url.to_string()); +} #[no_mangle] -pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn, _c: *mut Runtime) {} \ No newline at end of file +pub unsafe extern "C" fn proc_client_init_streams( + clientptr: *mut Conn, + buffer: *mut u8, + buflen: usize, +) { + assert!(!clientptr.is_null()); + + let client = &mut *clientptr; + let sender = client.datagram_ch_sender.clone(); + + client.buffer = Some(from_raw_parts_mut(buffer, buflen)); + + executor::spawn(async move { + loop { + tokio::select! { + _ = client.conn.accept_bi() => { + // match stream { + // Ok(mut stream) => { + + // println!("Accepted BI stream"); + // let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); + // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); + + // println!("Received (bi) '{str_data}' from client"); + + // stream.0.write_all(b"ACK").await.unwrap(); + // }, + // _ => { + // client.conn.closed().await; + // } + // }; + return client.conn.closed().await; + } + _ = client.conn.accept_uni() => { + //close the connection until we implement the uni stream + return client.conn.closed().await; + // match stream { + // Ok(mut stream) => { + // println!("Accepted UNI stream"); + // let bytes_read = match stream.read(&mut buffer).await.unwrap() { + // Some(bytes_read) => bytes_read, + // None => continue, + // }; + + // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); + + // println!("Received (uni) '{str_data}' from client"); + + // let mut stream = client.conn.open_uni().await.unwrap().await.unwrap(); + // stream.write_all(b"ACK").await.unwrap(); + // }, + // _ => { + // client.conn.closed().await; + // } + // } + + } + stream = client.conn.receive_datagram() => { + match stream { + Ok(dgram) => sender.send_async(dgram).await.unwrap(), + _ => { + client.conn.closed().await; + //TODO(hironichu): Send action to Deno to free the pointer and buffer + // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); + return ; + } + } + }, + + } + } + }) + .detach(); +} + +#[no_mangle] +pub unsafe extern "C" fn proc_client_close( + client_ptr: *mut WebTransportClient, + conn: *mut Conn, +) -> usize { + assert!(!client_ptr.is_null()); + assert!(!conn.is_null()); + let client = &mut *client_ptr; + let conn = &mut *conn; + client.state = Some(false); + RUNTIME.block_on(async move { + conn.closed().await; + }); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn free_all_client( + _a: *mut WebTransportClient, + _b: *mut Conn, + _c: *mut Runtime, +) { +} diff --git a/src/connection.rs b/src/connection.rs index 7aea875..dc7b37a 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,23 +1,26 @@ -use flume::{Sender, Receiver}; -use wtransport::{Connection, datagram::Datagram}; +use flume::{Receiver, Sender}; +use wtransport::{datagram::Datagram, Connection}; pub struct Conn { - pub conn: Connection, - pub buffer: Option<&'static mut [u8]>, - pub datagram_ch_sender: Sender, - pub datagram_ch_receiver: Receiver, + pub conn: Connection, + pub buffer: Option<&'static mut [u8]>, + pub datagram_ch_sender: Sender, + pub datagram_ch_receiver: Receiver, } - impl Conn { - pub(crate) fn new(conn: Connection) -> Self { - let (sender, receiver) = flume::unbounded(); + pub(crate) fn new(conn: Connection) -> Self { + let (sender, receiver) = flume::bounded(1); - Self { - conn, - buffer: None, - datagram_ch_sender: sender, - datagram_ch_receiver: receiver, - } - } -} \ No newline at end of file + Self { + conn, + buffer: None, + datagram_ch_sender: sender, + datagram_ch_receiver: receiver, + } + } + //TODO(hironichu): Add generic methods for openning and closing streams instead of doing it in both client and server. + pub async fn closed(&mut self) { + self.conn.closed().await + } +} diff --git a/src/lib.rs b/src/lib.rs index aa40b07..3b5a718 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,15 +2,15 @@ use connection::Conn; use once_cell::sync::Lazy; use tokio::runtime::Runtime; -///------------------------------------ +///------------------------------------ static mut SEND_FN: Option = None; -static mut CONN_FN: Option = None; +static mut SERVER_CONN_FN: Option = None; +static mut CLIENT_CONN_FN: Option = None; static mut RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); ///------------------------------------ - mod certificate; -mod connection; -pub mod server; pub mod client; +mod connection; mod executor; +pub mod server; mod shared; diff --git a/src/server.rs b/src/server.rs index b95d908..35664af 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,7 +2,7 @@ use std::{path::Path, slice::from_raw_parts_mut, time::Duration}; use tokio::runtime::Runtime; use wtransport::{endpoint, tls::Certificate, Endpoint, ServerConfig}; -use crate::{connection::Conn, executor, CONN_FN, RUNTIME, SEND_FN}; +use crate::{connection::Conn, executor, RUNTIME, SEND_FN, SERVER_CONN_FN}; pub struct WebTransportServer { pub server: Option>, @@ -60,8 +60,8 @@ impl WebTransportServer { // println!("DBG: Sending connection to channel."); let client = Conn::new(conn); let client_ptr = Box::into_raw(Box::new(client)); - assert!(!CONN_FN.is_none()); //TODO(hironichu): Handle this better. - CONN_FN.unwrap()(client_ptr); + assert!(!SERVER_CONN_FN.is_none()); //TODO(hironichu): Handle this better. + SERVER_CONN_FN.unwrap()(client_ptr); } _ => { println!("Error accepting connection"); @@ -133,7 +133,7 @@ pub unsafe extern "C" fn proc_server_listen( assert!(!server_ptr.is_null()); let server = &mut *server_ptr; server.conn_cb = cb; - CONN_FN = cb; + SERVER_CONN_FN = cb; server.handle_sess_in(); } diff --git a/src/shared.rs b/src/shared.rs index db2c0d2..ccaf9e6 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,11 +1,11 @@ +use crate::{connection::Conn, SEND_FN}; +use core::panic; use std::slice::from_raw_parts_mut; use tokio::runtime::Runtime; -use crate::connection::Conn; +use wtransport::error::SendDatagramError; #[no_mangle] -pub unsafe extern "C" fn proc_recv_datagram( - conn_ptr: *mut Conn, -) -> usize { +pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn) -> usize { let client = &mut *conn_ptr; match client.datagram_ch_receiver.recv() { Ok(dgram) => { @@ -18,26 +18,56 @@ pub unsafe extern "C" fn proc_recv_datagram( } #[no_mangle] -pub unsafe extern "C" fn proc_send_datagram( - connptr: *mut Conn, - buf: *const u8, - buflen: u32, -) { +pub unsafe extern "C" fn proc_send_datagram(connptr: *mut Conn, buf: *const u8, buflen: u32) { assert!(!connptr.is_null()); let client = &mut *connptr; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); match client.conn.send_datagram(buf) { Ok(_) => {} - Err(e) => { + Err(err) => { //TODO: Handle error better - println!("Error sending datagram: {:?}", e); + match err { + SendDatagramError::NotConnected => { + println!("DBG: Rust Connection closed"); + SEND_FN.unwrap()(0, vec![0].as_mut_ptr(), 1); + } + SendDatagramError::TooLarge => { + println!("DBG: Rust Too large"); + SEND_FN.unwrap()(0, vec![0].as_mut_ptr(), 1); + } + SendDatagramError::UnsupportedByPeer => { + println!("DBG: Rust not supported by peer"); + SEND_FN.unwrap()(0, vec![0].as_mut_ptr(), 1); + } + }; } } } +#[no_mangle] +pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn, _buf: *mut u8) { + assert!(!connptr.is_null()); + + let _client = &mut *connptr; + //return not implemented + panic!("Not implemented"); + // executor::spawn(async move { + // let stream = client.conn.open_bi().await.unwrap(); + // stream.await.unwrap(). + // }) + // .detach(); + // let buf = ::std::slice::from_raw_parts(buf, buflen as usize); + // match client.conn.send_datagram(buf) { + // Ok(_) => {} + // Err(e) => { + // //TODO: Handle error better + // println!("Error sending datagram: {:?}", e); + // } + // } +} #[no_mangle] pub unsafe extern "C" fn free_conn(_: *mut Conn) {} #[no_mangle] -pub unsafe extern "C" fn free_runtime(_: *mut Runtime) {} \ No newline at end of file +pub unsafe extern "C" fn free_runtime(_: *mut Runtime) {} From 456608a6b5879c71b2b8052b0c349bc0c6e8fd01 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 22:15:55 +0200 Subject: [PATCH 31/72] lint --- mod/client.ts | 1 - mod/deno.ts | 93 --------------------------------------------------- 2 files changed, 94 deletions(-) delete mode 100644 mod/deno.ts diff --git a/mod/client.ts b/mod/client.ts index 71695b8..e1e5510 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -7,7 +7,6 @@ import { } from "./connection.ts"; import { EventEmitter } from "./deps.ts"; import { - symbols, type WebTransportOptions, WebTransportOptions as ServerOpts, } from "./interface.ts"; diff --git a/mod/deno.ts b/mod/deno.ts deleted file mode 100644 index 7ce6748..0000000 --- a/mod/deno.ts +++ /dev/null @@ -1,93 +0,0 @@ -// import LIB from "./lib.ts"; -// import { encodeBuf } from "./utils.ts"; -// const lib = LIB; - -// const ptrstate = new Uint32Array(1); - -// const sender = new Deno.UnsafeCallback( -// { -// parameters: ["u32", "pointer", "u32"], -// result: "void", -// }, -// (_code: unknown | number, buffer, buflen) => { -// const code = _code as typeof ptrstate[0]; -// console.log(code); -// if (buflen < 0) { -// return; -// } -// const pointer = Deno.UnsafePointerView.getArrayBuffer( -// buffer as unknown as NonNullable, -// buflen, -// ); -// console.log(pointer); -// }, -// ); - -// sender.ref(); -// let serverPTR; -// let new_connection; -// const _decoder = new TextDecoder(); -// try { -// const certpath = encodeBuf("./certs/cert.pem"); -// const keypath = encodeBuf("./certs/key.pem"); - -// serverPTR = lib.symbols.proc_server_init( -// sender.pointer, -// 4433, -// true, -// 3, -// 20, -// certpath[0], -// certpath[1], -// keypath[0], -// keypath[1], -// ); -// new_connection = new Deno.UnsafeCallback( -// { -// parameters: ["pointer"], -// result: "void", -// }, -// async (client) => { -// console.log("DENO : New connection"); -// const DBufferB = new Uint8Array(65536); - -// lib.symbols.proc_server_init_streams( -// client, -// DBufferB, -// DBufferB.byteLength, -// ); -// const StreamBuffer = new ReadableStream({ -// async pull(controller) { -// try { -// const nread = await lib.symbols.proc_recv_datagram( -// client, -// ); -// if (nread > 0) { -// controller.enqueue( -// DBufferB.subarray(0, nread as number), -// ); -// } -// } catch (e) { -// controller.error(e); -// } -// }, -// cancel() { -// console.error("[Error] Stream cancelled"); -// }, -// }); -// for await (const chunk of StreamBuffer) { -// // const msg = parseInt(decoder.decode(chunk)); -// // console.log(decoder.decode(chunk)); -// lib.symbols.proc_send_datagram(client, chunk, chunk.length); -// } -// }, -// ); -// new_connection.ref(); -// lib.symbols.proc_server_listen(serverPTR, new_connection.pointer); -// console.info("Server is running"); -// } catch (e) { -// sender.unref(); -// new_connection?.unref(); -// console.error(e); -// } -// if (!serverPTR) throw new Error("Server is not running"); From e2839b83ef8b5675092f883ab78e8359016b5d77 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 22:28:00 +0200 Subject: [PATCH 32/72] Added more detail to readme --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ab53031..949aa1b 100644 --- a/README.md +++ b/README.md @@ -3,5 +3,71 @@ This library implements a very WIP version of the [WebTransport](https://w3c.github.io/webtransport/) API for Deno. -> This does not follow standards and is not production ready. basically just a -> PoC +> This does not follow perfectly the web standards and is not production ready. +> basically just a PoC + +## Usage + +### Client example : + +This client follows the standards of the API, but the server does not (since it +has not been defined). + +```ts +import "https://deno.land/x/webtransport/mod.ts"; + +//Client +const transport = new Deno.WebTransport("https://localhost:4433"); +/** + * Please note that this example above will not work if you have self validated certificates + * use the following to disable certificate verification.. (Warning this is not secure) + * const transport = new WebTransport("https://localhost:4433", { + * maxTimeout: 10, + * keepAlive: 3, + * validateCertificate: false, + * }); + */ +await transport.ready; +// Send Datagram packet + +const encoder = new TextEncoder(); +const data = encoder.encode("Hello World"); +const writer = await transport.datagrams.writable.getWriter(); +await writer.write(data); +``` + +### Server example : + +This server tries to be as close to the client api but has some differences. + +```ts +import "https://deno.land/x/webtransport/mod.ts"; + +//Client +const transport = new Deno.WebTransportServer(4433, { + keyFile: "./certs/key.pem", + certFile: "./certs/cert.pem", + maxTimeout: 10, + keepAlive: 3, +}); + +transport.on("connection", async (conn) => { + //To get the datagrams from the client you can just look over the datagram stream + for await (const datagram of conn.datagrams.readable) { + const decoder = new TextDecoder(); + console.log(decoder.decode(datagram)); + } + //The server can also send datagrams to the client (using the same API for consistency) + const encoder = new TextEncoder(); + const data = encoder.encode("Hello World"); + const writer = await conn.datagrams.writable.getWriter(); + await writer.write(data); +}); +``` + +# IMPORTANT + +The module add declaration to the global scope, so you can use the API without +importing the classes + +This might not be practical for some people, but for now cannot be changed. From 9bbae3934c5ca3fc7d3de230e1b623bd69c69d4a Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 17 Sep 2023 22:40:55 +0200 Subject: [PATCH 33/72] added classes to global scope --- README.md | 10 +++++----- examples/deno/wt_client.ts | 2 +- examples/deno/wt_server.ts | 2 +- mod/mod.ts | 32 ++++++++++++++++++-------------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 949aa1b..f0cedb6 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ has not been defined). import "https://deno.land/x/webtransport/mod.ts"; //Client -const transport = new Deno.WebTransport("https://localhost:4433"); +const transport = new WebTransport("https://localhost:4433"); /** * Please note that this example above will not work if you have self validated certificates * use the following to disable certificate verification.. (Warning this is not secure) @@ -44,7 +44,7 @@ This server tries to be as close to the client api but has some differences. import "https://deno.land/x/webtransport/mod.ts"; //Client -const transport = new Deno.WebTransportServer(4433, { +const transport = new WebTransportServer(4433, { keyFile: "./certs/key.pem", certFile: "./certs/cert.pem", maxTimeout: 10, @@ -67,7 +67,7 @@ transport.on("connection", async (conn) => { # IMPORTANT -The module add declaration to the global scope, so you can use the API without -importing the classes +The module add declaration to the global namespace, so you can use the API +without importing the classes -This might not be practical for some people, but for now cannot be changed. +This might not be practical for some people, but for now wont be changed. diff --git a/examples/deno/wt_client.ts b/examples/deno/wt_client.ts index 3cc89bd..65121ab 100644 --- a/examples/deno/wt_client.ts +++ b/examples/deno/wt_client.ts @@ -1,7 +1,7 @@ //TO BE IMPLEMENTED import "../../mod/mod.ts"; -const client = new Deno.WebTransport("https://localhost:4433", { +const client = new WebTransport("https://localhost:4433", { validateCertificate: false, maxTimeout: 10, keepAlive: 3, diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index ca82118..d56e409 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -1,7 +1,7 @@ //TO BE IMPLEMENTED import "../../mod/mod.ts"; -const server = new Deno.WebTransportServer(4433, { +const server = new WebTransportServer(4433, { certFile: "./certs/cert.pem", keyFile: "./certs/key.pem", maxTimeout: 10, diff --git a/mod/mod.ts b/mod/mod.ts index 455fc02..5a405de 100644 --- a/mod/mod.ts +++ b/mod/mod.ts @@ -2,30 +2,34 @@ import { symbols } from "./interface.ts"; import { LIB } from "./lib.ts"; window.WTLIB = await LIB(); -Deno.WTLIB = window.WTLIB; import { WebTransportServer as WtServer } from "./server.ts"; import { WebTransport as WtClient } from "./client.ts"; -// Deno.WebTransport = WebTransport; -// Deno.WebTransportServer = WebTransportServer; - declare global { - namespace Deno { + // namespace Deno { + // export class WebTransport extends WtClient {} + // export class WebTransportServer extends WtServer {} + // export let WTLIB: Deno.DynamicLibrary; + // } + + export interface Window { + WTLIB: Deno.DynamicLibrary; + } + namespace globalThis { export class WebTransport extends WtClient {} export class WebTransportServer extends WtServer {} export let WTLIB: Deno.DynamicLibrary; } - export interface Window { - WebTransport: typeof Deno.WebTransport; - WebTransportServer: typeof Deno.WebTransportServer; - WTLIB: Deno.DynamicLibrary; - } } +declare namespace globalThis { + export class WebTransport extends WtClient {} + export class WebTransportServer extends WtServer {} + export let WTLIB: Deno.DynamicLibrary; +} + +globalThis.WebTransport = WtClient; +globalThis.WebTransportServer = WtServer; -Deno.WebTransport = WtClient; -Deno.WebTransportServer = WtServer; -window.WebTransport = WtClient; -window.WebTransportServer = WtServer; export { WtClient, WtServer }; // Path: mod/mod.ts From 95fca83284090597216893a9bfa2e84aee6294f4 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 18 Sep 2023 01:07:14 +0200 Subject: [PATCH 34/72] Trying test --- Cargo.toml | 2 +- examples/deno/wt_client_test.ts | 72 ++++++++++++++++++++++----------- examples/deno/wt_server_test.ts | 26 +++++++++++- mod/client.ts | 14 ++++++- mod/connection.ts | 11 +++-- mod/crypto.ts | 11 ++--- mod/interface.ts | 18 ++++++++- mod/mod.ts | 10 +---- mod/server.ts | 45 ++++++++++++++++----- src/client.rs | 12 ++---- src/server.rs | 13 +++--- 11 files changed, 163 insertions(+), 71 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fa5e159..156432a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ tokio = { version = "=1.32.0", default-features = false, features = [ "macros", ] } # wtransport = "0.1.4" # TODO: Replace this once the fix for arm is merged. -wtransport = { git = "https://github.com/BiagioFesta/wtransport", branch = "master", features = [ +wtransport = { git = "https://github.com/hironichu/wtransport", branch = "feat/new", features = [ "dangerous-configuration", ] } rcgen = "=0.11.1" diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts index efeea99..1b8b1eb 100644 --- a/examples/deno/wt_client_test.ts +++ b/examples/deno/wt_client_test.ts @@ -1,29 +1,55 @@ -import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts"; -import { getAnyEdgeLatest } from "npm:edge-paths"; +import "../../mod/mod.ts"; +// import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts"; +// import { getAnyEdgeLatest } from "npm:edge-paths"; -Deno.test( - { name: "runEdgePuppeteer", ignore: Deno.build.os != "windows" }, - async () => { - const browser = await puppeteer.launch({ - headless: true, - defaultViewport: null, - executablePath: getAnyEdgeLatest(), - // no extension - args: [ - "--enable-automation", - "--disable-gpu", - "--disable-extensions", - ], - }); - //TODO(hironichu): Setup valid testing context for client - const page = await browser.newPage(); +// Deno.test( +// { name: "Browser test", ignore: Deno.build.os != "windows" }, +// async () => { +// const browser = await puppeteer.launch({ +// headless: true, +// defaultViewport: null, +// executablePath: getAnyEdgeLatest(), +// // no extension +// args: [ +// "--enable-automation", +// "--disable-gpu", +// "--disable-extensions", +// ], +// }); +// //TODO(hironichu): Setup valid testing context for client +// const page = await browser.newPage(); + +// await page.goto("https://google.com", { +// waitUntil: "networkidle2", +// }); - await page.goto("https://google.com", { - waitUntil: "networkidle2", - }); +// // await page.pdf({ path: "hn.pdf", format: "A4" }); - // await page.pdf({ path: "hn.pdf", format: "A4" }); +// await browser.close(); +// }, +// ); +Deno.test( + { + name: "Client connect/close (unsafe)", + sanitizeOps: false, + sanitizeResources: false, + }, + async () => { + // const server = new WebTransportServer(4433, { + // certFile: "./certs/cert.pem", + // keyFile: "./certs/key.pem", + // maxTimeout: 10, + // keepAlive: 5, + // }); - await browser.close(); + // const client = new WebTransport("https://localhost:4433", { + // maxTimeout: 50, + // keepAlive: 3, + // validateCertificate: false, + // }); + // server.on("connection", (_) => { + // console.log("OK"); + // // await client.close(); + // }); }, ); diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index a2a5283..00fa685 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1,3 +1,25 @@ -Deno.test({ name: "Server startup" }, async () => { - // +import "../../mod/mod.ts"; +Deno.test({ name: "Server startup/close" }, async () => { + const server = new WebTransportServer(4433, { + certFile: "./certs/cert.pem", + keyFile: "./certs/key.pem", + maxTimeout: 10, + keepAlive: 3, + }); + + await server.close(); }); + +// Deno.test( +// { name: "Server with generated certificate startup/close" }, +// async () => { +// const server = new WebTransportServer(4434, { +// maxTimeout: 10, +// keepAlive: 3, +// notAfter: 10, +// notBefore: 0, +// domain: "localhost", +// }); +// await server.close(); +// }, +// ); diff --git a/mod/client.ts b/mod/client.ts index e1e5510..78f8042 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -139,11 +139,21 @@ export class WebTransport extends EventEmitter { }); }); - close() { + async close() { this.#NOTIFY_PTR.unref(); + this.#NOTIFY_PTR.close(); this.#CONNECTION_CB.unref(); + this.#CONNECTION_CB.close(); if (this.#CONN_PTR) { - Deno.WTLIB.symbols.proc_client_close(this.#CONN_PTR); + await window.WTLIB.symbols.proc_client_close( + this.#CONN_PTR, + this.conn!.pointer, + ); + //free the Client pointer + window.WTLIB.symbols.free_all_client( + this.#CONN_PTR, + this.conn!.pointer, + ); } this.emit("close", new CloseEvent("close")); diff --git a/mod/connection.ts b/mod/connection.ts index 393145a..b73a0af 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -16,7 +16,7 @@ export class WebTransportDatagramDuplexStream { return new WritableStream({ write(chunk) { try { - Deno.WTLIB.symbols.proc_send_datagram( + window.WTLIB.symbols.proc_send_datagram( connection.pointer!, chunk, chunk.byteLength, @@ -36,7 +36,7 @@ export class WebTransportDatagramDuplexStream { const StreamBuffer = new ReadableStream({ async pull(controller) { try { - const nread = await Deno.WTLIB.symbols.proc_recv_datagram( + const nread = await window.WTLIB.symbols.proc_recv_datagram( connection.pointer!, ); if (nread > 0) { @@ -78,7 +78,12 @@ export class WebTransportConnection { this.#CONN_PTR = pointer; this.datagrams = new WebTransportDatagramDuplexStream(this, buffer); } - + async close() { + return await window.WTLIB.symbols.proc_client_close( + this.#CONN_PTR, + this.pointer, + ); + } get pointer() { return this.#CONN_PTR; } diff --git a/mod/crypto.ts b/mod/crypto.ts index 17998ff..0822482 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -71,7 +71,7 @@ export function GenerateCertKeyFile( domainStr: string, start: number, end: number, - path = "./certs/", + path = join(Deno.cwd(), "./certs/"), ) { const [cert, key] = GenerateCertKey(domainStr, start, end); try { @@ -79,14 +79,15 @@ export function GenerateCertKeyFile( const keypath = join(path, `${domainStr}.key`); Deno.writeFileSync(certpath, cert, { mode: 0o444, - createNew: true, + // createNew: true, }); Deno.writeFileSync(keypath, key, { mode: 0o444, - createNew: true, + // createNew: true, }); return [certpath, keypath]; - } catch { - throw new Error("Failed to write certificate file"); + } catch (e) { + console.error(e); + throw new Error("Failed to write certificate or key file ", e); } } diff --git a/mod/interface.ts b/mod/interface.ts index 7988541..4567352 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -58,7 +58,8 @@ export const symbols = { }, proc_server_close: { parameters: ["pointer"], - result: "void", + result: "usize", + nonblocking: true, }, // Client symbols proc_client_init: { @@ -86,8 +87,9 @@ export const symbols = { nonblocking: true, }, proc_client_close: { - parameters: ["pointer"], + parameters: ["pointer", "pointer"], result: "void", + nonblocking: true, }, // Shared symbols proc_recv_datagram: { @@ -114,6 +116,18 @@ export const symbols = { ], result: "bool", }, + free_server: { + parameters: ["pointer"], + result: "void", + }, + free_conn: { + parameters: ["pointer"], + result: "void", + }, + free_all_client: { + parameters: ["pointer", "pointer"], + result: "void", + }, } as const; //change the type of window so we add Webtransport and WebtransportServer diff --git a/mod/mod.ts b/mod/mod.ts index 5a405de..7d14897 100644 --- a/mod/mod.ts +++ b/mod/mod.ts @@ -6,22 +6,16 @@ import { WebTransportServer as WtServer } from "./server.ts"; import { WebTransport as WtClient } from "./client.ts"; declare global { - // namespace Deno { - // export class WebTransport extends WtClient {} - // export class WebTransportServer extends WtServer {} - // export let WTLIB: Deno.DynamicLibrary; - // } - export interface Window { WTLIB: Deno.DynamicLibrary; } - namespace globalThis { + export namespace globalThis { export class WebTransport extends WtClient {} export class WebTransportServer extends WtServer {} export let WTLIB: Deno.DynamicLibrary; } } -declare namespace globalThis { +export declare namespace globalThis { export class WebTransport extends WtClient {} export class WebTransportServer extends WtServer {} export let WTLIB: Deno.DynamicLibrary; diff --git a/mod/server.ts b/mod/server.ts index 8e01ba8..1a55a55 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -71,7 +71,6 @@ export class WebTransportServer extends EventEmitter { ); this.#NOTIFY_PTR.ref(); this.#CONNECTION_CB.ref(); - this.emit("listening", new Event("ready")); } /** @@ -132,18 +131,35 @@ export class WebTransportServer extends EventEmitter { close() { this.#NOTIFY_PTR.unref(); + this.#NOTIFY_PTR.close(); + // this.#CONNECTION_CB.unref(); + this.#CONNECTION_CB.close(); + if (this.#SRV_PTR) { - Deno.WTLIB.symbols.proc_server_close(this.#SRV_PTR); + // await window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); } this.emit("close", new CloseEvent("close")); + + //free all the connections + this.connections.forEach(async (conn, id) => { + await conn.close(); + window.WTLIB.symbols.free_conn(conn.pointer); + //delete the connection + this.connections.delete(id); + }); + window.WTLIB.symbols.free_server(this.#SRV_PTR!); + this.#SRV_PTR = undefined; + return; } private checkArgs(_options: WebTransportServerOptions) { if ( ((!_options.certFile || _options.certFile.length == 0) && (!_options.keyFile || _options.keyFile.length == 0)) && - (!_options.notAfter && !_options.notBefore && !_options.domain) + (typeof _options.notAfter == "undefined" && + typeof _options.notBefore == "undefined" && + !_options.domain) ) { throw new TypeError( "Missing necessary parameters: certFile, keyFile or notAfter, notBefore to generate a new certificate", @@ -151,25 +167,36 @@ export class WebTransportServer extends EventEmitter { } let certificate = ""; let key = ""; - if (_options.certFile && _options.keyFile) { + + if ( + (_options.certFile && _options.keyFile) + ) { + if (_options.certFile.length == 0 || _options.keyFile.length == 0) { + throw new TypeError( + "Invalid certificate or key file path (empty string)", + ); + } Deno.statSync(_options.certFile); Deno.statSync(_options.keyFile); // certificate = _options.certFile; key = _options.keyFile; + return [certificate, key]; } if ( - ((!_options.certFile || _options.certFile.length == 0) && - (!_options.keyFile || _options.keyFile.length == 0)) && - (_options.notAfter && _options.notBefore && _options.domain) + typeof _options.notAfter != "undefined" && + typeof _options.notBefore != "undefined" && _options.domain ) { - [certificate, key] = GenerateCertKeyFile( + return [certificate, key] = GenerateCertKeyFile( _options.domain, _options.notBefore, _options.notAfter, ); + } else { + throw new TypeError( + "Missing necessary parameters: notAfter, notBefore, domain to generate a new certificate", + ); } - return [certificate, key]; } } export default WebTransportServer; diff --git a/src/client.rs b/src/client.rs index 38bce4a..11ad669 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,5 +1,4 @@ use std::{path::Path, slice::from_raw_parts_mut, time::Duration}; -use tokio::runtime::Runtime; use wtransport::{endpoint, tls::Certificate, ClientConfig, Endpoint}; use crate::{connection::Conn, executor, CLIENT_CONN_FN, RUNTIME, SEND_FN}; @@ -41,8 +40,8 @@ impl WebTransportClient { assert!(!CLIENT_CONN_FN.is_none()); CLIENT_CONN_FN.unwrap()(client_ptr); } - _ => { - println!("DBG: Error connecting to server."); + Err(err) => { + println!("DBG: Error connecting to server. Err: {}", err.to_string()); } } }); @@ -212,9 +211,4 @@ pub unsafe extern "C" fn proc_client_close( } #[no_mangle] -pub unsafe extern "C" fn free_all_client( - _a: *mut WebTransportClient, - _b: *mut Conn, - _c: *mut Runtime, -) { -} +pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn) {} diff --git a/src/server.rs b/src/server.rs index 35664af..5d57e2a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -214,20 +214,19 @@ pub unsafe extern "C" fn proc_server_init_streams( } #[no_mangle] -pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) { +pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) -> usize { assert!(!server_ptr.is_null()); + let server = &mut *server_ptr; server.state = Some(false); + let endpoint = server.server.as_mut().unwrap(); + endpoint.close(20, b"closed"); + 0 } //free all above once #[no_mangle] -pub unsafe extern "C" fn free_all_server( - _a: *mut WebTransportServer, - _b: *mut Conn, - _c: *mut Runtime, -) { -} +pub unsafe extern "C" fn free_all_server(_a: *mut WebTransportServer, _c: *mut Runtime) {} #[no_mangle] pub unsafe extern "C" fn free_server(_: *mut WebTransportServer) {} From 1a58800cf07feaa045575c7c9c6d2c27dae9df89 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 18 Sep 2023 01:11:35 +0200 Subject: [PATCH 35/72] Fixes CI env variable --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 777205f..9d63e76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -180,14 +180,19 @@ jobs: - name: Run deno test (debug) if: | matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') + env: + DEVELOPMENT: true run: | deno task test + - name: Run deno test (release) if: | (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || ( github.repository == 'hironichu/webtransport' && github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) + env: + DEVELOPMENT: true run: | deno task test - name: Upload release to GitHub From f68340cf8f6a3d0ea3c86ec7db5f92d326be2ebe Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 18 Sep 2023 01:14:56 +0200 Subject: [PATCH 36/72] Fixes CI cache --- mod/lib.ts | 2 +- utils/download_lib.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mod/lib.ts b/mod/lib.ts index 74c78f7..25d2b55 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -4,7 +4,7 @@ import { symbols } from "./interface.ts"; const options: FetchOptions = { name: "webtransport", - cache: "only", + cache: "reloadAll", url: LIB_URL!, }; diff --git a/utils/download_lib.ts b/utils/download_lib.ts index b8ec7f6..2bb9bb9 100644 --- a/utils/download_lib.ts +++ b/utils/download_lib.ts @@ -62,5 +62,5 @@ if (!Deno.env.get("DEVELOPMENT")) { LIB_URL!.username = Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]!; } -LIB_URL = LIB_URL ?? new URL("../target/release/", import.meta.url); +LIB_URL = LIB_URL ?? new URL("../dist/", import.meta.url); export default LIB_URL; From 707fc1ecf054350b93596543df40b088404f6516 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 18 Sep 2023 01:17:16 +0200 Subject: [PATCH 37/72] Fixes CI cache --- utils/download_lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/download_lib.ts b/utils/download_lib.ts index 2bb9bb9..b8ec7f6 100644 --- a/utils/download_lib.ts +++ b/utils/download_lib.ts @@ -62,5 +62,5 @@ if (!Deno.env.get("DEVELOPMENT")) { LIB_URL!.username = Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]!; } -LIB_URL = LIB_URL ?? new URL("../dist/", import.meta.url); +LIB_URL = LIB_URL ?? new URL("../target/release/", import.meta.url); export default LIB_URL; From b27e136dc3f358766ee66635a99ac31c5a1ee474 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 18 Sep 2023 01:19:41 +0200 Subject: [PATCH 38/72] Fixes CI... --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d63e76..0ef61d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -188,9 +188,7 @@ jobs: - name: Run deno test (release) if: | (matrix.job == 'test' && matrix.profile == 'release') && - (matrix.use_sysroot || ( - github.repository == 'hironichu/webtransport' && - github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) + ((github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) env: DEVELOPMENT: true run: | From e61c83771c62bca773d5798890e1e86c2c50bd91 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 18 Sep 2023 01:24:13 +0200 Subject: [PATCH 39/72] Fixes CI... --- .github/workflows/ci.yml | 2 ++ utils/download_lib.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ef61d8..9ae3e54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -182,6 +182,7 @@ jobs: matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') env: DEVELOPMENT: true + BUILD_TARGET: debug run: | deno task test @@ -190,6 +191,7 @@ jobs: (matrix.job == 'test' && matrix.profile == 'release') && ((github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) env: + BUILD_TARGET: release DEVELOPMENT: true run: | deno task test diff --git a/utils/download_lib.ts b/utils/download_lib.ts index b8ec7f6..2bb9bb9 100644 --- a/utils/download_lib.ts +++ b/utils/download_lib.ts @@ -62,5 +62,5 @@ if (!Deno.env.get("DEVELOPMENT")) { LIB_URL!.username = Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]!; } -LIB_URL = LIB_URL ?? new URL("../target/release/", import.meta.url); +LIB_URL = LIB_URL ?? new URL("../dist/", import.meta.url); export default LIB_URL; From d52a1ec94dbb9e21ba69703a4a44bdb702549080 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Tue, 19 Sep 2023 05:41:21 +0200 Subject: [PATCH 40/72] ci: trying new CI config (#3) --- .github/workflows/ci.yml | 212 ++++++++++++++++---------------- Cargo.toml | 3 +- deno.jsonc | 13 +- examples/deno/wt_client_test.ts | 1 + examples/deno/wt_server_test.ts | 72 +++++++---- mod/client.ts | 2 +- mod/crypto.ts | 20 ++- mod/deps.ts | 2 + mod/interface.ts | 15 +-- mod/lib.ts | 10 +- mod/server.ts | 2 +- src/client.rs | 86 ++----------- src/connection.rs | 43 ++++++- src/server.rs | 85 +------------ src/shared.rs | 8 ++ utils/clean.js | 15 --- utils/download_lib.ts | 39 ++---- 17 files changed, 267 insertions(+), 361 deletions(-) delete mode 100644 utils/clean.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ae3e54..d470df1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,65 +18,34 @@ concurrency: name: Webtransport CI jobs: build: - name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} if: | - github.event_name == 'push' || - !startsWith(github.event.pull_request.head.label, 'hironichu:') - runs-on: ${{ matrix.os }} - timeout-minutes: 90 + github.event_name == 'push' || !startsWith(github.event.pull_request.head.label, 'hironichu:') strategy: matrix: + os: [ 'ubuntu-latest', 'self-hosted'] + job: [build] + profile: [debug, release] include: - - os: macos-latest - job: test - profile: debug - - os: macos-latest - job: test - profile: release - - os: windows-latest - job: test - profile: debug - - os: windows-latest - job: test - profile: release - - os: 'ubuntu-22.04' - job: test - profile: release - use_sysroot: true - - os: 'ubuntu-22.04' - job: test - profile: debug - - - os: 'ubuntu-22.04' + - os: 'ubuntu-latest' job: lint profile: debug - - os: 'self-hosted' - job: test - profile: debug - - - os: 'self-hosted' - job: test - profile: release - fail-fast: ${{ github.event_name == 'pull_request' || (github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/')) }} + name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 10 env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full steps: - - name: Configure git - run: | - git config --global core.symlinks true - git config --global fetch.parallel 4 - name: Clone repository uses: actions/checkout@v3 with: fetch-depth: 2 - submodules: recursive - name: Create source tarballs (release, linux) if: | startsWith(matrix.os, 'ubuntu') && matrix.profile == 'release' && - matrix.job == 'test' && + matrix.job == 'build' && github.repository == 'hironichu/webtransport' && startsWith(github.ref, 'refs/tags/') run: | @@ -86,9 +55,10 @@ jobs: - name: Setting Up Rust uses: dtolnay/rust-toolchain@master + if: | + matrix.job != 'lint' with: - toolchain: nightly - + toolchain: stable - name: Install Deno from .land if: matrix.os != 'self-hosted' uses: denoland/setup-deno@v1 @@ -108,37 +78,6 @@ jobs: - name: Error on warning run: echo "RUSTFLAGS=-D warnings" >> $GITHUB_ENV - - name: Log versions - shell: bash - run: | - rustc --version - cargo --version - deno --version - - - name: Cache build output (main) - uses: actions/cache@v3 - if: (matrix.job == 'test' && matrix.profile == 'release') && - github.ref == 'refs/heads/main' - with: - path: | - !./target/*/*.dll - !./target/*/*.so - !./target/*/*.dylib - key: | - 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }} - - name: Cache build output (PR) - uses: actions/cache@v3 - if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') && - matrix.job == 'test' && matrix.profile == 'release' - with: - path: | - !./target/*/*.dll - !./target/*/*.so - !./target/*/*.dylib - key: never_saved - restore-keys: | - 16-cargo-target-${{ matrix.os }}-${{ matrix.profile }}- - - name: Deno Format if: matrix.job == 'lint' run: deno task util:fmt @@ -149,34 +88,104 @@ jobs: - name: Build Debug if: | - (!startsWith(matrix.os, 'self-hosted')) && - (matrix.job == 'test' && matrix.profile == 'debug') - run: deno task build:debug - - - name: Build Debug ARM - if: | - (startsWith(matrix.os, 'self-hosted')) && - (matrix.job == 'test' && matrix.profile == 'debug') - run: | - deno task build:debug-arm + (matrix.job == 'build' && matrix.profile == 'debug') + run: deno task build:${{matrix.profile}} - name: Build release if: | - (!startsWith(matrix.os, 'self-hosted')) && - (matrix.job == 'test' && matrix.profile == 'release') && + (matrix.job == 'build' && matrix.profile == 'release') && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) - run: deno task build:release + run: deno task build:${{matrix.profile}} - - name: Build release Linux ARM + - name: Move arm file (release) + if: startsWith(matrix.os, 'self-hosted') && matrix.job == 'build' && matrix.profile == 'release' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + run: | + mv target/${{matrix.profile}}/libwebtransport.so target/${{matrix.profile}}/libwebtransport_aarch64.so + + - name: Move arm file (debug) + if: startsWith(matrix.os, 'self-hosted') && matrix.job == 'build' && matrix.profile == 'debug' + run: | + mv target/${{matrix.profile}}/libwebtransport.so target/${{matrix.profile}}/libwebtransport_aarch64.so + - name: Upload artifact (release) + uses: actions/upload-artifact@master + if: | + (matrix.job == 'build' && matrix.profile == 'release') && + ((github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) + with: + name: release + path: | + target/${{matrix.profile}}/webtransport.dll + target/${{matrix.profile}}/libwebtransport.so + target/${{matrix.profile}}/libwebtransport_aarch64.so + target/${{matrix.profile}}/libwebtransport.dylib + - name: Upload artifact (debug) + uses: actions/upload-artifact@master + if: | + matrix.job == 'build' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') + with: + name: debug + path: | + target/${{matrix.profile}}/webtransport.dll + target/${{matrix.profile}}/libwebtransport.so + target/${{matrix.profile}}/libwebtransport_aarch64.so + target/${{matrix.profile}}/libwebtransport.dylib + - name: Upload release to GitHub + uses: softprops/action-gh-release@59c3b4891632ff9a897f99a91d7bc557467a3a22 if: | - (startsWith(matrix.os, 'self-hosted')) && - (matrix.job == 'test' && matrix.profile == 'release') && (matrix.use_sysroot || - (github.repository == 'hironichu/webtransport' && - (github.ref == 'refs/heads/main' || - startsWith(github.ref, 'refs/tags/')))) + (matrix.job == 'build' && matrix.profile == 'release') && + github.repository == 'hironichu/webtransport' && + github.ref == 'refs/heads/main' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: | + target/${{matrix.profile}}/webtransport.dll + target/${{matrix.profile}}/libwebtransport.so + target/${{matrix.profile}}/libwebtransport_aarch64.so + target/${{matrix.profile}}/libwebtransport.dylib + draft: true + test: + needs: build + if: | + github.event_name == 'push' || !startsWith(github.event.pull_request.head.label, 'hironichu:') + strategy: + matrix: + os: [ 'ubuntu-latest', 'self-hosted'] + job: [test] + profile: [debug, release] + name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + + steps: + - uses: actions/checkout@master + - name: Creating target structure run: | - deno task build:release-arm + mkdir -p target + + - name: Download artifact + uses: actions/download-artifact@master + with: + path: target + - name: Display structure of downloaded files + run: ls -R + - name: Install Deno from .land + if: matrix.os != 'self-hosted' + uses: denoland/setup-deno@v1 + with: + deno-version: v1.x + + - name: Install Deno from source + if: matrix.os == 'self-hosted' + run: | + echo "Check if Deno is already installed" + if ! type deno > /dev/null; then + echo "Deno is not installed, installing..." + curl -s https://gist.githubusercontent.com/LukeChannings/09d53f5c364391042186518c8598b85e/raw/ac8cd8c675b985edd4b3e16df63ffef14d1f0e24/deno_install.sh | sh + else + echo "Deno is already installed" + fi - name: Run deno test (debug) if: | matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') @@ -194,19 +203,4 @@ jobs: BUILD_TARGET: release DEVELOPMENT: true run: | - deno task test - - name: Upload release to GitHub - uses: softprops/action-gh-release@59c3b4891632ff9a897f99a91d7bc557467a3a22 - if: | - (matrix.job == 'test' && matrix.profile == 'release') && - github.repository == 'hironichu/webtransport' && - github.ref == 'refs/heads/main' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - files: | - dist/webtransport.dll - dist/libwebtransport.so - dist/libwebtransport_aarch64.so - dist/libwebtransport.dylib - draft: true \ No newline at end of file + deno task test \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 156432a..ea236c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "webtransport" +name = "Webtransport" description = "Deno WebTransport FFI library for Deno" version = "0.1.0" edition = "2021" @@ -47,6 +47,7 @@ tokio = { version = "=1.32.0", default-features = false, features = [ wtransport = { git = "https://github.com/hironichu/wtransport", branch = "feat/new", features = [ "dangerous-configuration", ] } +wtransport-proto = { git = "https://github.com/hironichu/wtransport", branch = "feat/new" } rcgen = "=0.11.1" ring = "=0.16.20" time = "=0.3.28" diff --git a/deno.jsonc b/deno.jsonc index c7c98b4..3b25d9e 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -6,17 +6,16 @@ "example:web": "deno run -A ./examples/web_server/web.js", "example:server": "deno run -A --unstable ./examples/deno/wt_server.ts", "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", - // Build task - "build:release": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release", - "build:release-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options --release && mv ./dist/libwebtransport.so ./dist/libwebtransport_aarch64.so", - "build:debug": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options", - "build:debug-arm": "deno task util:clean && cargo build --out-dir ./dist -Z unstable-options && mv ./dist/libwebtransport.so ./dist/libwebtransport_aarch64.so", + // CI Build task + "build:release": "cargo clean && cargo build --release", + "build:debug": "cargo clean && cargo build", //Utils - "util:clean": "deno run --allow-write --allow-read ./utils/clean.js", "util:fmt": "deno fmt --unstable", "util:lint": "deno lint --unstable", //Tests - "test": "deno test -A --unstable examples/deno/wt_server_test.ts" + "test": "deno test -A --unstable ", + //Non CI build + "build": "cargo build" }, "compilerOptions": { "checkJs": true, diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts index 1b8b1eb..9094858 100644 --- a/examples/deno/wt_client_test.ts +++ b/examples/deno/wt_client_test.ts @@ -35,6 +35,7 @@ Deno.test( sanitizeResources: false, }, async () => { + //THis causes panic??????? // const server = new WebTransportServer(4433, { // certFile: "./certs/cert.pem", // keyFile: "./certs/key.pem", diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 00fa685..2d88cbc 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1,25 +1,51 @@ -import "../../mod/mod.ts"; -Deno.test({ name: "Server startup/close" }, async () => { - const server = new WebTransportServer(4433, { - certFile: "./certs/cert.pem", - keyFile: "./certs/key.pem", - maxTimeout: 10, - keepAlive: 3, - }); +// import { join } from "https://deno.land/std@0.201.0/path/mod.ts"; +// import { GenerateCertKeyFile } from "../../mod/crypto.ts"; +// import "../../mod/mod.ts"; +// import { assert } from "https://deno.land/std@0.201.0/assert/assert.ts"; - await server.close(); -}); +// //add certs cleanup methods after tests +// const certPath = join(Deno.cwd(), "./certs/"); +// Deno.test({ name: "Server startup/close" }, () => { +// //generate a certificate +// const [cert, key] = GenerateCertKeyFile( +// "localhost", +// 0, +// 10, +// undefined, +// "cert.pem", +// "key.pem", +// ); +// const server = new WebTransportServer(4433, { +// certFile: cert, +// keyFile: key, +// maxTimeout: 10, +// keepAlive: 3, +// }); +// server.close(); +// //try to start a UDP socket on the same port to see if it's closed +// const sock = Deno.listenDatagram({ +// hostname: "0.0.0.0", +// port: 4433, +// transport: "udp", +// }); +// assert(sock, "Server did not close"); +// sock.close(); +// }); -// Deno.test( -// { name: "Server with generated certificate startup/close" }, -// async () => { -// const server = new WebTransportServer(4434, { -// maxTimeout: 10, -// keepAlive: 3, -// notAfter: 10, -// notBefore: 0, -// domain: "localhost", -// }); -// await server.close(); -// }, -// ); +// // Deno.test( +// // { name: "Server with generated certificate startup/close" }, +// // () => { +// // const server = new WebTransportServer(4433, { +// // maxTimeout: 10, +// // keepAlive: 3, +// // notAfter: 10, +// // notBefore: 0, +// // domain: "localhost", +// // }); +// // server.close(); +// // }, +// // ); + +// Deno.test({ name: "Server Cleanup certs" }, () => { +// Deno.removeSync(certPath, { recursive: true }); +// }); diff --git a/mod/client.ts b/mod/client.ts index 78f8042..cbf1c64 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -84,7 +84,7 @@ export class WebTransport extends EventEmitter { private connection(client: Deno.PointerValue) { const CONN_BUFFER = new Uint8Array(65536); // - window.WTLIB.symbols.proc_client_init_streams( + window.WTLIB.symbols.proc_init_datagrams( client, CONN_BUFFER, CONN_BUFFER.byteLength, diff --git a/mod/crypto.ts b/mod/crypto.ts index 0822482..0ca41f3 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -71,12 +71,26 @@ export function GenerateCertKeyFile( domainStr: string, start: number, end: number, - path = join(Deno.cwd(), "./certs/"), + path?: string, + keyFileName?: string, + certFileName?: string, ) { + if (start > end) throw new Error("Invalid date range"); + path = path ?? join(Deno.cwd(), "./certs/"); + //check path + try { + Deno.statSync(path); + } catch { + console.info("[Webtransport] Creating directory: ", path); + Deno.mkdirSync(path, { + recursive: true, + }); + } const [cert, key] = GenerateCertKey(domainStr, start, end); try { - const certpath = join(path, `${domainStr}.crt`); - const keypath = join(path, `${domainStr}.key`); + const certpath = join(path, `${certFileName ?? domainStr + ".crt"}`); + const keypath = join(path, `${keyFileName ?? domainStr + ".key"}`); + Deno.writeFileSync(certpath, cert, { mode: 0o444, // createNew: true, diff --git a/mod/deps.ts b/mod/deps.ts index eed783d..b0cd8ed 100644 --- a/mod/deps.ts +++ b/mod/deps.ts @@ -1,3 +1,5 @@ +import "https://deno.land/std@0.201.0/dotenv/load.ts"; + export { dlopen, type FetchOptions, diff --git a/mod/interface.ts b/mod/interface.ts index 4567352..409c218 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -51,11 +51,6 @@ export const symbols = { result: "pointer", callback: true, }, - proc_server_init_streams: { - parameters: ["pointer", "buffer", "usize"], - result: "void", - nonblocking: true, - }, proc_server_close: { parameters: ["pointer"], result: "usize", @@ -81,11 +76,6 @@ export const symbols = { result: "pointer", callback: true, }, - proc_client_init_streams: { - parameters: ["pointer", "buffer", "usize"], - result: "void", - nonblocking: true, - }, proc_client_close: { parameters: ["pointer", "pointer"], result: "void", @@ -102,6 +92,11 @@ export const symbols = { result: "void", nonblocking: false, }, + proc_init_datagrams: { + parameters: ["pointer", "buffer", "usize"], + result: "void", + nonblocking: true, + }, // Crypto symbols proc_gencert: { parameters: [ diff --git a/mod/lib.ts b/mod/lib.ts index 25d2b55..2961e60 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -1,11 +1,19 @@ import { dlopen, FetchOptions } from "./deps.ts"; import LIB_URL from "../utils/download_lib.ts"; import { symbols } from "./interface.ts"; +import { CacheSetting } from "https://deno.land/x/plug@1.0.2/mod.ts"; + +const cache: CacheSetting = Deno.env.has("DEVELOPMENT") ? "reloadAll" : "only"; const options: FetchOptions = { name: "webtransport", - cache: "reloadAll", + cache: cache, url: LIB_URL!, + suffixes: { + linux: { + "aarch64": "_aarch64", + }, + }, }; export const LIB = async () => await dlopen(options, symbols); diff --git a/mod/server.ts b/mod/server.ts index 1a55a55..c70c463 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -81,7 +81,7 @@ export class WebTransportServer extends EventEmitter { */ private connection(client: Deno.PointerValue) { const CONN_BUFFER = new Uint8Array(65536); - window.WTLIB.symbols.proc_server_init_streams( + window.WTLIB.symbols.proc_init_datagrams( client, CONN_BUFFER, CONN_BUFFER.byteLength, diff --git a/src/client.rs b/src/client.rs index 11ad669..ed9f6e6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,7 @@ -use std::{path::Path, slice::from_raw_parts_mut, time::Duration}; +use std::{path::Path, time::Duration}; use wtransport::{endpoint, tls::Certificate, ClientConfig, Endpoint}; -use crate::{connection::Conn, executor, CLIENT_CONN_FN, RUNTIME, SEND_FN}; +use crate::{connection::Conn, CLIENT_CONN_FN, RUNTIME, SEND_FN}; pub struct WebTransportClient { pub client: Option>, @@ -118,82 +118,6 @@ pub unsafe extern "C" fn proc_client_connect( client.connect(url.to_string()); } -#[no_mangle] -pub unsafe extern "C" fn proc_client_init_streams( - clientptr: *mut Conn, - buffer: *mut u8, - buflen: usize, -) { - assert!(!clientptr.is_null()); - - let client = &mut *clientptr; - let sender = client.datagram_ch_sender.clone(); - - client.buffer = Some(from_raw_parts_mut(buffer, buflen)); - - executor::spawn(async move { - loop { - tokio::select! { - _ = client.conn.accept_bi() => { - // match stream { - // Ok(mut stream) => { - - // println!("Accepted BI stream"); - // let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); - // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // println!("Received (bi) '{str_data}' from client"); - - // stream.0.write_all(b"ACK").await.unwrap(); - // }, - // _ => { - // client.conn.closed().await; - // } - // }; - return client.conn.closed().await; - } - _ = client.conn.accept_uni() => { - //close the connection until we implement the uni stream - return client.conn.closed().await; - // match stream { - // Ok(mut stream) => { - // println!("Accepted UNI stream"); - // let bytes_read = match stream.read(&mut buffer).await.unwrap() { - // Some(bytes_read) => bytes_read, - // None => continue, - // }; - - // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // println!("Received (uni) '{str_data}' from client"); - - // let mut stream = client.conn.open_uni().await.unwrap().await.unwrap(); - // stream.write_all(b"ACK").await.unwrap(); - // }, - // _ => { - // client.conn.closed().await; - // } - // } - - } - stream = client.conn.receive_datagram() => { - match stream { - Ok(dgram) => sender.send_async(dgram).await.unwrap(), - _ => { - client.conn.closed().await; - //TODO(hironichu): Send action to Deno to free the pointer and buffer - // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); - return ; - } - } - }, - - } - } - }) - .detach(); -} - #[no_mangle] pub unsafe extern "C" fn proc_client_close( client_ptr: *mut WebTransportClient, @@ -211,4 +135,8 @@ pub unsafe extern "C" fn proc_client_close( } #[no_mangle] -pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn) {} +pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn) { + let _con = &mut *_b; + drop(_con.datagram_ch_receiver.drain()); + drop(_con.datagram_ch_sender.downgrade()); +} diff --git a/src/connection.rs b/src/connection.rs index dc7b37a..3a4a709 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,6 +1,8 @@ use flume::{Receiver, Sender}; use wtransport::{datagram::Datagram, Connection}; +use wtransport_proto::varint::VarInt; +use crate::executor; pub struct Conn { pub conn: Connection, pub buffer: Option<&'static mut [u8]>, @@ -10,7 +12,7 @@ pub struct Conn { impl Conn { pub(crate) fn new(conn: Connection) -> Self { - let (sender, receiver) = flume::bounded(1); + let (sender, receiver) = flume::bounded(2); Self { conn, @@ -23,4 +25,43 @@ impl Conn { pub async fn closed(&mut self) { self.conn.closed().await } + + pub fn close(&mut self, code: u32, reason: Option<&[u8]>) { + let reason = match reason { + Some(reason) => reason, + None => b"closed", + }; + self.conn.close(VarInt::from_u32(code), reason); + } + + pub fn datagrams(&'static mut self) { + executor::spawn(async move { + loop { + tokio::select! { + stream = self.conn.receive_datagram() => { + match stream { + Ok(dgram) => match self.datagram_ch_sender.send_async(dgram).await { + Ok(_) => {} + Err(_) => { + //We should close the connection from Deno. + self.conn.closed().await; + // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); + return ; + } + }, + _ => { + //We should close the connection from Deno. + self.conn.closed().await; + //TODO(hironichu): Send action to Deno to free the pointer and buffer + // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); + return ; + } + } + }, + + } + } + }) + .detach(); + } } diff --git a/src/server.rs b/src/server.rs index 5d57e2a..e303c76 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,8 +1,8 @@ -use std::{path::Path, slice::from_raw_parts_mut, time::Duration}; +use std::{path::Path, time::Duration}; use tokio::runtime::Runtime; use wtransport::{endpoint, tls::Certificate, Endpoint, ServerConfig}; -use crate::{connection::Conn, executor, RUNTIME, SEND_FN, SERVER_CONN_FN}; +use crate::{connection::Conn, RUNTIME, SEND_FN, SERVER_CONN_FN}; pub struct WebTransportServer { pub server: Option>, @@ -137,82 +137,6 @@ pub unsafe extern "C" fn proc_server_listen( server.handle_sess_in(); } -#[no_mangle] -pub unsafe extern "C" fn proc_server_init_streams( - clientptr: *mut Conn, - buffer: *mut u8, - buflen: usize, -) { - assert!(!clientptr.is_null()); - - let client = &mut *clientptr; - let sender = client.datagram_ch_sender.clone(); - - client.buffer = Some(from_raw_parts_mut(buffer, buflen)); - - executor::spawn(async move { - loop { - tokio::select! { - _ = client.conn.accept_bi() => { - // match stream { - // Ok(mut stream) => { - - // println!("Accepted BI stream"); - // let bytes_read = stream.1.read(&mut buffer).await.unwrap().unwrap(); - // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // println!("Received (bi) '{str_data}' from client"); - - // stream.0.write_all(b"ACK").await.unwrap(); - // }, - // _ => { - // client.conn.closed().await; - // } - // }; - return client.conn.closed().await; - } - _ = client.conn.accept_uni() => { - //close the connection until we implement the uni stream - return client.conn.closed().await; - // match stream { - // Ok(mut stream) => { - // println!("Accepted UNI stream"); - // let bytes_read = match stream.read(&mut buffer).await.unwrap() { - // Some(bytes_read) => bytes_read, - // None => continue, - // }; - - // let str_data = std::str::from_utf8(&buffer[..bytes_read]).unwrap(); - - // println!("Received (uni) '{str_data}' from client"); - - // let mut stream = client.conn.open_uni().await.unwrap().await.unwrap(); - // stream.write_all(b"ACK").await.unwrap(); - // }, - // _ => { - // client.conn.closed().await; - // } - // } - - } - stream = client.conn.receive_datagram() => { - match stream { - Ok(dgram) => sender.send_async(dgram).await.unwrap(), - _ => { - client.conn.closed().await; - //TODO(hironichu): Send action to Deno to free the pointer and buffer - // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); - return ; - } - } - }, - - } - } - }) - .detach(); -} - #[no_mangle] pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) -> usize { assert!(!server_ptr.is_null()); @@ -229,4 +153,7 @@ pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) pub unsafe extern "C" fn free_all_server(_a: *mut WebTransportServer, _c: *mut Runtime) {} #[no_mangle] -pub unsafe extern "C" fn free_server(_: *mut WebTransportServer) {} +pub unsafe extern "C" fn free_server(_v: *mut WebTransportServer) { + let _s = &mut *_v; + drop(_s.server.take()); +} diff --git a/src/shared.rs b/src/shared.rs index ccaf9e6..014646a 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -44,6 +44,14 @@ pub unsafe extern "C" fn proc_send_datagram(connptr: *mut Conn, buf: *const u8, } } } +#[no_mangle] +pub unsafe extern "C" fn proc_init_datagrams(conn_ptr: *mut Conn, buffer: *mut u8, buflen: usize) { + assert!(!conn_ptr.is_null()); + + let connection = &mut *conn_ptr; + connection.buffer = Some(from_raw_parts_mut(buffer, buflen)); + connection.datagrams(); +} #[no_mangle] pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn, _buf: *mut u8) { diff --git a/utils/clean.js b/utils/clean.js deleted file mode 100644 index c459be9..0000000 --- a/utils/clean.js +++ /dev/null @@ -1,15 +0,0 @@ -if (import.meta.main) { - try { - Deno.statSync("./dist"); - const rule = /^(.*)\.so$|^(.*)\.dll$|^(.*)\.dylib$/; - const files = Deno.readDirSync("./dist"); - for (const file of files) { - if (rule.test(file.name)) { - Deno.removeSync("./dist/" + file.name); - } - } - console.info(`Cleaned up ./dist`); - } catch { - console.error(`Count not find ./dist`); - } -} diff --git a/utils/download_lib.ts b/utils/download_lib.ts index 2bb9bb9..01a3430 100644 --- a/utils/download_lib.ts +++ b/utils/download_lib.ts @@ -30,37 +30,14 @@ if (!Deno.env.get("DEVELOPMENT")) { if (json.length === 0) { throw new Error("No release found"); } - - switch (Deno.build.os) { - case "windows": - LIB_URL = new URL( - // json[0].assets.filter((item: { name: string }) => - // item.name.endsWith(".dll") - // )[0].url, - json[0].assets[`${LIB_NAME}.dll`][0].url, - ); - break; - case "linux": - { - let filename = LIB_NAME; - if (Deno.build.arch === "aarch64") { - filename = `lib${LIB_NAME}_aarch64.so`; - } else { - filename = `lib${LIB_NAME}.so`; - } - LIB_URL = new URL( - json[0].assets[filename][0].url, - ); - } - break; - case "darwin": - LIB_URL = new URL( - json[0].assets[`${LIB_NAME}.dylib`][0].url, - ); - break; - } + LIB_URL = new URL( + json[0] + .assets[ + `${LIB_NAME}_${Deno.build.os}_${Deno.build.arch}` + ][0].url, + ); LIB_URL!.username = Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]!; } - -LIB_URL = LIB_URL ?? new URL("../dist/", import.meta.url); +const build_target = Deno.env.get("DEVELOPMENT") ? "debug" : "release"; +LIB_URL = LIB_URL ?? new URL(`../target/${build_target}/`, import.meta.url); export default LIB_URL; From 9a7418a32507d3b1755a60b021c7f9c589bd0f41 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Tue, 19 Sep 2023 21:16:10 +0200 Subject: [PATCH 41/72] Added safety and fixes --- examples/deno/wt_client.ts | 5 +++- examples/deno/wt_gencert.ts | 11 ++++++++ examples/deno/wt_server.ts | 22 ++++++++++++++-- examples/web_server/web.js | 2 +- mod/client.ts | 52 +++++++++++++++++++++++-------------- mod/connection.ts | 4 ++- mod/interface.ts | 4 +-- mod/server.ts | 5 ++-- src/lib.rs | 2 +- 9 files changed, 76 insertions(+), 31 deletions(-) create mode 100644 examples/deno/wt_gencert.ts diff --git a/examples/deno/wt_client.ts b/examples/deno/wt_client.ts index 65121ab..9853a96 100644 --- a/examples/deno/wt_client.ts +++ b/examples/deno/wt_client.ts @@ -1,7 +1,10 @@ //TO BE IMPLEMENTED import "../../mod/mod.ts"; -const client = new WebTransport("https://localhost:4433", { +//get the connect address from args 1 +const connectAddr = Deno.args[0] ?? "https://localhost:4433"; + +const client = new WebTransport(connectAddr, { validateCertificate: false, maxTimeout: 10, keepAlive: 3, diff --git a/examples/deno/wt_gencert.ts b/examples/deno/wt_gencert.ts new file mode 100644 index 0000000..47725af --- /dev/null +++ b/examples/deno/wt_gencert.ts @@ -0,0 +1,11 @@ +import "../../mod/mod.ts"; +import { GenerateCertKeyFile } from "../../mod/crypto.ts"; + +const hostname = Deno.args[0] ?? "localhost"; + +if (typeof hostname !== "string" || hostname.length == 0) { + console.error("Invalid hostname"); + Deno.exit(1); +} + +GenerateCertKeyFile(hostname, 0, 10); \ No newline at end of file diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index d56e409..f2781f8 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -1,12 +1,30 @@ //TO BE IMPLEMENTED import "../../mod/mod.ts"; +//get cert path from args 1 and 2 (cert and key) or use default +const certFile = Deno.args[0] ?? "./certs/localhost.crt"; +const keyFile = Deno.args[1] ?? "./certs/localhost.key"; +//check if certFile and keyFile are valid non-empty strings +if (typeof certFile !== "string" || typeof keyFile !== "string") { + console.error("Invalid certFile or keyFile"); + Deno.exit(1); +} +//check path +try { + Deno.statSync(certFile); + Deno.statSync(keyFile); +} catch (e) { + console.error("Invalid certFile or keyFile"); + Deno.exit(1); +} + const server = new WebTransportServer(4433, { - certFile: "./certs/cert.pem", - keyFile: "./certs/key.pem", + certFile: "./certs/localhost.crt", + keyFile: "./certs/localhost.key", maxTimeout: 10, keepAlive: 3, }); + console.log("Server created"); server.on("listening", (e) => { console.log("Listening OUT", e); diff --git a/examples/web_server/web.js b/examples/web_server/web.js index 678cd51..a80282d 100644 --- a/examples/web_server/web.js +++ b/examples/web_server/web.js @@ -2,7 +2,7 @@ import { readCertFile } from "../../mod/crypto.ts"; //grab the first argument as the IP to connect to (defaults to localhost) const ip = Deno.args[0] || "localhost"; -const certFile = readCertFile("./certs/cert.pem"); +const certFile = readCertFile("./certs/localhost.crt"); const certHash = Array.from( new Uint8Array(await crypto.subtle.digest("SHA-256", certFile)), ); diff --git a/mod/client.ts b/mod/client.ts index cbf1c64..efb8baf 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -1,8 +1,7 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } -import { - WebTransportConnection, +import WebTransportConnection, { WebTransportDatagramDuplexStream, } from "./connection.ts"; import { EventEmitter } from "./deps.ts"; @@ -51,29 +50,42 @@ export class WebTransport extends EventEmitter { const certbuf = encodeBuf(certificate); const keybuf = encodeBuf(key); - this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( - this.#NOTIFY_PTR.pointer, - _options.keepAlive, - _options.maxTimeout, - _options.validateCertificate, - certbuf[0], - certbuf[1], - keybuf[0], - keybuf[1], - ); + try { + this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( + this.#NOTIFY_PTR.pointer, + _options.keepAlive, + _options.maxTimeout, + _options.validateCertificate, + certbuf[0], + certbuf[1], + keybuf[0], + keybuf[1], + ); + } catch (e) { + console.error(e); + } if (!this.#CONN_PTR) { throw new Error("Failed to initialize client"); } + + if (_client instanceof URL) { + _client = _client.href; + } + const addr = encodeBuf(_client.toString()); - window.WTLIB.symbols.proc_client_connect( - this.#CONN_PTR, - this.#CONNECTION_CB.pointer, - addr[0], - addr[1], - ); - this.#NOTIFY_PTR.ref(); - this.#CONNECTION_CB.ref(); + try { + window.WTLIB.symbols.proc_client_connect( + this.#CONN_PTR, + this.#CONNECTION_CB.pointer, + addr[0], + addr[1], + ); + this.#NOTIFY_PTR.ref(); + this.#CONNECTION_CB.ref(); + } catch (e) { + console.error("Could not connect to the server", e); + } } /** * @callback connection diff --git a/mod/connection.ts b/mod/connection.ts index b73a0af..20e9930 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -1,3 +1,5 @@ +// import { FFI_CODES } from "./code.ts"; + export class WebTransportDatagramDuplexStream { #READ_BUFFER: Uint8Array; readonly incomingHighWaterMark = 1; @@ -64,7 +66,7 @@ export class WebTransportDatagramDuplexStream { > = undefined; } -export class WebTransportConnection { +export default class WebTransportConnection { state: "connected" | "closed" | "draining" | "failed" | "connecting" = "connected" as const; diff --git a/mod/interface.ts b/mod/interface.ts index 409c218..ca4b011 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -122,7 +122,5 @@ export const symbols = { free_all_client: { parameters: ["pointer", "pointer"], result: "void", - }, + } } as const; - -//change the type of window so we add Webtransport and WebtransportServer diff --git a/mod/server.ts b/mod/server.ts index c70c463..4425a05 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -1,7 +1,7 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } -import { WebTransportConnection } from "./connection.ts"; +import WebTransportConnection from "./connection.ts"; import { GenerateCertKeyFile } from "./crypto.ts"; import { EventEmitter } from "./deps.ts"; import { @@ -80,7 +80,8 @@ export class WebTransportServer extends EventEmitter { * @description This function is called when a new connection is received from the server */ private connection(client: Deno.PointerValue) { - const CONN_BUFFER = new Uint8Array(65536); + const SHARED_BUF = new SharedArrayBuffer(65536); + const CONN_BUFFER = new Uint8Array(SHARED_BUF); window.WTLIB.symbols.proc_init_datagrams( client, CONN_BUFFER, diff --git a/src/lib.rs b/src/lib.rs index 3b5a718..d004ec4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ use tokio::runtime::Runtime; static mut SEND_FN: Option = None; static mut SERVER_CONN_FN: Option = None; static mut CLIENT_CONN_FN: Option = None; -static mut RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); +static RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); ///------------------------------------ mod certificate; pub mod client; From c23ef68478318866da676f977f2dc0fccdea4dc5 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 00:52:55 +0200 Subject: [PATCH 42/72] Re-Writen Connection (rust side) and fixed Lib loading without Plug. --- deno.jsonc | 7 +- examples/deno/wt_client.ts | 3 +- examples/deno/wt_gencert.ts | 2 +- examples/deno/wt_server.ts | 2 +- examples/web_server/index.html | 2 +- examples/web_server/web.js | 6 +- mod/client.ts | 31 +-------- mod/code.ts | 2 + mod/connection.ts | 12 ++-- mod/crypto.ts | 8 +-- mod/deps.ts | 9 +-- mod/interface.ts | 12 +--- mod/lib.ts | 115 +++++++++++++++++++++++++++---- mod/mod.ts | 51 +++++++++----- mod/server.ts | 2 +- src/client.rs | 54 +++++---------- src/connection.rs | 119 ++++++++++++++++++++++++++------- src/executor.rs | 24 +++---- src/lib.rs | 6 +- src/server.rs | 24 ++++--- src/shared.rs | 25 +++++-- test.ts | 0 22 files changed, 332 insertions(+), 184 deletions(-) create mode 100644 test.ts diff --git a/deno.jsonc b/deno.jsonc index 3b25d9e..262b145 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -3,9 +3,10 @@ }, "tasks": { //Examples - "example:web": "deno run -A ./examples/web_server/web.js", - "example:server": "deno run -A --unstable ./examples/deno/wt_server.ts", - "example:client": "deno run -A --unstable ./examples/deno/wt_client.ts", + "demo:web": "deno run -A ./examples/web_server/web.js", + "demo:server": "deno run -A --unstable ./examples/deno/wt_server.ts", + "demo:client": "deno run -A --unstable ./examples/deno/wt_client.ts", + "demo:gencert": "deno run -A --unstable ./examples/deno/wt_gencert.ts", // CI Build task "build:release": "cargo clean && cargo build --release", "build:debug": "cargo clean && cargo build", diff --git a/examples/deno/wt_client.ts b/examples/deno/wt_client.ts index 9853a96..68c0cfc 100644 --- a/examples/deno/wt_client.ts +++ b/examples/deno/wt_client.ts @@ -1,11 +1,10 @@ //TO BE IMPLEMENTED import "../../mod/mod.ts"; -//get the connect address from args 1 +//get the connect address from args 1 const connectAddr = Deno.args[0] ?? "https://localhost:4433"; const client = new WebTransport(connectAddr, { - validateCertificate: false, maxTimeout: 10, keepAlive: 3, }); diff --git a/examples/deno/wt_gencert.ts b/examples/deno/wt_gencert.ts index 47725af..6849f8a 100644 --- a/examples/deno/wt_gencert.ts +++ b/examples/deno/wt_gencert.ts @@ -8,4 +8,4 @@ if (typeof hostname !== "string" || hostname.length == 0) { Deno.exit(1); } -GenerateCertKeyFile(hostname, 0, 10); \ No newline at end of file +GenerateCertKeyFile(hostname, 0, 10); diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index f2781f8..af5dd4b 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -13,7 +13,7 @@ if (typeof certFile !== "string" || typeof keyFile !== "string") { try { Deno.statSync(certFile); Deno.statSync(keyFile); -} catch (e) { +} catch { console.error("Invalid certFile or keyFile"); Deno.exit(1); } diff --git a/examples/web_server/index.html b/examples/web_server/index.html index 812740e..090fad5 100644 --- a/examples/web_server/index.html +++ b/examples/web_server/index.html @@ -64,7 +64,7 @@

Server IP : HOST_IP

const encoder = new TextEncoder(); console.log("start sending") while (1) { - if (int2 === 100_000) { + if (int2 === 500) { break; } await window.writeDDBN.write(encoder.encode(int2++)); diff --git a/examples/web_server/web.js b/examples/web_server/web.js index a80282d..e1daa0e 100644 --- a/examples/web_server/web.js +++ b/examples/web_server/web.js @@ -1,11 +1,11 @@ import { readCertFile } from "../../mod/crypto.ts"; + //grab the first argument as the IP to connect to (defaults to localhost) const ip = Deno.args[0] || "localhost"; const certFile = readCertFile("./certs/localhost.crt"); -const certHash = Array.from( - new Uint8Array(await crypto.subtle.digest("SHA-256", certFile)), -); +const crtdata = new Uint8Array(await crypto.subtle.digest("SHA-256", certFile)); +const certHash = Array.from(crtdata); let indexHTML = Deno.readTextFileSync("./examples/web_server/index.html"); diff --git a/mod/client.ts b/mod/client.ts index efb8baf..e7d2213 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -43,23 +43,17 @@ export class WebTransport extends EventEmitter { constructor( _client: URL | string, - _options: typeof WebTransportOptions = ServerOpts, + _options: WebTransportOptions = ServerOpts, ) { super(); - const [certificate, key] = this.checkArgs(_options); + // Server Certificate check is not implemented yet + // const [certificate, key] = this.checkArgs(_options); - const certbuf = encodeBuf(certificate); - const keybuf = encodeBuf(key); try { this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( this.#NOTIFY_PTR.pointer, _options.keepAlive, _options.maxTimeout, - _options.validateCertificate, - certbuf[0], - certbuf[1], - keybuf[0], - keybuf[1], ); } catch (e) { console.error(e); @@ -170,25 +164,6 @@ export class WebTransport extends EventEmitter { this.emit("close", new CloseEvent("close")); } - - private checkArgs(_options: WebTransportOptions) { - let certificate = ""; - let key = ""; - if (_options.certFile && _options.keyFile) { - Deno.statSync(_options.certFile); - Deno.statSync(_options.keyFile); - // - certificate = _options.certFile; - key = _options.keyFile; - return [certificate, key]; - } - if (_options.validateCertificate == false) { - return ["", ""]; - } - throw new TypeError( - "Missing necessary parameters", - ); - } } export default WebTransport; diff --git a/mod/code.ts b/mod/code.ts index 878d7c3..c8b4a6b 100644 --- a/mod/code.ts +++ b/mod/code.ts @@ -1,3 +1,5 @@ +// This needs to be implemented in some way... might not be the smartest way to do it though + export const C_TYPES = { "PROC_OK": 0, "PROC_ERR": 1, diff --git a/mod/connection.ts b/mod/connection.ts index 20e9930..13a265c 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -67,11 +67,15 @@ export class WebTransportDatagramDuplexStream { } export default class WebTransportConnection { - state: "connected" | "closed" | "draining" | "failed" | "connecting" = - "connected" as const; + state: + | "connected" + | "closed" + | "draining" + | "failed" + | "connecting" = "connected" as const; - #CONN_PTR: Deno.PointerValue; - public datagrams: WebTransportDatagramDuplexStream; + readonly #CONN_PTR: Deno.PointerValue; + public readonly datagrams: WebTransportDatagramDuplexStream; constructor( pointer: Deno.PointerValue, buffer: Uint8Array, diff --git a/mod/crypto.ts b/mod/crypto.ts index 0ca41f3..576cada 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -36,16 +36,16 @@ export function GenerateCertKey( if (domainStr.length === 0) throw new Error("Invalid domain name"); const domain = encodeBuf(domainStr); - const certBUFF = new Uint8Array(1024); - const keyBUFF = new Uint8Array(1024); + const certBUFF = new Uint8Array(2048); const certLenPTR = new Uint32Array(1); + const keyBUFF = new Uint8Array(2048); const keyLenPTR = new Uint32Array(1); try { const struct = window.WTLIB.symbols.proc_gencert( domain[0], domain[1], - 2, - 10, + start, + end, certBUFF, certLenPTR, keyBUFF, diff --git a/mod/deps.ts b/mod/deps.ts index b0cd8ed..7f7d6fd 100644 --- a/mod/deps.ts +++ b/mod/deps.ts @@ -1,8 +1,3 @@ -import "https://deno.land/std@0.201.0/dotenv/load.ts"; - -export { - dlopen, - type FetchOptions, -} from "https://deno.land/x/plug@1.0.2/mod.ts"; - +import "https://deno.land/std@0.202.0/dotenv/load.ts"; +export { toFileUrl } from "https://deno.land/std@0.202.0/path/mod.ts"; export { EventEmitter } from "https://deno.land/x/event@2.0.1/mod.ts"; diff --git a/mod/interface.ts b/mod/interface.ts index ca4b011..02634e4 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -1,7 +1,6 @@ export const WebTransportOptions = { maxTimeout: 10, keepAlive: 3, - validateCertificate: true, }; export const CertificateOptions = { @@ -26,9 +25,7 @@ export type WebTransportServerOptions = & typeof WebTransportServerOptions & Partial; -export type WebTransportOptions = - & typeof WebTransportOptions - & Partial; +export type WebTransportOptions = typeof WebTransportOptions; export const symbols = { // Server symbols proc_server_init: { @@ -62,11 +59,6 @@ export const symbols = { "function", "u64", //KeepAlive "u64", //MaxTimeout - "bool", //Certcheck - "buffer", //Cert - "usize", //CertLen - "buffer", //Key - "usize", //KeyLen ], result: "pointer", callback: true, @@ -122,5 +114,5 @@ export const symbols = { free_all_client: { parameters: ["pointer", "pointer"], result: "void", - } + }, } as const; diff --git a/mod/lib.ts b/mod/lib.ts index 2961e60..e299762 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -1,21 +1,110 @@ -import { dlopen, FetchOptions } from "./deps.ts"; +import { toFileUrl } from "./deps.ts"; import LIB_URL from "../utils/download_lib.ts"; import { symbols } from "./interface.ts"; -import { CacheSetting } from "https://deno.land/x/plug@1.0.2/mod.ts"; -const cache: CacheSetting = Deno.env.has("DEVELOPMENT") ? "reloadAll" : "only"; +let local = false; +let download_lib: URL; +if (import.meta.url.startsWith("file://")) { + local = true; + download_lib = new URL(`http://localhost/`); +} else { + local = false; + if (LIB_URL) { + download_lib = LIB_URL; + } else { + throw new Error("LIB_URL is not defined"); + } +} +const askPerm = async (): Promise => { + if ((await Deno.permissions.query({ name: "write" })).state !== "granted") { + console.info( + `We need to download the library to use this module, please grant write permission to Deno.`, + ); + await Deno.permissions.request({ + name: "write", + path: Deno.cwd(), + }); + } -const options: FetchOptions = { - name: "webtransport", - cache: cache, - url: LIB_URL!, - suffixes: { - linux: { - "aarch64": "_aarch64", - }, - }, + if ( + (await Deno.permissions.query({ name: "read" })) + .state !== + "granted" + ) { + console.info( + `We need to download the library to use this module, please grant read permission to Deno.`, + ); + await Deno.permissions.request({ + name: "read", + }); + } }; +const currentPath = toFileUrl(Deno.cwd() + "/"); + +const dirpath = local ? "./target/debug/" : "./.lib/"; +let [fileExt, fileprefix] = ["", Deno.build.os === "windows" ? "" : "lib"]; +switch (Deno.build.os) { + case "windows": + fileExt = ".dll"; + break; + case "linux": + fileExt = ".so"; + break; + case "darwin": { + fileExt = ".dylib"; + break; + } +} +const buildFilename = local + ? `${fileprefix}webtransport${fileExt}` + : `${fileprefix}webtransport_${Deno.build.arch}_${fileExt}`; +const DURL = new URL(dirpath + buildFilename, currentPath); +if (!local) { + const remoteLIb = await fetch(download_lib, { + headers: { + Accept: "application/octet-stream", + }, + }); + const remoteBuffer = new Uint8Array(await remoteLIb.arrayBuffer()); + let dirs = false; + try { + const dir = Deno.statSync(`./.lib`); + if (dir.isDirectory) { + dirs = true; + Deno.statSync(DURL); + const file = Deno.readFileSync(DURL); + const localhash = await crypto.subtle.digest("SHA-1", file); + const remotehash = await crypto.subtle.digest( + "SHA-1", + remoteBuffer, + ); + const localhashHex = Array.from(new Uint8Array(localhash)).map(( + b, + ) => b.toString(16).padStart(2, "0")).join(""); + const remotehashHex = Array.from(new Uint8Array(remotehash)).map(( + b, + ) => b.toString(16).padStart(2, "0")).join(""); + if (localhashHex !== remotehashHex) { + await askPerm(); + Deno.writeFileSync(DURL, remoteBuffer); + } + } else { + dirs = false; + throw ""; + } + } catch { + await askPerm(); + if (!dirs) { + Deno.mkdirSync(`./dist`, { recursive: false }); + } + Deno.writeFileSync(DURL, remoteBuffer, { + create: true, + mode: 0o755, + }); + } + console.log(`Downloaded library to ${DURL}`); +} -export const LIB = async () => await dlopen(options, symbols); +export const LIB = async () => await Deno.dlopen(DURL, symbols); export default { LIB, symbols }; diff --git a/mod/mod.ts b/mod/mod.ts index 7d14897..e091583 100644 --- a/mod/mod.ts +++ b/mod/mod.ts @@ -1,29 +1,48 @@ -import { symbols } from "./interface.ts"; +import { + symbols, + WebTransportOptions, + WebTransportServerOptions, +} from "./interface.ts"; import { LIB } from "./lib.ts"; +if (window.WTLIB_STATE) { + throw new Error("[Webtransport] Main module already imported."); +} + window.WTLIB = await LIB(); -import { WebTransportServer as WtServer } from "./server.ts"; -import { WebTransport as WtClient } from "./client.ts"; +window.WTLIB_STATE = true; +import { WebTransportServer as WebTransportServerT } from "./server.ts"; +import { WebTransport as WebTransportT } from "./client.ts"; declare global { export interface Window { WTLIB: Deno.DynamicLibrary; + WTLIB_STATE: boolean; } - export namespace globalThis { - export class WebTransport extends WtClient {} - export class WebTransportServer extends WtServer {} - export let WTLIB: Deno.DynamicLibrary; - } -} -export declare namespace globalThis { - export class WebTransport extends WtClient {} - export class WebTransportServer extends WtServer {} - export let WTLIB: Deno.DynamicLibrary; } -globalThis.WebTransport = WtClient; -globalThis.WebTransportServer = WtServer; +declare global { + namespace globalThis { + export class WebTransport extends WebTransportT { + constructor( + _client: URL | string, + _options: WebTransportOptions, + ); + } + export class WebTransportServer extends WebTransportServerT { + constructor( + _port: number, + _options: WebTransportServerOptions, + ); + } + } +} -export { WtClient, WtServer }; +((globalThis) => { + Object.assign(globalThis, { + WebTransport: WebTransportT, + WebTransportServer: WebTransportServerT, + }); +})(globalThis); // Path: mod/mod.ts diff --git a/mod/server.ts b/mod/server.ts index 4425a05..04faf68 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -10,7 +10,7 @@ import { } from "./interface.ts"; import { encodeBuf } from "./utils.ts"; -type WebTransportServerEvents = { +export type WebTransportServerEvents = { listening: [Event]; connection: [WebTransportConnection]; event: [MessageEvent]; diff --git a/src/client.rs b/src/client.rs index ed9f6e6..f666e08 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,11 +1,14 @@ -use std::{path::Path, time::Duration}; -use wtransport::{endpoint, tls::Certificate, ClientConfig, Endpoint}; +use std::time::Duration; +use wtransport::{endpoint, ClientConfig, Endpoint}; -use crate::{connection::Conn, CLIENT_CONN_FN, RUNTIME, SEND_FN}; +use crate::{ + connection::{self, Client, Conn}, + CLIENT_CONN_FN, RUNTIME, SEND_FN, +}; pub struct WebTransportClient { pub client: Option>, - pub conn_cb: Option, + pub conn_cb: Option)>, pub state: Option, } @@ -35,7 +38,7 @@ impl WebTransportClient { RUNTIME.block_on(async move { match self.client.as_mut().unwrap().connect(url).await { Ok(conn) => { - let client = Conn::new(conn); + let client = Conn::::new(conn); let client_ptr = Box::into_raw(Box::new(client)); assert!(!CLIENT_CONN_FN.is_none()); CLIENT_CONN_FN.unwrap()(client_ptr); @@ -53,11 +56,6 @@ pub unsafe extern "C" fn proc_client_init( send_func: Option, keepalive: u64, timeout: u64, - certcheck: bool, - cert_path: *const u8, - cert_path_len: usize, - key_path: *const u8, - key_path_len: usize, ) -> *mut WebTransportClient { assert!(!send_func.is_none()); @@ -71,30 +69,14 @@ pub unsafe extern "C" fn proc_client_init( } else { Some(Duration::from_secs(timeout)) }; - //print the paths for debug - let config = if certcheck { - let cert_path = ::std::slice::from_raw_parts(cert_path, cert_path_len); - let key_path = ::std::slice::from_raw_parts(key_path, key_path_len); - let cert_path = Path::new(std::str::from_utf8(cert_path).unwrap()); - let key_path = Path::new(std::str::from_utf8(key_path).unwrap()); - let _certificates = Certificate::load(cert_path, key_path).unwrap(); - ClientConfig::builder() - .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) - .with_native_certs() - .keep_alive_interval(keepalive) - .max_idle_timeout(timeout) - .unwrap() - .build() - } else { - ClientConfig::builder() - .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) - .with_no_cert_validation() - .keep_alive_interval(keepalive) - .max_idle_timeout(timeout) - .unwrap() - .build() - }; + let config = ClientConfig::builder() + .with_bind_config(wtransport::config::IpBindConfig::InAddrAnyDual) + .with_no_cert_validation() + .keep_alive_interval(keepalive) + .max_idle_timeout(timeout) + .unwrap() + .build(); let client = WebTransportClient::new(send_func, config); match client { Ok(client) => Box::into_raw(Box::new(client)), @@ -106,7 +88,7 @@ pub unsafe extern "C" fn proc_client_init( #[no_mangle] pub unsafe extern "C" fn proc_client_connect( client: *mut WebTransportClient, - cb: Option, + cb: Option)>, url: *const u8, url_len: usize, ) { @@ -121,7 +103,7 @@ pub unsafe extern "C" fn proc_client_connect( #[no_mangle] pub unsafe extern "C" fn proc_client_close( client_ptr: *mut WebTransportClient, - conn: *mut Conn, + conn: *mut Conn, ) -> usize { assert!(!client_ptr.is_null()); assert!(!conn.is_null()); @@ -135,7 +117,7 @@ pub unsafe extern "C" fn proc_client_close( } #[no_mangle] -pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn) { +pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn) { let _con = &mut *_b; drop(_con.datagram_ch_receiver.drain()); drop(_con.datagram_ch_sender.downgrade()); diff --git a/src/connection.rs b/src/connection.rs index 3a4a709..d3cde0a 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,57 +1,82 @@ +use crate::executor; use flume::{Receiver, Sender}; -use wtransport::{datagram::Datagram, Connection}; +use std::{collections::HashMap, future::Future, marker::PhantomData, pin::Pin}; +use wtransport::{datagram::Datagram, endpoint::SessionRequest, Connection}; use wtransport_proto::varint::VarInt; -use crate::executor; -pub struct Conn { - pub conn: Connection, - pub buffer: Option<&'static mut [u8]>, - pub datagram_ch_sender: Sender, - pub datagram_ch_receiver: Receiver, -} +type DynFutureIncomingSession = dyn Future> + Send + Sync; -impl Conn { - pub(crate) fn new(conn: Connection) -> Self { - let (sender, receiver) = flume::bounded(2); - - Self { - conn, - buffer: None, - datagram_ch_sender: sender, - datagram_ch_receiver: receiver, - } - } +impl Conn { //TODO(hironichu): Add generic methods for openning and closing streams instead of doing it in both client and server. pub async fn closed(&mut self) { - self.conn.closed().await + let conn = self.conn.as_ref().unwrap(); + conn.closed().await } + pub fn open_uni(&'static mut self) { + println!("To be implemented"); + // executor::spawn(async move { + // let conn = self.conn.as_ref().unwrap(); + // let stream = conn.open_uni().await.unwrap(); + // stream.await.unwrap(); + // }) + // .detach(); + } + pub fn open_bi(&'static mut self) { + println!("To be implemented"); + // executor::spawn(async move { + // let conn = self.conn.as_ref().unwrap(); + // let stream = conn.open_bi().await.unwrap(); + // stream.await.unwrap(); + // }) + // .detach(); + } pub fn close(&mut self, code: u32, reason: Option<&[u8]>) { let reason = match reason { Some(reason) => reason, None => b"closed", }; - self.conn.close(VarInt::from_u32(code), reason); + self.conn + .as_ref() + .unwrap() + .close(VarInt::from_u32(code), reason); } +} + +pub struct Server(Pin>); + +/// Type of endpoint opening a WebTransport connection. +pub struct Client; +pub struct Conn { + pub conn: Option, + pub accepted_session: Option, + pub buffer: Option<&'static mut [u8]>, + pub datagram_ch_sender: Sender, + pub datagram_ch_receiver: Receiver, + _marker: PhantomData, +} + +impl Conn { pub fn datagrams(&'static mut self) { executor::spawn(async move { + let conn = self.conn.as_ref().unwrap(); loop { tokio::select! { - stream = self.conn.receive_datagram() => { + stream = conn.receive_datagram() => { match stream { Ok(dgram) => match self.datagram_ch_sender.send_async(dgram).await { Ok(_) => {} Err(_) => { //We should close the connection from Deno. - self.conn.closed().await; + conn.closed().await; // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); return ; } }, _ => { //We should close the connection from Deno. - self.conn.closed().await; + conn.closed().await; //TODO(hironichu): Send action to Deno to free the pointer and buffer // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); return ; @@ -65,3 +90,49 @@ impl Conn { .detach(); } } + +impl Conn { + pub(crate) fn new(accepted_session: SessionRequest) -> Self { + let (sender, receiver) = flume::bounded(2); + + Self { + conn: None, + accepted_session: Some(accepted_session), + buffer: None, + datagram_ch_sender: sender, + datagram_ch_receiver: receiver, + _marker: PhantomData, + } + } + pub fn accepted(&mut self, conn: Connection) { + self.conn = Some(conn); + } + pub async fn accept(&mut self) -> Result { + let accepted_session = self.accepted_session.take().unwrap(); + accepted_session.accept().await + } + pub fn path(&self) -> &str { + self.accepted_session.as_ref().unwrap().path() + } + pub fn authority(&self) -> &str { + self.accepted_session.as_ref().unwrap().authority() + } + pub fn headers(&self) -> &HashMap { + self.accepted_session.as_ref().unwrap().headers() + } +} + +impl Conn { + pub(crate) fn new(conn: Connection) -> Self { + let (sender, receiver) = flume::bounded(2); + + Self { + conn: Some(conn), + accepted_session: None, + buffer: None, + datagram_ch_sender: sender, + datagram_ch_receiver: receiver, + _marker: PhantomData, + } + } +} diff --git a/src/executor.rs b/src/executor.rs index 0ad3f64..86e0922 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -5,18 +5,18 @@ use smol::{block_on, future, Executor, Task}; use std::{future::Future, thread}; pub fn spawn(future: impl Future + Send + 'static) -> Task { - static GLOBAL: Lazy> = Lazy::new(|| { - for n in 0..= num_cpus::get() { - thread::Builder::new() - .name(format!("ftlt-{}", n)) - .spawn(|| loop { - block_on(GLOBAL.run(future::pending::<()>())) - }) - .expect("cannot spawn executor thread"); - } + static GLOBAL: Lazy> = Lazy::new(|| { + for n in 0..=num_cpus::get() { + thread::Builder::new() + .name(format!("ftlt-{}", n)) + .spawn(|| loop { + block_on(GLOBAL.run(future::pending::<()>())) + }) + .expect("cannot spawn executor thread"); + } - Executor::new() - }); + Executor::new() + }); - GLOBAL.spawn(future) + GLOBAL.spawn(future) } diff --git a/src/lib.rs b/src/lib.rs index d004ec4..cac1b9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,11 @@ -use connection::Conn; +use connection::{Client, Conn, Server}; use once_cell::sync::Lazy; use tokio::runtime::Runtime; ///------------------------------------ static mut SEND_FN: Option = None; -static mut SERVER_CONN_FN: Option = None; -static mut CLIENT_CONN_FN: Option = None; +static mut SERVER_CONN_FN: Option)> = None; +static mut CLIENT_CONN_FN: Option)> = None; static RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); ///------------------------------------ mod certificate; diff --git a/src/server.rs b/src/server.rs index e303c76..b445f5f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -2,11 +2,14 @@ use std::{path::Path, time::Duration}; use tokio::runtime::Runtime; use wtransport::{endpoint, tls::Certificate, Endpoint, ServerConfig}; -use crate::{connection::Conn, RUNTIME, SEND_FN, SERVER_CONN_FN}; +use crate::{ + connection::{self, Conn, Server}, + RUNTIME, SEND_FN, SERVER_CONN_FN, +}; pub struct WebTransportServer { pub server: Option>, - pub conn_cb: Option, + pub conn_cb: Option)>, pub state: Option, } @@ -39,12 +42,15 @@ impl WebTransportServer { let session_request = incoming_session.await; - let accepted_session = match session_request { - Ok(session_request) => session_request, + let mut accepted_session = match session_request { + Ok(session_request) => { + let client = Conn::::new(session_request); + client + } Err(e) => { //TODO(hironichu): Handle error with callback SENDER_FN println!("Error accepting session: {:?}", e); - return; + continue; } }; // accepted_session. @@ -55,16 +61,18 @@ impl WebTransportServer { // accepted_session.authority(), // accepted_session.path() // ); + match accepted_session.accept().await { Ok(conn) => { // println!("DBG: Sending connection to channel."); - let client = Conn::new(conn); - let client_ptr = Box::into_raw(Box::new(client)); + accepted_session.accepted(conn); + let client_ptr = Box::into_raw(Box::new(accepted_session)); assert!(!SERVER_CONN_FN.is_none()); //TODO(hironichu): Handle this better. SERVER_CONN_FN.unwrap()(client_ptr); } _ => { println!("Error accepting connection"); + continue; } } } @@ -128,7 +136,7 @@ pub unsafe extern "C" fn proc_server_init( #[no_mangle] pub unsafe extern "C" fn proc_server_listen( server_ptr: *mut WebTransportServer, - cb: Option, + cb: Option)>, ) { assert!(!server_ptr.is_null()); let server = &mut *server_ptr; diff --git a/src/shared.rs b/src/shared.rs index 014646a..a3d438a 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,11 +1,14 @@ -use crate::{connection::Conn, SEND_FN}; +use crate::{ + connection::{Conn, Server}, + SEND_FN, +}; use core::panic; use std::slice::from_raw_parts_mut; use tokio::runtime::Runtime; use wtransport::error::SendDatagramError; #[no_mangle] -pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn) -> usize { +pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn) -> usize { let client = &mut *conn_ptr; match client.datagram_ch_receiver.recv() { Ok(dgram) => { @@ -18,12 +21,16 @@ pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn) -> usize { } #[no_mangle] -pub unsafe extern "C" fn proc_send_datagram(connptr: *mut Conn, buf: *const u8, buflen: u32) { +pub unsafe extern "C" fn proc_send_datagram( + connptr: *mut Conn, + buf: *const u8, + buflen: u32, +) { assert!(!connptr.is_null()); let client = &mut *connptr; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); - match client.conn.send_datagram(buf) { + match client.conn.as_ref().unwrap().send_datagram(buf) { Ok(_) => {} Err(err) => { //TODO: Handle error better @@ -45,7 +52,11 @@ pub unsafe extern "C" fn proc_send_datagram(connptr: *mut Conn, buf: *const u8, } } #[no_mangle] -pub unsafe extern "C" fn proc_init_datagrams(conn_ptr: *mut Conn, buffer: *mut u8, buflen: usize) { +pub unsafe extern "C" fn proc_init_datagrams( + conn_ptr: *mut Conn, + buffer: *mut u8, + buflen: usize, +) { assert!(!conn_ptr.is_null()); let connection = &mut *conn_ptr; @@ -54,7 +65,7 @@ pub unsafe extern "C" fn proc_init_datagrams(conn_ptr: *mut Conn, buffer: *mut u } #[no_mangle] -pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn, _buf: *mut u8) { +pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn, _buf: *mut u8) { assert!(!connptr.is_null()); let _client = &mut *connptr; @@ -75,7 +86,7 @@ pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn, _buf: *mut u8) { // } } #[no_mangle] -pub unsafe extern "C" fn free_conn(_: *mut Conn) {} +pub unsafe extern "C" fn free_conn(_: *mut Conn) {} #[no_mangle] pub unsafe extern "C" fn free_runtime(_: *mut Runtime) {} diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..e69de29 From 28276200646227fd9d09f7d6ce0b3faacca0d105 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 00:53:28 +0200 Subject: [PATCH 43/72] Rm useless file --- test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test.ts diff --git a/test.ts b/test.ts deleted file mode 100644 index e69de29..0000000 From 2a51b7f058023718d61c531c650fbacf8b1050e7 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:03:39 +0200 Subject: [PATCH 44/72] fix ci : Lib loading in CI --- deno.jsonc | 2 +- mod/lib.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index 262b145..7facb13 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -6,7 +6,7 @@ "demo:web": "deno run -A ./examples/web_server/web.js", "demo:server": "deno run -A --unstable ./examples/deno/wt_server.ts", "demo:client": "deno run -A --unstable ./examples/deno/wt_client.ts", - "demo:gencert": "deno run -A --unstable ./examples/deno/wt_gencert.ts", + "demo:gencert": "deno run -A --unstable ./examples/deno/wt_gencert.ts", // CI Build task "build:release": "cargo clean && cargo build --release", "build:debug": "cargo clean && cargo build", diff --git a/mod/lib.ts b/mod/lib.ts index e299762..a5aa317 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -56,8 +56,11 @@ switch (Deno.build.os) { } } const buildFilename = local - ? `${fileprefix}webtransport${fileExt}` + ? `${fileprefix}webtransport${ + Deno.env.has("DEVELOPMENT") ? "" : `_${Deno.build.arch}_` + }${fileExt}` : `${fileprefix}webtransport_${Deno.build.arch}_${fileExt}`; +console.log(buildFilename); const DURL = new URL(dirpath + buildFilename, currentPath); if (!local) { const remoteLIb = await fetch(download_lib, { From 2c7aa8f511092f2baa1f6da5b52fd97eb037ad15 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:07:56 +0200 Subject: [PATCH 45/72] Removed env from CI --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d470df1..eab7638 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -189,9 +189,6 @@ jobs: - name: Run deno test (debug) if: | matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') - env: - DEVELOPMENT: true - BUILD_TARGET: debug run: | deno task test @@ -199,8 +196,5 @@ jobs: if: | (matrix.job == 'test' && matrix.profile == 'release') && ((github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) - env: - BUILD_TARGET: release - DEVELOPMENT: true run: | deno task test \ No newline at end of file From 9782b349cb33e7d5670cfba9d7a0f920332eacc8 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:16:45 +0200 Subject: [PATCH 46/72] Trying to fix custom lib loading in CI --- .github/workflows/ci.yml | 6 ++++++ mod/lib.ts | 2 +- utils/download_lib.ts | 28 ++++++++++++++++------------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eab7638..28de915 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -189,6 +189,9 @@ jobs: - name: Run deno test (debug) if: | matrix.job == 'test' && matrix.profile == 'debug' && !startsWith(github.ref, 'refs/tags/') + env: + BUILD_TARGET: debug + CI_BUILD: true run: | deno task test @@ -196,5 +199,8 @@ jobs: if: | (matrix.job == 'test' && matrix.profile == 'release') && ((github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/'))) + env: + BUILD_TARGET: release + CI_BUILD: true run: | deno task test \ No newline at end of file diff --git a/mod/lib.ts b/mod/lib.ts index a5aa317..85db5e0 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -57,7 +57,7 @@ switch (Deno.build.os) { } const buildFilename = local ? `${fileprefix}webtransport${ - Deno.env.has("DEVELOPMENT") ? "" : `_${Deno.build.arch}_` + !Deno.env.has("CI_BUILD") ? "" : `_${Deno.build.arch}_` }${fileExt}` : `${fileprefix}webtransport_${Deno.build.arch}_${fileExt}`; console.log(buildFilename); diff --git a/utils/download_lib.ts b/utils/download_lib.ts index 01a3430..260e504 100644 --- a/utils/download_lib.ts +++ b/utils/download_lib.ts @@ -2,21 +2,23 @@ import "https://deno.land/std@0.201.0/dotenv/load.ts"; const LIB_NAME = "webtransport"; let LIB_URL: URL | undefined; -if (!Deno.env.get("DEVELOPMENT")) { - if (!Deno.env.get("DENO_AUTH_TOKENS")) { - throw new Error("Please set DENO_AUTH_TOKENS to your auth tokens"); +if (!Deno.env.has("DEVELOPMENT")) { + const headers = new Headers(); + if (Deno.env.has("DENO_AUTH_TOKENS")) { + headers.set( + "Authorization", + `token ${ + Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0].split( + ":", + )[1] + }`, + ); } + headers.set("Accept", "application/vnd.github.v3+json"); const data = await fetch( `https://api.github.com/repos/hironichu/${LIB_NAME}/releases`, { - headers: { - Authorization: `token ${ - Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0].split( - ":", - )[1] - }`, - Accept: "application/vnd.github.v3+json", - }, + headers: headers, }, ); @@ -36,7 +38,9 @@ if (!Deno.env.get("DEVELOPMENT")) { `${LIB_NAME}_${Deno.build.os}_${Deno.build.arch}` ][0].url, ); - LIB_URL!.username = Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]!; + LIB_URL!.username = Deno.env.has("DENO_AUTH_TOKENS") + ? Deno.env.get("DENO_AUTH_TOKENS")!.split("@")[0]! + : ""; } const build_target = Deno.env.get("DEVELOPMENT") ? "debug" : "release"; LIB_URL = LIB_URL ?? new URL(`../target/${build_target}/`, import.meta.url); From 80b3d6b327568ccf74713d5fd727d314720ac266 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:23:16 +0200 Subject: [PATCH 47/72] fix --- utils/download_lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/download_lib.ts b/utils/download_lib.ts index 260e504..520d075 100644 --- a/utils/download_lib.ts +++ b/utils/download_lib.ts @@ -2,7 +2,7 @@ import "https://deno.land/std@0.201.0/dotenv/load.ts"; const LIB_NAME = "webtransport"; let LIB_URL: URL | undefined; -if (!Deno.env.has("DEVELOPMENT")) { +if (!Deno.env.has("DEVELOPMENT") && !Deno.env.has("CI_BUILD")) { const headers = new Headers(); if (Deno.env.has("DENO_AUTH_TOKENS")) { headers.set( From 4ae2e7d6cac8fcc7a80817c31b22fa2225ba2619 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:26:11 +0200 Subject: [PATCH 48/72] fix aarch64 --- mod/lib.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mod/lib.ts b/mod/lib.ts index 85db5e0..36eab80 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -57,7 +57,9 @@ switch (Deno.build.os) { } const buildFilename = local ? `${fileprefix}webtransport${ - !Deno.env.has("CI_BUILD") ? "" : `_${Deno.build.arch}_` + !Deno.env.has("CI_BUILD") && Deno.build.arch == "aarch64" + ? "" + : `_${Deno.build.arch}_` }${fileExt}` : `${fileprefix}webtransport_${Deno.build.arch}_${fileExt}`; console.log(buildFilename); From 2016ec697a97034f9fbf2ef54ddbbed67f57fd57 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:32:14 +0200 Subject: [PATCH 49/72] fix aarch64 2 --- mod/lib.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mod/lib.ts b/mod/lib.ts index 36eab80..81ca12c 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -55,14 +55,12 @@ switch (Deno.build.os) { break; } } -const buildFilename = local - ? `${fileprefix}webtransport${ - !Deno.env.has("CI_BUILD") && Deno.build.arch == "aarch64" - ? "" - : `_${Deno.build.arch}_` - }${fileExt}` - : `${fileprefix}webtransport_${Deno.build.arch}_${fileExt}`; -console.log(buildFilename); +const buildFilename = `${fileprefix}webtransport${ + !Deno.env.has("CI_BUILD") + ? "" + : `${Deno.build.arch == "aarch64" ? `_${Deno.build.arch}_` : ""}` +}${fileExt}`; + const DURL = new URL(dirpath + buildFilename, currentPath); if (!local) { const remoteLIb = await fetch(download_lib, { From 8a37adb3f9aa5d4dc241f779ce2e7a9c94a32e60 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:35:11 +0200 Subject: [PATCH 50/72] Removed _ --- mod/lib.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/lib.ts b/mod/lib.ts index 81ca12c..067c24f 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -58,7 +58,7 @@ switch (Deno.build.os) { const buildFilename = `${fileprefix}webtransport${ !Deno.env.has("CI_BUILD") ? "" - : `${Deno.build.arch == "aarch64" ? `_${Deno.build.arch}_` : ""}` + : `${Deno.build.arch == "aarch64" ? `_${Deno.build.arch}` : ""}` }${fileExt}`; const DURL = new URL(dirpath + buildFilename, currentPath); From 49406935613870bea4025f446f689f6821a969a2 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Thu, 21 Sep 2023 01:46:38 +0200 Subject: [PATCH 51/72] Re-added Macos and windows CI, if it works it works.. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28de915..064381b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: github.event_name == 'push' || !startsWith(github.event.pull_request.head.label, 'hironichu:') strategy: matrix: - os: [ 'ubuntu-latest', 'self-hosted'] + os: [ 'ubuntu-latest', 'self-hosted', "macos-latest", "windows-latest"] job: [build] profile: [debug, release] include: @@ -150,7 +150,7 @@ jobs: github.event_name == 'push' || !startsWith(github.event.pull_request.head.label, 'hironichu:') strategy: matrix: - os: [ 'ubuntu-latest', 'self-hosted'] + os: ['ubuntu-latest', 'self-hosted', "macos-latest", "windows-latest"] job: [test] profile: [debug, release] name: ${{ matrix.job }} ${{ matrix.profile }} ${{ matrix.os }} From 47f724a9c8d14de2aa86e354fc1e161bb7f4650b Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Fri, 22 Sep 2023 00:10:17 +0200 Subject: [PATCH 52/72] Getting WebTransport closer to 'standards' --- Cargo.toml | 3 - examples/deno/wt_client.ts | 9 ++- examples/deno/wt_server.ts | 14 ++-- examples/deno/wt_server_test.ts | 117 +++++++++++++++++++------------- examples/web_server/index.html | 13 +++- mod/client.ts | 106 +++++++++++++---------------- mod/connection.ts | 32 +++++---- mod/crypto.ts | 7 +- mod/deps.ts | 2 +- mod/interface.ts | 12 ++-- mod/mod.ts | 30 ++++---- mod/server.ts | 55 ++++++++++----- src/client.rs | 10 +-- src/connection.rs | 54 ++++----------- src/executor.rs | 22 ------ src/lib.rs | 1 - src/server.rs | 89 ++++++++++++------------ src/shared.rs | 55 +++++++++------ 18 files changed, 321 insertions(+), 310 deletions(-) delete mode 100644 src/executor.rs diff --git a/Cargo.toml b/Cargo.toml index ea236c5..3f44ae3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,10 +34,7 @@ incremental = true codegen-units = 1 [dependencies] -flume = "=0.11.0" -num_cpus = "=1.16.0" once_cell = "=1.18.0" -smol = "=1.3.0" tokio = { version = "=1.32.0", default-features = false, features = [ "rt", "rt-multi-thread", diff --git a/examples/deno/wt_client.ts b/examples/deno/wt_client.ts index 68c0cfc..8d3033a 100644 --- a/examples/deno/wt_client.ts +++ b/examples/deno/wt_client.ts @@ -9,7 +9,9 @@ const client = new WebTransport(connectAddr, { keepAlive: 3, }); -await client.ready; +// console.log(client); + +await client.ready(); console.log("Client connected"); const writer = client.datagrams.writable.getWriter(); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); @@ -17,3 +19,8 @@ writer?.write(new Uint8Array([1, 2, 3, 4, 5])); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); + +// //await messages +for await (const read of client.datagrams.readable) { + console.log(read); +} diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index af5dd4b..0233a98 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -18,21 +18,15 @@ try { Deno.exit(1); } -const server = new WebTransportServer(4433, { +const server = new WebTransportServer("https://localhost:4433", { certFile: "./certs/localhost.crt", keyFile: "./certs/localhost.key", maxTimeout: 10, keepAlive: 3, }); -console.log("Server created"); -server.on("listening", (e) => { - console.log("Listening OUT", e); -}); +server.listen(); -server.on("connection", async (connection) => { - console.log("Connection "); - for await (const read of connection.datagrams.readable) { - console.log(read); - } +server.on("connection", (_) => { + console.log(server.connections.size); }); diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 2d88cbc..658e86c 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1,51 +1,76 @@ -// import { join } from "https://deno.land/std@0.201.0/path/mod.ts"; -// import { GenerateCertKeyFile } from "../../mod/crypto.ts"; -// import "../../mod/mod.ts"; -// import { assert } from "https://deno.land/std@0.201.0/assert/assert.ts"; +import { GenerateCertKeyFile } from "../../mod/crypto.ts"; +import "../../mod/mod.ts"; +import { assert } from "https://deno.land/std@0.202.0/assert/mod.ts"; +import { WebTransportServer } from "../../mod/server.ts"; // //add certs cleanup methods after tests // const certPath = join(Deno.cwd(), "./certs/"); -// Deno.test({ name: "Server startup/close" }, () => { -// //generate a certificate -// const [cert, key] = GenerateCertKeyFile( -// "localhost", -// 0, -// 10, -// undefined, -// "cert.pem", -// "key.pem", -// ); -// const server = new WebTransportServer(4433, { -// certFile: cert, -// keyFile: key, -// maxTimeout: 10, -// keepAlive: 3, -// }); -// server.close(); -// //try to start a UDP socket on the same port to see if it's closed -// const sock = Deno.listenDatagram({ -// hostname: "0.0.0.0", -// port: 4433, -// transport: "udp", -// }); -// assert(sock, "Server did not close"); -// sock.close(); -// }); +async function _sleep(msec: number) { + await new Promise((res, _rej) => setTimeout(res, msec)); +} -// // Deno.test( -// // { name: "Server with generated certificate startup/close" }, -// // () => { -// // const server = new WebTransportServer(4433, { -// // maxTimeout: 10, -// // keepAlive: 3, -// // notAfter: 10, -// // notBefore: 0, -// // domain: "localhost", -// // }); -// // server.close(); -// // }, -// // ); +Deno.test({ name: "Server startup/close" }, () => { + //generate a certificate + const [cert, key] = GenerateCertKeyFile( + "localhost", + 0, + 10, + ); + const server = new WebTransportServer("https://localhost:4433", { + certFile: cert, + keyFile: key, + maxTimeout: 10, + keepAlive: 3, + }); + server.close(); + //try to start a UDP socket on the same port to see if it's closed + const sock = Deno.listenDatagram({ + hostname: "0.0.0.0", + port: 4433, + transport: "udp", + }); + assert(sock, "Server did not close"); + sock.close(); +}); -// Deno.test({ name: "Server Cleanup certs" }, () => { -// Deno.removeSync(certPath, { recursive: true }); -// }); +// Deno.test( +// { name: "Server with generated certificate startup/close" }, +// async (test) => { +// const _srv = GetTestServer(); +// const client = new WebTransport("https://localhost:4433"); + +// await client.ready; + +// await test.step("Client connection", () => { +// assert(client.ready, "Client not ready"); +// }); +// const testCases = [1]; +// Promise.all(testCases.map(async (testCase) => { +// await test.step({ +// name: "Client message Receiving: " + testCase, +// fn: async () => { +// console.info("steup listener"); +// for await (const data of client.datagrams.readable) { +// console.log(data); +// } +// }, +// sanitizeOps: false, +// sanitizeResources: false, +// sanitizeExit: false, +// }); + +// await test.step({ +// name: "Client message Sending: " + testCase, +// fn: async () => { +// console.log("SENDING SHIT"); +// const writer = client.datagrams.writable.getWriter(); +// await writer.write(new Uint8Array([1, 2, 3, 4, 5])); +// }, +// sanitizeOps: false, +// sanitizeResources: false, +// sanitizeExit: false, +// }); +// })).then(client.close); +// _srv.close(); +// }, +// ); diff --git a/examples/web_server/index.html b/examples/web_server/index.html index 090fad5..90b46d2 100644 --- a/examples/web_server/index.html +++ b/examples/web_server/index.html @@ -28,17 +28,25 @@

Server IP : HOST_IP

diff --git a/mod/client.ts b/mod/client.ts index 0d41ba1..4d697dd 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -77,17 +77,22 @@ export class WebTransport { * @description This function is called when a new connection is received from the server */ private connection(client: Deno.PointerValue) { + if (!client || client == null) { + return; + } + Promise.resolve(this.ready); const CONN_BUFFER = new Uint8Array(65536); - const conn = new WebTransportConnection( + this.conn = new WebTransportConnection( client, CONN_BUFFER, ); this.datagrams = new WebTransportDatagramDuplexStream( - conn, + this.conn, CONN_BUFFER, ); + return this.conn; } /** * @callback notify @@ -98,41 +103,48 @@ export class WebTransport { * * @description This function is called when a new event is received from the server */ - private notify( + private async notify( _code: unknown | number, buffer: Deno.PointerValue, buflen: number, ) { - const code = _code as bigint; - console.log(code); + const code = _code as number; + if (buflen < 0) { return; } + const pointer = Deno.UnsafePointerView.getArrayBuffer( buffer as unknown as NonNullable, buflen, ); - const _event = new MessageEvent("message", { - data: pointer, + const data = new TextDecoder().decode(pointer); + if (code >= 400) { + this.conn!.close(); + await Promise.reject(this.ready); + throw new Error(data); + } + const _event = new MessageEvent("error", { + data, }); + dispatchEvent(_event); //TODO(hironichu): Implement Error/event catching from rust to free the memory once a connection drop or if something else happens. } ready = () => - new Promise((resolve, reject) => { - try { - const encoded = encodeBuf(this.remote.href); - window.WTLIB.symbols.proc_client_connect( - this.#CONN_PTR!, - this.#CONNECTION_CB.pointer, - encoded[0], - encoded[1], - ); - - resolve(this); - } catch (e) { - reject(e); + new Promise(() => { + const encoded = encodeBuf(this.remote.href); + window.WTLIB.symbols.proc_client_connect( + this.#CONN_PTR!, + this.#CONNECTION_CB.pointer, + encoded[0], + encoded[1], + ); + if (this.conn) { + return this.conn; + } else { + throw new Error("Failed to connect to server"); } }); diff --git a/mod/connection.ts b/mod/connection.ts index cd9c24e..4484fb9 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -1,12 +1,19 @@ // import { FFI_CODES } from "./code.ts"; - +/// +interface WebTransportSendStreamOptions { + sendOrder?: number | null; +} +interface WebTransportCloseInfo { + errorCode?: number; + reason?: string; +} export class WebTransportDatagramDuplexStream { #READ_BUFFER: Uint8Array; readonly incomingHighWaterMark = 1; - readonly incomingMaxAge = null; + readonly incomingMaxAge = 0; readonly maxDatagramSize = 1024; readonly outgoingHighWaterMark = 1; - readonly outgoingMaxAge = null; + readonly outgoingMaxAge = 0; constructor( private connection: WebTransportConnection, _buffer: Uint8Array, @@ -64,6 +71,125 @@ export class WebTransportDatagramDuplexStream { } } +export class WebTransportBidirectionalStream { + readonly readable: ReadableStream; + readonly writable: WritableStream; + + constructor( + public ptr: Deno.PointerValue, + ) { + this.readable = WebTransportReceiveStream.from(this.ptr); + this.writable = WebTransportSendStream.from(this.ptr); + } +} +export class WebTransportReceiveStream { + private static readonly ptr: Deno.PointerValue; + constructor(private readonly ptr: Deno.PointerValue) { + } + static from( + ptr: Deno.PointerValue, + ) { + return new ReadableStream({ + type: "bytes", + start( + _, + ) { + }, + pull(controller) { + readRepeatedly().catch((e) => controller.error(e)); + async function readRepeatedly() { + if (!ptr || ptr === null) { + throw new Error("Stream is closed"); + } + let bytesRead; + if (controller.byobRequest) { + const v = controller.byobRequest.view; + bytesRead = await window.WTLIB.symbols.proc_read( + ptr, + v?.buffer!, + v?.byteLength!, + ); + if (bytesRead === 0) { + console.log("BYOB REQUEST"); + controller.close(); + } + controller.byobRequest.respond(bytesRead as number); + } else { + // return; + } + if (bytesRead === 0) { + return; + } + return readRepeatedly(); + } + }, + async cancel(reason?: string): Promise { + if (!ptr || ptr === null) { + return; + } + console.log("CANCEL ", reason); + await window.WTLIB.symbols.proc_recvtream_stop(ptr).catch( + (e) => { + console.error(e); + }, + ); + }, + }); + } +} + +export class WebTransportSendStream { + constructor(private ptr: Deno.PointerValue) { + } + static from( + ptr: Deno.PointerValue, + ) { + return new WritableStream({ + async write( + chunk: Uint8Array, + controller: WritableStreamDefaultController, + ) { + let written = 0; + if (!ptr || ptr === null) { + controller.error("Stream is closed"); + return; + } + try { + console.log("Writing chunk: ", chunk); + written = await window.WTLIB.symbols.proc_write( + ptr, + chunk, + chunk.byteLength, + ) as number; + if (written === 0) { + controller.error("Write failed"); + return; + } + } catch (e) { + console.error(e); + return; + } + }, + async abort() { + if (!ptr || ptr === null) { + return; + } + await window.WTLIB.symbols.proc_sendstream_finish( + ptr, + ); + }, + async close() { + if (!ptr || ptr === null) { + return; + } + await window.WTLIB.symbols.proc_sendstream_finish( + ptr, + ); + }, + }); + } +} + export default class WebTransportConnection { state: | "connected" @@ -72,33 +198,120 @@ export default class WebTransportConnection { | "failed" | "connecting" = "closed" as const; - readonly #CONN_PTR: Deno.PointerValue; + // readonly #CONN_PTR!: Deno.PointerValue; public readonly datagrams: WebTransportDatagramDuplexStream; + public readonly incomingBidirectionalStreams: ReadableStream< + WebTransportBidirectionalStream + >; + public readonly incomingUnidirectionalStreams: ReadableStream< + ReadableStream + >; + constructor( - pointer: Deno.PointerValue, + public readonly pointer: Deno.PointerValue, buffer: Uint8Array, ) { this.state = "connected"; - this.#CONN_PTR = pointer; + // this.#CONN_PTR = pointer; this.datagrams = new WebTransportDatagramDuplexStream(this, buffer); + + this.incomingBidirectionalStreams = new ReadableStream< + WebTransportBidirectionalStream + >({ + // start(_) { + // }, + async start(controller) { + try { + const stream = await window.WTLIB.symbols + .proc_accept_bi( + pointer, + ); + if (!stream) { + controller.close(); + return; + } + const jsStream = new WebTransportBidirectionalStream( + stream, + ); + controller.enqueue(jsStream); + } catch (e) { + controller.error(e); + } + }, + cancel() { + }, + }); + this.incomingUnidirectionalStreams = new ReadableStream< + ReadableStream + >({ + async start(controller) { + try { + const stream = await window.WTLIB.symbols + .proc_accept_uni( + pointer, + ); + if (!stream) { + controller.close(); + return; + } + controller.enqueue(WebTransportReceiveStream.from( + stream, + )); + } catch (e) { + controller.error(e); + } + }, + cancel() { + }, + }); } - //TODO(hironichu): implement the rest of the methods - public readonly incomingBidirectionalStreams?: ReadableStream< - ReadableStream - > = undefined; - public readonly incomingUnidirectionalStreams?: ReadableStream< - ReadableStream - > = undefined; + public async createBidirectionalStream( + _options?: WebTransportSendStreamOptions, + ): Promise { + //The following operation block the thread until the stream is created. + if (!this.pointer) { + throw new Error("Connection is closed"); + } + const _streams = await window.WTLIB.symbols.proc_open_bi( + this.pointer, + ); + if (!_streams) { + throw new Error("Failed to create stream"); + } + return new WebTransportBidirectionalStream(_streams); + } + + public async createUnidirectionalStream( + _options?: WebTransportSendStreamOptions, + ): Promise { + if (!this.pointer) { + throw new Error("Connection is closed"); + } + //The following operation block the thread until the stream is created. + const _streams = await window.WTLIB.symbols.proc_open_uni( + this.pointer, + ); - async close() { + if (!_streams) { + throw new Error("Failed to create stream"); + } + const stream = WebTransportSendStream.from(_streams); + + return stream; + } + + async close(_closeInfo?: WebTransportCloseInfo) { this.state = "closed"; + if (!this.pointer || this.pointer === null) { + throw new Error("Connection is closed"); + } + if (!this.pointer || this.pointer === null) { + throw new Error("Connection is closed"); + } await window.WTLIB.symbols.proc_client_close( - this.#CONN_PTR, + this.pointer, this.pointer, ); } - get pointer() { - return this.#CONN_PTR; - } } diff --git a/mod/interface.ts b/mod/interface.ts index 3cc575f..435fbd6 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -84,11 +84,69 @@ export const symbols = { result: "void", nonblocking: false, }, - // proc_init_datagrams: { - // parameters: ["pointer", "buffer", "usize"], - // result: "void", - // nonblocking: true, - // }, + proc_accept_bi: { + parameters: ["pointer"], //Option<> + result: "pointer", + nonblocking: true, + }, + proc_open_bi: { + parameters: ["pointer"], //Option<> + result: "pointer", + nonblocking: true, + }, + + proc_accept_uni: { + parameters: ["pointer"], + result: "pointer", + nonblocking: true, + }, + proc_open_uni: { + parameters: ["pointer"], + result: "pointer", + nonblocking: true, + }, + + proc_read: { + parameters: ["pointer", "buffer", "u32"], + result: "usize", + nonblocking: true, + }, + proc_write: { + parameters: ["pointer", "buffer", "u32"], + result: "usize", + nonblocking: true, + }, + proc_write_all: { + parameters: ["pointer", "buffer", "u32"], + result: "usize", + nonblocking: true, + }, + proc_recvstream_id: { + parameters: ["pointer"], + result: "u64", + }, + proc_sendstream_id: { + parameters: ["pointer"], + result: "u64", + }, + proc_sendstream_priority: { + parameters: ["pointer"], + result: "u64", + }, + proc_sendstream_set_priority: { + parameters: ["pointer", "u64"], + result: "u64", + }, + proc_sendstream_finish: { + parameters: ["pointer"], + result: "void", + nonblocking: true, + }, + proc_recvtream_stop: { + parameters: ["pointer"], + result: "void", + nonblocking: true, + }, // Crypto symbols proc_gencert: { parameters: [ diff --git a/mod/server.ts b/mod/server.ts index 66fb5f2..91ba84a 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -124,6 +124,7 @@ export class WebTransportServer extends EventEmitter { buffer: Deno.PointerValue, buflen: number, ) { + console.info("CB NOTIFY CALLEd"); const code = _code as bigint; console.log(code); if (buflen < 0) { diff --git a/src/client.rs b/src/client.rs index 556addd..b680559 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,13 +1,13 @@ -use std::time::Duration; -use wtransport::{endpoint, ClientConfig, Endpoint}; - use crate::{ connection::{self, Client, Conn}, CLIENT_CONN_FN, RUNTIME, SEND_FN, }; +use std::time::Duration; +use wtransport::endpoint::endpoint_side::Client as endClient; +use wtransport::{ClientConfig, Endpoint}; pub struct WebTransportClient { - pub client: Option>, + pub client: Option>, pub conn_cb: Option)>, pub state: Option, } @@ -45,6 +45,8 @@ impl WebTransportClient { } Err(err) => { println!("DBG: Error connecting to server. Err: {}", err.to_string()); + let mut msg = err.to_string(); + SEND_FN.unwrap()(141, msg.as_mut_ptr(), msg.len() as u32); } } }); @@ -81,7 +83,8 @@ pub unsafe extern "C" fn proc_client_init( match client { Ok(client) => Box::into_raw(Box::new(client)), Err(_) => { - panic!("Error creating client") + SEND_FN.unwrap()(140, std::ptr::null_mut(), 0); + std::ptr::null_mut() } } } diff --git a/src/connection.rs b/src/connection.rs index d813dda..abce704 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,47 +1,13 @@ use crate::RUNTIME; use std::{collections::HashMap, future::Future, marker::PhantomData, pin::Pin}; -use wtransport::{datagram::Datagram, endpoint::SessionRequest, Connection}; +use wtransport::{ + datagram::Datagram, endpoint::SessionRequest, error::ConnectionError, Connection, RecvStream, + SendStream, +}; use wtransport_proto::varint::VarInt; type DynFutureIncomingSession = dyn Future> + Send + Sync; -impl Conn { - //TODO(hironichu): Add generic methods for openning and closing streams instead of doing it in both client and server. - pub async fn closed(&mut self) { - let conn = self.conn.as_ref().unwrap(); - conn.closed().await - } - - pub fn open_uni(&'static mut self) { - println!("To be implemented"); - // executor::spawn(async move { - // let conn = self.conn.as_ref().unwrap(); - // let stream = conn.open_uni().await.unwrap(); - // stream.await.unwrap(); - // }) - // .detach(); - } - pub fn open_bi(&'static mut self) { - println!("To be implemented"); - // executor::spawn(async move { - // let conn = self.conn.as_ref().unwrap(); - // let stream = conn.open_bi().await.unwrap(); - // stream.await.unwrap(); - // }) - // .detach(); - } - pub fn close(&mut self, code: u32, reason: Option<&[u8]>) { - let reason = match reason { - Some(reason) => reason, - None => b"closed", - }; - self.conn - .as_ref() - .unwrap() - .close(VarInt::from_u32(code), reason); - } -} - pub struct Server(Pin>); /// Type of endpoint opening a WebTransport connection. @@ -56,10 +22,8 @@ pub struct Conn { impl Conn { pub fn datagrams(&mut self) -> Result { - let stream = RUNTIME.block_on(async move { - let conn = self.conn.as_ref().unwrap(); - conn.receive_datagram().await - }); + let conn = self.conn.as_ref().unwrap(); + let stream = RUNTIME.block_on(async move { conn.receive_datagram().await }); match stream { Ok(dgram) => Ok(dgram), _ => { @@ -71,6 +35,77 @@ impl Conn { } } } + pub async fn closed(&mut self) { + let conn = self.conn.as_ref().unwrap(); + conn.closed().await + } + + /// Open a unidirectional stream. + pub fn open_uni(&'static mut self) -> Result { + let conn = self.conn.as_ref().unwrap(); + let stream = RUNTIME.block_on(async move { + let stream = conn.open_uni().await; + match stream { + Ok(stream) => Ok(stream.await.unwrap()), + Err(e) => Err(e), + } + }); + stream + } + + /// Open a bidirectional stream. + pub fn open_bi(&'static mut self) -> Result<(SendStream, RecvStream), ConnectionError> { + let conn = self.conn.as_ref().unwrap(); + let stream = RUNTIME.block_on(async move { + let stream = conn.open_bi().await; + match stream { + Ok(stream) => Ok(stream.await.unwrap()), + Err(e) => Err(e), + } + }); + + stream + } + + /// Accept a unidirectional stream. + pub fn accept_uni(&'static mut self) -> Result { + let conn = self.conn.as_ref().unwrap(); + let stream = RUNTIME.block_on(async move { + let stream = conn.accept_uni().await; + match stream { + Ok(stream) => Ok(stream), + Err(e) => Err(e), + } + }); + stream + } + + /// Accept a bidirectional stream. + pub fn accept_bi(&'static mut self) -> Result<(SendStream, RecvStream), ConnectionError> { + let conn = self.conn.as_ref().unwrap(); + let stream = RUNTIME.block_on(async move { + let stream = conn.accept_bi().await; + match stream { + Ok(stream) => Ok(stream), + Err(e) => Err(e), + } + }); + stream + } + + /// Close the connection. + pub fn close(&mut self, code: u32, reason: Option<&[u8]>) { + let reason = match reason { + Some(reason) => reason, + None => b"closed", + }; + self.conn + .as_ref() + .unwrap() + .close(VarInt::from_u32(code), reason); + + drop(self.conn.take()) + } } impl Conn { diff --git a/src/server.rs b/src/server.rs index e3b1eb3..b86f00f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,14 +1,14 @@ -use std::{path::Path, time::Duration}; -use tokio::runtime::Runtime; -use wtransport::{endpoint, tls::Certificate, Endpoint, ServerConfig}; - use crate::{ connection::{self, Conn, Server}, RUNTIME, SEND_FN, SERVER_CONN_FN, }; +use std::{path::Path, time::Duration}; +use tokio::runtime::Runtime; +use wtransport::endpoint::endpoint_side::Server as endServer; +use wtransport::{tls::Certificate, Endpoint, ServerConfig}; pub struct WebTransportServer { - pub server: Option>, + pub server: Option>, pub state: Option, } @@ -56,7 +56,7 @@ impl WebTransportServer { Ok(client_ptr) } Err(e) => { - println!("Error accepting connection : {}", e); + println!("Error accepting connection : {}", e.to_string()); //TODO(hironichu): Map this code to an error in Typescript Err(0) } @@ -139,7 +139,7 @@ pub unsafe extern "C" fn proc_server_listen( SERVER_CONN_FN.unwrap()(conn); } Err(e) => { - println!("Error accepting connection : {}", e); + println!("Error accepting sess in : {}", e); } } } diff --git a/src/shared.rs b/src/shared.rs index 59e43e1..ba287f3 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,25 +1,19 @@ use crate::{ connection::{Conn, Server}, - SEND_FN, + RUNTIME, SEND_FN, }; -use core::panic; + use std::slice::from_raw_parts_mut; use tokio::runtime::Runtime; -use wtransport::error::SendDatagramError; - -// #[no_mangle] -// pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn) -> usize { -// let client = &mut *conn_ptr; -// match client.datagram_ch_receiver.recv() { -// Ok(dgram) => { -// from_raw_parts_mut(client.buffer.as_mut().unwrap().as_mut_ptr(), dgram.len()) -// .clone_from_slice(&dgram); -// dgram.len() -// } -// Err(_) => 0, -// } -// } +use wtransport::{error::SendDatagramError, RecvStream, SendStream}; + +#[repr(C)] +pub struct BidiStreams { + pub send: Option, + pub recv: Option, +} +/// Send a datagram. #[no_mangle] pub unsafe extern "C" fn proc_send_datagram( connptr: *mut Conn, @@ -37,33 +31,22 @@ pub unsafe extern "C" fn proc_send_datagram( match err { SendDatagramError::NotConnected => { println!("DBG: Rust Connection closed"); - SEND_FN.unwrap()(0, vec![0].as_mut_ptr(), 1); + SEND_FN.unwrap()(161, vec![0].as_mut_ptr(), 1); } SendDatagramError::TooLarge => { println!("DBG: Rust Too large"); - SEND_FN.unwrap()(0, vec![0].as_mut_ptr(), 1); + SEND_FN.unwrap()(162, vec![0].as_mut_ptr(), 1); } SendDatagramError::UnsupportedByPeer => { println!("DBG: Rust not supported by peer"); - SEND_FN.unwrap()(0, vec![0].as_mut_ptr(), 1); + SEND_FN.unwrap()(163, vec![0].as_mut_ptr(), 1); } }; } } } -// #[no_mangle] -// pub unsafe extern "C" fn proc_init_datagrams( -// conn_ptr: *mut Conn, -// buffer: *mut u8, -// buflen: usize, -// ) { -// assert!(!conn_ptr.is_null()); - -// let connection = &mut *conn_ptr; -// connection.buffer = Some(from_raw_parts_mut(buffer, buflen)); -// // connection.datagrams(); -// } +/// Receive a datagram. #[no_mangle] pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: *mut u8) -> usize { assert!(!conn_ptr.is_null()); @@ -75,31 +58,248 @@ pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: * from_raw_parts_mut(buff, dgram.len()).clone_from_slice(&dgram); dgram.len() } - Err(_) => 0, + Err(err) => { + let mut msg = err.to_string(); + SEND_FN.unwrap()(156, msg.as_mut_ptr(), msg.len() as u32); + 0 + } } } +/// Open a bidirectional stream. (Temporary because unsafe) +#[allow(improper_ctypes_definitions)] #[no_mangle] -pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn, _buf: *mut u8) { +pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiStreams { assert!(!connptr.is_null()); let _client = &mut *connptr; - //return not implemented - panic!("Not implemented"); - // executor::spawn(async move { - // let stream = client.conn.open_bi().await.unwrap(); - // stream.await.unwrap(). - // }) - // .detach(); - // let buf = ::std::slice::from_raw_parts(buf, buflen as usize); - // match client.conn.send_datagram(buf) { - // Ok(_) => {} - // Err(e) => { - // //TODO: Handle error better - // println!("Error sending datagram: {:?}", e); - // } - // } + let stream = _client.open_bi(); + match stream { + Ok((send, recv)) => { + let bidi = BidiStreams { + send: Some(send), + recv: Some(recv), + }; + Box::into_raw(Box::new(bidi)) + } + Err(err) => { + println!("Error opening bidirectional stream: {:?}", err); + let mut msg = err.to_string(); + SEND_FN.unwrap()(155, msg.as_mut_ptr(), msg.len() as u32); + std::ptr::null_mut() + } + } +} + +/// Open a unidirectional stream. +#[no_mangle] +pub unsafe extern "C" fn proc_open_uni(connptr: *mut Conn) -> *mut BidiStreams { + assert!(!connptr.is_null()); + + let _client = &mut *connptr; + let stream = _client.open_uni(); + match stream { + Ok(stream) => Box::into_raw(Box::new(BidiStreams { + send: Some(stream), + recv: None, + })), + Err(err) => { + let mut msg = err.to_string(); + SEND_FN.unwrap()(152, msg.as_mut_ptr(), msg.len() as u32); + std::ptr::null_mut() + } + } +} + +/// Accept a unidirectional stream. +#[no_mangle] +pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut BidiStreams { + assert!(!connptr.is_null()); + + let _client = &mut *connptr; + let stream = _client.accept_uni(); + match stream { + Ok(stream) => Box::into_raw(Box::new(BidiStreams { + send: None, + recv: Some(stream), + })), + Err(err) => { + let mut msg = err.to_string(); + SEND_FN.unwrap()(151, msg.as_mut_ptr(), msg.len() as u32); + std::ptr::null_mut() + } + } +} + +/// Accept a bidirectional stream. +#[no_mangle] +pub unsafe extern "C" fn proc_accept_bi(connptr: *mut Conn) -> *mut BidiStreams { + assert!(!connptr.is_null()); + + let _client = &mut *connptr; + let stream = _client.accept_bi(); + match stream { + Ok((send, recv)) => { + let bidi = BidiStreams { + send: Some(send), + recv: Some(recv), + }; + Box::into_raw(Box::new(bidi)) + } + Err(err) => { + let mut msg = err.to_string(); + SEND_FN.unwrap()(152, msg.as_mut_ptr(), msg.len() as u32); + std::ptr::null_mut() + } + } +} + +///Write to a stream +#[no_mangle] +pub unsafe extern "C" fn proc_write( + stream_ptr: *mut BidiStreams, + buf: *const u8, + buflen: u32, +) -> usize { + assert!(!stream_ptr.is_null()); + assert!(buflen > 0); + + let bidi_streams = &mut *stream_ptr; + let buf = ::std::slice::from_raw_parts(buf, buflen as usize); + let writer = bidi_streams.send.as_mut().unwrap(); + let writenlen = RUNTIME.block_on(async move { + match writer.write(buf).await { + Ok(len) => len, + Err(err) => { + let mut str = err.to_string(); + SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); + 0 + } + } + }); + writenlen +} + +#[no_mangle] +pub unsafe extern "C" fn proc_write_all( + stream_ptr: *mut BidiStreams, + buf: *const u8, + buflen: u32, +) -> u32 { + assert!(!stream_ptr.is_null()); + assert!(buflen > 0); + let stream = &mut *stream_ptr; + let buf = ::std::slice::from_raw_parts(buf, buflen as usize); + let writer = stream.send.as_mut().unwrap(); + let writenlen = RUNTIME.block_on(async move { + match writer.write_all(buf).await { + Ok(_) => buflen, + Err(err) => { + let mut str = err.to_string(); + SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); + 0 + } + } + }); + writenlen +} + +/// Read from a stream +/// +/// Warning : We should always provide valid pointer from JS, if we dont we will crash for safety +/// Rust will automatically panic for any invalid pointer +#[no_mangle] +pub unsafe extern "C" fn proc_read( + stream_ptr: *mut BidiStreams, + buf: *mut u8, + buflen: u32, +) -> usize { + assert!(!stream_ptr.is_null()); + assert!(buflen > 0); + + let stream = &mut *stream_ptr; + let buf = ::std::slice::from_raw_parts_mut(buf, buflen as usize); + let readlen = RUNTIME.block_on(async move { + match stream.recv.as_mut().unwrap().read(buf).await { + Ok(len) => len, + Err(err) => { + let mut strs = err.to_string(); + SEND_FN.unwrap()(154, strs.as_mut_ptr(), strs.len() as u32); + None + } + } + }); + match readlen { + Some(len) => len, + None => 0, + } } + +/// Get a rescvstream id +#[no_mangle] +pub unsafe extern "C" fn proc_recvstream_id(stream_ptr: *mut BidiStreams) -> u64 { + assert!(!stream_ptr.is_null()); + let stream = &mut *stream_ptr; + stream.recv.as_mut().unwrap().id().into_u64() +} + +/// Get a sendstream id +#[no_mangle] +pub unsafe extern "C" fn proc_sendstream_id(stream_ptr: *mut BidiStreams) -> u64 { + assert!(!stream_ptr.is_null()); + let stream = &mut *stream_ptr; + stream.send.as_mut().unwrap().id().into_u64() +} + +/// Close a send stream. +#[no_mangle] +pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) { + assert!(!stream_ptr.is_null()); + let stream = &mut *stream_ptr; + let sendstream = stream.send.as_mut().unwrap(); + RUNTIME.block_on(async move { + match sendstream.finish().await { + Ok(_) => drop(stream_ptr.as_ref()), + Err(err) => { + let mut msg = err.to_string(); + SEND_FN.unwrap()(150, msg.as_mut_ptr(), msg.len() as u32); + } + } + }); +} + +#[no_mangle] +pub unsafe extern "C" fn proc_recvtream_stop(stream_ptr: *mut BidiStreams) { + assert!(!stream_ptr.is_null()); + let stream = &mut *stream_ptr; + RUNTIME.block_on(async move { + match stream.recv.as_mut().unwrap().stop(0).await { + Ok(_) => drop(stream_ptr.as_ref()), + Err(_) => { + SEND_FN.unwrap()(158, std::ptr::null_mut(), 0); + } + } + }); +} +#[no_mangle] +pub unsafe extern "C" fn proc_sendstream_priority(stream_ptr: *mut BidiStreams) -> i32 { + assert!(!stream_ptr.is_null()); + let stream = &mut *stream_ptr; + stream.send.as_ref().unwrap().priority() +} + +/// +#[no_mangle] +pub unsafe extern "C" fn proc_sendstream_set_priority( + stream_ptr: *mut BidiStreams, + priority: i32, +) -> i32 { + assert!(!stream_ptr.is_null()); + let stream = &mut *stream_ptr; + stream.send.as_ref().unwrap().set_priority(priority); + priority +} + #[no_mangle] pub unsafe extern "C" fn free_conn(_: *mut Conn) {} From 3656dc190fc4d77146dce3aa651c886fe867b310 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sat, 23 Sep 2023 16:02:41 +0200 Subject: [PATCH 54/72] Cleaned up code --- deno.jsonc | 12 +++---- examples/deno/wt_client_bidi.ts | 2 +- examples/deno/wt_client_uni_recv.ts | 2 +- examples/deno/wt_client_uni_send.ts | 2 +- mod/client.ts | 50 ++++++++++++++--------------- mod/connection.ts | 26 ++++++++++++--- mod/interface.ts | 6 +++- src/client.rs | 3 ++ src/shared.rs | 2 +- 9 files changed, 65 insertions(+), 40 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index b35e32b..9dac27d 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -5,13 +5,13 @@ //Examples "demo:web": "deno run -A ./examples/web_server/web.js", "demo:server": "deno run -A --unstable ./examples/deno/wt_server.ts", - "demo:server-uni-recv": "deno run -A --unstable ./examples/deno/wt_server_uni_recv.ts", - "demo:server-uni-send": "deno run -A --unstable ./examples/deno/wt_server_uni_send.ts", - "demo:server-bidi": "deno run -A --unstable ./examples/deno/wt_server_bidi.ts", + "demo:server-uni-recv": "deno run -A --unstable ./examples/deno/wt_server_uni_recv.ts", + "demo:server-uni-send": "deno run -A --unstable ./examples/deno/wt_server_uni_send.ts", + "demo:server-bidi": "deno run -A --unstable ./examples/deno/wt_server_bidi.ts", "demo:client": "deno run -A --unstable ./examples/deno/wt_client.ts", - "demo:bidi": "deno run -A --unstable ./examples/deno/wt_client_bidi.ts", - "demo:uni-send": "deno run -A --unstable ./examples/deno/wt_client_uni_send.ts", - "demo:uni-recv": "deno run -A --unstable ./examples/deno/wt_client_uni_recv.ts", + "demo:bidi": "deno run -A --unstable ./examples/deno/wt_client_bidi.ts", + "demo:uni-send": "deno run -A --unstable ./examples/deno/wt_client_uni_send.ts", + "demo:uni-recv": "deno run -A --unstable ./examples/deno/wt_client_uni_recv.ts", "demo:gencert": "deno run -A --unstable ./examples/deno/wt_gencert.ts", // CI Build task "build:release": "cargo clean && cargo build --release", diff --git a/examples/deno/wt_client_bidi.ts b/examples/deno/wt_client_bidi.ts index de3740b..befd9f5 100644 --- a/examples/deno/wt_client_bidi.ts +++ b/examples/deno/wt_client_bidi.ts @@ -8,7 +8,7 @@ const client = new WebTransport(connectAddr, { keepAlive: 3, }); -const transport = await client.ready(); +const transport = await client.ready; console.log("Client connected"); const stream = transport.incomingBidirectionalStreams; const reader = stream.getReader(); diff --git a/examples/deno/wt_client_uni_recv.ts b/examples/deno/wt_client_uni_recv.ts index 8afed95..0a23952 100644 --- a/examples/deno/wt_client_uni_recv.ts +++ b/examples/deno/wt_client_uni_recv.ts @@ -8,7 +8,7 @@ const client = new WebTransport(connectAddr, { keepAlive: 3, }); -const transport = await client.ready(); +const transport = await client.ready; console.log("Client connected"); const currentTime = performance.now(); diff --git a/examples/deno/wt_client_uni_send.ts b/examples/deno/wt_client_uni_send.ts index a862c28..aa4113d 100644 --- a/examples/deno/wt_client_uni_send.ts +++ b/examples/deno/wt_client_uni_send.ts @@ -8,7 +8,7 @@ const client = new WebTransport(connectAddr, { keepAlive: 3, }); -const transport = await client.ready(); +const transport = await client.ready; console.log("Client connected"); const _currentTime = performance.now(); diff --git a/mod/client.ts b/mod/client.ts index 4d697dd..76a31f0 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -13,8 +13,6 @@ import { encodeBuf } from "./utils.ts"; export class WebTransport { #CONN_PTR: Deno.PointerValue | undefined; - #STATE_PTR = new Uint32Array(1); - #NOTIFY_PTR = new Deno.UnsafeCallback( { parameters: ["u32", "pointer", "u32"], @@ -31,7 +29,7 @@ export class WebTransport { this.connection.bind(this), ); public datagrams!: WebTransportDatagramDuplexStream; - + public readonly ready: Promise; public conn?: WebTransportConnection; /** * @typedef {Deno.NetAddr} remote @@ -69,6 +67,18 @@ export class WebTransport { /// ref the callback to prevent it from being garbage collected this.#NOTIFY_PTR.ref(); this.#CONNECTION_CB.ref(); + this.ready = new Promise((resolve) => { + const encoded = encodeBuf(this.remote.href); + window.WTLIB.symbols.proc_client_connect( + this.#CONN_PTR!, + this.#CONNECTION_CB.pointer, + encoded[0], + encoded[1], + ); + if (this.conn) { + resolve(this.conn); + } + }); } /** * @callback connection @@ -119,9 +129,9 @@ export class WebTransport { buflen, ); const data = new TextDecoder().decode(pointer); - if (code >= 400) { - this.conn!.close(); - await Promise.reject(this.ready); + if (code >= 130) { + await Promise.race([this.ready]); + this.close(); throw new Error(data); } const _event = new MessageEvent("error", { @@ -132,37 +142,27 @@ export class WebTransport { //TODO(hironichu): Implement Error/event catching from rust to free the memory once a connection drop or if something else happens. } - ready = () => - new Promise(() => { - const encoded = encodeBuf(this.remote.href); - window.WTLIB.symbols.proc_client_connect( - this.#CONN_PTR!, - this.#CONNECTION_CB.pointer, - encoded[0], - encoded[1], - ); - if (this.conn) { - return this.conn; - } else { - throw new Error("Failed to connect to server"); - } - }); - close() { this.#NOTIFY_PTR.unref(); this.#NOTIFY_PTR.close(); this.#CONNECTION_CB.unref(); this.#CONNECTION_CB.close(); //close the datagram - this.datagrams.readable.cancel("Connection closed"); - this.datagrams.writable.abort("Connection closed"); + if (this.datagrams) { + this.datagrams.close(); + } - if (this.#CONN_PTR) { + if (this.#CONN_PTR && this.conn) { window.WTLIB.symbols.proc_client_close( this.#CONN_PTR, this.conn!.pointer, ); } + if (this.#CONN_PTR && !this.conn) { + window.WTLIB.symbols.free_client( + this.#CONN_PTR, + ); + } return true; } diff --git a/mod/connection.ts b/mod/connection.ts index 4484fb9..0188b65 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -8,7 +8,7 @@ interface WebTransportCloseInfo { reason?: string; } export class WebTransportDatagramDuplexStream { - #READ_BUFFER: Uint8Array; + #READ_BUFFER?: Uint8Array; readonly incomingHighWaterMark = 1; readonly incomingMaxAge = 0; readonly maxDatagramSize = 1024; @@ -47,7 +47,7 @@ export class WebTransportDatagramDuplexStream { } get readable() { const connection = this.connection; - const buffer = this.#READ_BUFFER; + const buffer = this.#READ_BUFFER ?? new Uint8Array(1024); const StreamBuffer = new ReadableStream({ async pull(controller) { try { @@ -69,6 +69,11 @@ export class WebTransportDatagramDuplexStream { }); return StreamBuffer; } + close() { + this.#READ_BUFFER = undefined; + this.readable.cancel(); + this.writable.abort(); + } } export class WebTransportBidirectionalStream { @@ -88,6 +93,7 @@ export class WebTransportReceiveStream { } static from( ptr: Deno.PointerValue, + DEFAULT_CHUNK_SIZE = 1024, ) { return new ReadableStream({ type: "bytes", @@ -115,7 +121,19 @@ export class WebTransportReceiveStream { } controller.byobRequest.respond(bytesRead as number); } else { - // return; + const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); + bytesRead = await window.WTLIB.symbols.proc_read( + ptr, + buffer, + DEFAULT_CHUNK_SIZE, + ); + if (bytesRead === 0) { + controller.close(); + } else { + controller.enqueue( + new Uint8Array(buffer, 0, bytesRead as number), + ); + } } if (bytesRead === 0) { return; @@ -127,7 +145,7 @@ export class WebTransportReceiveStream { if (!ptr || ptr === null) { return; } - console.log("CANCEL ", reason); + console.error("Canceled: ", reason); await window.WTLIB.symbols.proc_recvtream_stop(ptr).catch( (e) => { console.error(e); diff --git a/mod/interface.ts b/mod/interface.ts index 435fbd6..2158f22 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -65,7 +65,7 @@ export const symbols = { }, proc_client_connect: { parameters: ["pointer", "function", "buffer", "usize"], - result: "pointer", + result: "void", callback: true, }, proc_client_close: { @@ -173,4 +173,8 @@ export const symbols = { parameters: ["pointer", "pointer"], result: "void", }, + free_client: { + parameters: ["pointer"], + result: "void", + }, } as const; diff --git a/src/client.rs b/src/client.rs index b680559..ebda98d 100644 --- a/src/client.rs +++ b/src/client.rs @@ -119,3 +119,6 @@ pub unsafe extern "C" fn proc_client_close( #[no_mangle] pub unsafe extern "C" fn free_all_client(_a: *mut WebTransportClient, _b: *mut Conn) {} + +#[no_mangle] +pub unsafe extern "C" fn free_client(_a: *mut WebTransportClient) {} diff --git a/src/shared.rs b/src/shared.rs index ba287f3..90fab02 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -125,7 +125,7 @@ pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut Bid })), Err(err) => { let mut msg = err.to_string(); - SEND_FN.unwrap()(151, msg.as_mut_ptr(), msg.len() as u32); + SEND_FN.unwrap()(188, msg.as_mut_ptr(), msg.len() as u32); std::ptr::null_mut() } } From ebdbb26196cde66d06aa9b04c76f4b1dadb6e0c2 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 12:17:09 +0200 Subject: [PATCH 55/72] Changing Callback calls --- .gitignore | 4 +- .vscode/launch.json | 38 +++++++++ Cargo.toml | 7 +- examples/deno/wt_client_test.ts | 34 ++++---- examples/deno/wt_client_uni_send.ts | 4 + examples/deno/wt_server_test.ts | 42 ---------- examples/deno/wt_server_uni_recv.ts | 49 +++++------ mod/client.ts | 66 +++++++-------- mod/connection.ts | 31 +++---- mod/interface.ts | 1 + mod/server.ts | 5 +- src/client.rs | 39 +++++---- src/connection.rs | 21 ++--- src/lib.rs | 2 +- src/server.rs | 7 +- src/shared.rs | 125 ++++++++++++++++++++-------- 16 files changed, 267 insertions(+), 208 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.gitignore b/.gitignore index 93c03d0..fee9251 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,6 @@ Cargo.lock .env -*.env \ No newline at end of file +*.env + +.VSCodeCounter/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..bfac3ff --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,38 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "request": "launch", + "name": "server-uni-recv", + "type": "node", + "program": "${workspaceFolder}/examples/deno/wt_server_uni_recv.ts", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "C:\\Users\\zenze\\.deno\\bin\\deno.EXE", + "runtimeArgs": [ + "run", + "--unstable", + "--inspect-wait", + "--allow-all" + ], + "attachSimplePort": 9229 + }, + { + "request": "launch", + "name": "client-uni-send", + "type": "node", + "program": "${workspaceFolder}/examples/deno/wt_client_uni_send.ts", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "C:\\Users\\zenze\\.deno\\bin\\deno.EXE", + "runtimeArgs": [ + "run", + "--unstable", + "--inspect-wait", + "--allow-all" + ], + "attachSimplePort": 9229 + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 4e1f2cf..07c4cb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,12 +41,13 @@ tokio = { version = "=1.32.0", default-features = false, features = [ "macros", ] } # wtransport = "0.1.4" # TODO: Replace this once the fix for arm is merged. -wtransport = { git = "https://github.com/hironichu/wtransport", branch = "feat/new", features = [ +wtransport = { git = "https://github.com/hironichu/wtransport", branch = "master", features = [ "dangerous-configuration", + "quinn", ] } -wtransport-proto = { git = "https://github.com/hironichu/wtransport", branch = "feat/new" } +wtransport-proto = { git = "https://github.com/hironichu/wtransport", branch = "master" } rcgen = "=0.11.2" ring = "=0.16.20" -time = "=0.3.28" +time = "=0.3.29" anyhow = "=1.0.75" serde = { version = "=1.0", features = ["derive"] } diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts index 9094858..1d9dbab 100644 --- a/examples/deno/wt_client_test.ts +++ b/examples/deno/wt_client_test.ts @@ -36,21 +36,25 @@ Deno.test( }, async () => { //THis causes panic??????? - // const server = new WebTransportServer(4433, { - // certFile: "./certs/cert.pem", - // keyFile: "./certs/key.pem", - // maxTimeout: 10, - // keepAlive: 5, - // }); + const server = new WebTransportServer("https://localhost:4433", { + certFile: "./certs/localhost.crt", + keyFile: "./certs/localhost.key", + maxTimeout: 10, + keepAlive: 3, + }); + server.listen(); - // const client = new WebTransport("https://localhost:4433", { - // maxTimeout: 50, - // keepAlive: 3, - // validateCertificate: false, - // }); - // server.on("connection", (_) => { - // console.log("OK"); - // // await client.close(); - // }); + const client = new WebTransport("https://localhost:4433", { + maxTimeout: 50, + keepAlive: 3, + }); + await client.ready; + server.on("connection", async (_) => { + setTimeout(async () => { + await client.closed; + }, 2000); + }); + console.log("Closing"); + server.close(); }, ); diff --git a/examples/deno/wt_client_uni_send.ts b/examples/deno/wt_client_uni_send.ts index aa4113d..c19e278 100644 --- a/examples/deno/wt_client_uni_send.ts +++ b/examples/deno/wt_client_uni_send.ts @@ -22,3 +22,7 @@ await writer.write( new TextEncoder().encode("Hello from client"), ); console.log("Stream opened"); + +Deno.serve((req) => { + return new Response("Hello " + req.url); +}); diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 658e86c..0b7855f 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -32,45 +32,3 @@ Deno.test({ name: "Server startup/close" }, () => { assert(sock, "Server did not close"); sock.close(); }); - -// Deno.test( -// { name: "Server with generated certificate startup/close" }, -// async (test) => { -// const _srv = GetTestServer(); -// const client = new WebTransport("https://localhost:4433"); - -// await client.ready; - -// await test.step("Client connection", () => { -// assert(client.ready, "Client not ready"); -// }); -// const testCases = [1]; -// Promise.all(testCases.map(async (testCase) => { -// await test.step({ -// name: "Client message Receiving: " + testCase, -// fn: async () => { -// console.info("steup listener"); -// for await (const data of client.datagrams.readable) { -// console.log(data); -// } -// }, -// sanitizeOps: false, -// sanitizeResources: false, -// sanitizeExit: false, -// }); - -// await test.step({ -// name: "Client message Sending: " + testCase, -// fn: async () => { -// console.log("SENDING SHIT"); -// const writer = client.datagrams.writable.getWriter(); -// await writer.write(new Uint8Array([1, 2, 3, 4, 5])); -// }, -// sanitizeOps: false, -// sanitizeResources: false, -// sanitizeExit: false, -// }); -// })).then(client.close); -// _srv.close(); -// }, -// ); diff --git a/examples/deno/wt_server_uni_recv.ts b/examples/deno/wt_server_uni_recv.ts index 2f60c20..6bb1210 100644 --- a/examples/deno/wt_server_uni_recv.ts +++ b/examples/deno/wt_server_uni_recv.ts @@ -25,7 +25,7 @@ const server = new WebTransportServer("https://localhost:4433", { keepAlive: 3, }); -server.listen(); +await server.listen(); console.log("Server listening"); server.on("connection", async (transport) => { console.log("New client"); @@ -33,36 +33,27 @@ server.on("connection", async (transport) => { const stream = transport.incomingUnidirectionalStreams; const reader = stream.getReader(); while (true) { - const view = new Uint8Array(100); const { value, done } = await reader.read(); - const DATA = value!.getReader({ mode: "byob" }); + const DATA = value!.getReader(); console.log("New incoming stream opened ", value); - while (value) { - DATA.read(view); - console.log(view); - if (done) break; - } - console.log("new uni stream opened ", value); + let { value: val, done: d } = await DATA.read(); + console.log(val); + if (done) break; } - //stream should close after 20 messages - - // const writer = sendStream.getWriter(); - // //wait 5 seeconds and send 2 message every 2 secondes - // await new Promise((resolve) => setTimeout(resolve, 2000)); - // console.log("starting sends"); - // await writer.write(new TextEncoder().encode("Hello from server")); - // await writer.write(new TextEncoder().encode("Hello from server 2")); - // await writer.write(new TextEncoder().encode("Hello from server 3")); - // let sent = 0; - // const inter = setInterval(() => { - // writer.write(new Uint8Array([1, 2, 3, 4, 5])); - // writer.write(new Uint8Array([1, 2, 3, 4, 5])); - // if (sent === 10) { - // clearInterval(inter); - // //close the stream (this will also finish the stream on rust side) - // writer.close(); - // } - // sent++; - // }, 2000); }); +const client = new WebTransport("https://localhost:4433"); +await client.ready; +console.log("Client connected"); + +//after 5 seconds call closed on client +setTimeout(async () => { + console.log("Client client after 2 seconds"); + await client.closed; +}, 2000); + +//after 10 seconds call close on server +setTimeout(() => { + console.log("Server closed after 5 seconds"); + server.close(); +}, 5000); diff --git a/mod/client.ts b/mod/client.ts index 76a31f0..e09c378 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -30,14 +30,8 @@ export class WebTransport { ); public datagrams!: WebTransportDatagramDuplexStream; public readonly ready: Promise; + public conn?: WebTransportConnection; - /** - * @typedef {Deno.NetAddr} remote - * @property {string} transport - * @property {string} hostname - * @property {number} port - * @description This object contains the remote address of the server - */ protected remote: URL; constructor( _client: URL | string, @@ -67,7 +61,7 @@ export class WebTransport { /// ref the callback to prevent it from being garbage collected this.#NOTIFY_PTR.ref(); this.#CONNECTION_CB.ref(); - this.ready = new Promise((resolve) => { + this.ready = new Promise((resolve, reject) => { const encoded = encodeBuf(this.remote.href); window.WTLIB.symbols.proc_client_connect( this.#CONN_PTR!, @@ -77,6 +71,9 @@ export class WebTransport { ); if (this.conn) { resolve(this.conn); + } else { + this.conn = undefined; + reject("Failed to connect to server"); } }); } @@ -130,41 +127,42 @@ export class WebTransport { ); const data = new TextDecoder().decode(pointer); if (code >= 130) { - await Promise.race([this.ready]); - this.close(); + // await Promise.race([this.closed]); + await this.closed; throw new Error(data); } const _event = new MessageEvent("error", { data, }); dispatchEvent(_event); - - //TODO(hironichu): Implement Error/event catching from rust to free the memory once a connection drop or if something else happens. } + get closed() { + return new Promise((resolve) => { + this.#NOTIFY_PTR.unref(); + this.#CONNECTION_CB.unref(); + + //close the datagrams + if (this.datagrams) { + this.datagrams.close(); + } + //close all the streams - close() { - this.#NOTIFY_PTR.unref(); - this.#NOTIFY_PTR.close(); - this.#CONNECTION_CB.unref(); - this.#CONNECTION_CB.close(); - //close the datagram - if (this.datagrams) { - this.datagrams.close(); - } - - if (this.#CONN_PTR && this.conn) { - window.WTLIB.symbols.proc_client_close( - this.#CONN_PTR, - this.conn!.pointer, - ); - } - if (this.#CONN_PTR && !this.conn) { - window.WTLIB.symbols.free_client( - this.#CONN_PTR, - ); - } + if (this.#CONN_PTR && this.conn) { + window.WTLIB.symbols.proc_client_close( + this.#CONN_PTR, + this.conn!.pointer, + ); + } + if (this.#CONN_PTR && !this.conn) { + window.WTLIB.symbols.free_client( + this.#CONN_PTR, + ); + } - return true; + resolve(true); + // this.#NOTIFY_PTR.close(); + // this.#CONNECTION_CB.close(); + }); } } diff --git a/mod/connection.ts b/mod/connection.ts index 0188b65..3b1b396 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -7,6 +7,7 @@ interface WebTransportCloseInfo { errorCode?: number; reason?: string; } + export class WebTransportDatagramDuplexStream { #READ_BUFFER?: Uint8Array; readonly incomingHighWaterMark = 1; @@ -23,25 +24,30 @@ export class WebTransportDatagramDuplexStream { get writable() { const connection = this.connection; return new WritableStream({ - write(chunk) { + start(controller) { + if (!connection.pointer) { + controller.error("Connection is closed"); + return; + } + }, + write(chunk: Uint8Array, controller) { try { window.WTLIB.symbols.proc_send_datagram( connection.pointer!, chunk, chunk.byteLength, ); - - return chunk.byteLength; + return; } catch (e) { - console.error(e); - return -1; + controller.error(e); + return; } }, - abort() { - console.error("[Error] Write aborted"); + abort(e) { + console.error("[Error] Write aborted", e); }, close() { - // console.log("[Info] Write closed"); + console.log("[Info] Write closed"); }, }); } @@ -72,7 +78,7 @@ export class WebTransportDatagramDuplexStream { close() { this.#READ_BUFFER = undefined; this.readable.cancel(); - this.writable.abort(); + this.writable.close(); } } @@ -157,8 +163,6 @@ export class WebTransportReceiveStream { } export class WebTransportSendStream { - constructor(private ptr: Deno.PointerValue) { - } static from( ptr: Deno.PointerValue, ) { @@ -173,8 +177,7 @@ export class WebTransportSendStream { return; } try { - console.log("Writing chunk: ", chunk); - written = await window.WTLIB.symbols.proc_write( + written = await window.WTLIB.symbols.proc_write_all( ptr, chunk, chunk.byteLength, @@ -236,8 +239,6 @@ export default class WebTransportConnection { this.incomingBidirectionalStreams = new ReadableStream< WebTransportBidirectionalStream >({ - // start(_) { - // }, async start(controller) { try { const stream = await window.WTLIB.symbols diff --git a/mod/interface.ts b/mod/interface.ts index 2158f22..314b4ed 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -47,6 +47,7 @@ export const symbols = { parameters: ["pointer", "function"], result: "pointer", callback: true, + nonblocking: true, }, proc_server_close: { parameters: ["pointer"], diff --git a/mod/server.ts b/mod/server.ts index 91ba84a..037fa40 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -144,10 +144,7 @@ export class WebTransportServer extends EventEmitter { close() { this.#NOTIFY_PTR.unref(); - this.#NOTIFY_PTR.close(); - // this.#CONNECTION_CB.unref(); - this.#CONNECTION_CB.close(); if (this.#SRV_PTR) { // await window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); @@ -166,6 +163,8 @@ export class WebTransportServer extends EventEmitter { }); window.WTLIB.symbols.free_server(this.#SRV_PTR!); + this.#NOTIFY_PTR.close(); + this.#CONNECTION_CB.close(); this.#SRV_PTR = undefined; return; } diff --git a/src/client.rs b/src/client.rs index ebda98d..ad395d2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,8 +1,8 @@ use crate::{ connection::{self, Client, Conn}, - CLIENT_CONN_FN, RUNTIME, SEND_FN, + CLIENT_CONN_FN, RUNTIME, }; -use std::time::Duration; +use std::{io::Error, time::Duration}; use wtransport::endpoint::endpoint_side::Client as endClient; use wtransport::{ClientConfig, Endpoint}; @@ -10,43 +10,46 @@ pub struct WebTransportClient { pub client: Option>, pub conn_cb: Option)>, pub state: Option, + pub cb: extern "C" fn(u32, *mut u8, u32), } impl WebTransportClient { - pub(crate) unsafe fn new( + pub(crate) fn new( sender_fn: Option, config: ClientConfig, - ) -> Result { - SEND_FN = sender_fn; + ) -> Result { let _guard = RUNTIME.enter(); let client = match Endpoint::client(config) { Ok(server) => server, Err(e) => { - println!("Error creating client: {:?}", e); - return Err(2); + return Err(e); } }; Ok(Self { + cb: sender_fn.unwrap(), conn_cb: None, client: Some(client), state: Some(true), }) } - pub(crate) unsafe fn connect(&'static mut self, url: String) { + pub(crate) fn connect(&'static mut self, url: String) { RUNTIME.block_on(async move { - match self.client.as_mut().unwrap().connect(url).await { + let client = self.client.as_mut().unwrap(); + + let sender_cb = self.cb; + match client.connect(url).await { Ok(conn) => { - let client = Conn::::new(conn); + let client = Conn::::new(conn, sender_cb); let client_ptr = Box::into_raw(Box::new(client)); - assert!(!CLIENT_CONN_FN.is_none()); - CLIENT_CONN_FN.unwrap()(client_ptr); + unsafe { + CLIENT_CONN_FN.unwrap()(client_ptr); + } } Err(err) => { - println!("DBG: Error connecting to server. Err: {}", err.to_string()); let mut msg = err.to_string(); - SEND_FN.unwrap()(141, msg.as_mut_ptr(), msg.len() as u32); + sender_cb(141, msg.as_mut_ptr(), msg.len() as u32); } } }); @@ -54,7 +57,7 @@ impl WebTransportClient { } #[no_mangle] -pub unsafe extern "C" fn proc_client_init( +pub extern "C" fn proc_client_init( send_func: Option, keepalive: u64, timeout: u64, @@ -82,8 +85,10 @@ pub unsafe extern "C" fn proc_client_init( let client = WebTransportClient::new(send_func, config); match client { Ok(client) => Box::into_raw(Box::new(client)), - Err(_) => { - SEND_FN.unwrap()(140, std::ptr::null_mut(), 0); + Err(error) => { + let message = error.kind(); + let mut msg = message.to_string(); + send_func.unwrap()(10, msg.as_mut_ptr(), msg.len() as u32); std::ptr::null_mut() } } diff --git a/src/connection.rs b/src/connection.rs index abce704..d22aef8 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -17,24 +17,20 @@ pub struct Conn { pub conn: Option, pub accepted_session: Option, pub buffer: Option<&'static mut [u8]>, + pub cb: extern "C" fn(u32, *mut u8, u32), _marker: PhantomData, } impl Conn { - pub fn datagrams(&mut self) -> Result { + pub fn read_datagram(&mut self) -> Result { let conn = self.conn.as_ref().unwrap(); let stream = RUNTIME.block_on(async move { conn.receive_datagram().await }); match stream { Ok(dgram) => Ok(dgram), - _ => { - //We should close the connection from Deno. - // conn.closed().await; - //TODO(hironichu): Send action to Deno to free the pointer and buffer - // SEND_FN.unwrap()(client, std::ptr::null_mut(), 0); - Err(0) - } + Err(error) => Err(error), } } + pub async fn closed(&mut self) { let conn = self.conn.as_ref().unwrap(); conn.closed().await @@ -109,12 +105,16 @@ impl Conn { } impl Conn { - pub(crate) fn new(accepted_session: SessionRequest) -> Self { + pub(crate) fn new( + accepted_session: SessionRequest, + cb: extern "C" fn(u32, *mut u8, u32), + ) -> Self { Self { conn: None, accepted_session: Some(accepted_session), buffer: None, _marker: PhantomData, + cb, } } pub fn accepted(&mut self, conn: Connection) { @@ -136,11 +136,12 @@ impl Conn { } impl Conn { - pub(crate) fn new(conn: Connection) -> Self { + pub(crate) fn new(conn: Connection, cb: extern "C" fn(u32, *mut u8, u32)) -> Self { Self { conn: Some(conn), accepted_session: None, buffer: None, + cb, _marker: PhantomData, } } diff --git a/src/lib.rs b/src/lib.rs index 4edb308..632851e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use once_cell::sync::Lazy; use tokio::runtime::Runtime; ///------------------------------------ -static mut SEND_FN: Option = None; +// static mut SEND_FN: Option = None; static mut SERVER_CONN_FN: Option)> = None; static mut CLIENT_CONN_FN: Option)> = None; static RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); diff --git a/src/server.rs b/src/server.rs index b86f00f..be7208c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,6 @@ use crate::{ connection::{self, Conn, Server}, - RUNTIME, SEND_FN, SERVER_CONN_FN, + RUNTIME, SERVER_CONN_FN, }; use std::{path::Path, time::Duration}; use tokio::runtime::Runtime; @@ -10,6 +10,7 @@ use wtransport::{tls::Certificate, Endpoint, ServerConfig}; pub struct WebTransportServer { pub server: Option>, pub state: Option, + pub sender_fn: Option, } impl WebTransportServer { @@ -17,7 +18,6 @@ impl WebTransportServer { sender_fn: Option, config: ServerConfig, ) -> Result { - SEND_FN = sender_fn; let _guard = RUNTIME.enter(); let server = match Endpoint::server(config) { @@ -30,6 +30,7 @@ impl WebTransportServer { Ok(Self { server: Some(server), state: Some(true), + sender_fn, }) } @@ -40,7 +41,7 @@ impl WebTransportServer { let accepted_session = match session_request { Ok(session_request) => { - let client = Conn::::new(session_request); + let client = Conn::::new(session_request, self.sender_fn.unwrap()); Ok(client) } Err(e) => { diff --git a/src/shared.rs b/src/shared.rs index 90fab02..d963d06 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,19 +1,50 @@ use crate::{ connection::{Conn, Server}, - RUNTIME, SEND_FN, + RUNTIME, }; use std::slice::from_raw_parts_mut; use tokio::runtime::Runtime; -use wtransport::{error::SendDatagramError, RecvStream, SendStream}; +use wtransport::{ + error::{ConnectionError, SendDatagramError}, + RecvStream, SendStream, +}; + +//impl a way to identify ConnectionError with a number for JS +pub struct ConnectionErrorWrapper(pub ConnectionError); + +// Implement the From trait for the new type +impl From for u32 { + fn from(wrapper: ConnectionErrorWrapper) -> Self { + match wrapper.0 { + ConnectionError::TimedOut => 170, + ConnectionError::ApplicationClosed(_) => 171, + ConnectionError::ConnectionClosed(_) => 172, + ConnectionError::LocalH3Error(_) => 173, + ConnectionError::QuicProto => 174, + ConnectionError::LocallyClosed => 175, + } + } +} + +unsafe fn send_error(code: u32, message: String, sender_fn: extern "C" fn(u32, *mut u8, u32)) { + let mut msg = message; + sender_fn(code, msg.as_mut_ptr(), msg.len() as u32); +} #[repr(C)] pub struct BidiStreams { + pub source: *mut Conn, pub send: Option, pub recv: Option, } -/// Send a datagram. +/// Send a datagram +/// Error Codes : +/// 161 : Connection closed +/// 162 : Too large +/// 163 : Not supported by peer +/// 164 : Other error #[no_mangle] pub unsafe extern "C" fn proc_send_datagram( connptr: *mut Conn, @@ -24,22 +55,24 @@ pub unsafe extern "C" fn proc_send_datagram( let client = &mut *connptr; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); - match client.conn.as_ref().unwrap().send_datagram(buf) { + let conn = client.conn.as_ref().unwrap(); + let sender_cb = client.cb; + match conn.send_datagram(buf) { Ok(_) => {} Err(err) => { //TODO: Handle error better match err { SendDatagramError::NotConnected => { println!("DBG: Rust Connection closed"); - SEND_FN.unwrap()(161, vec![0].as_mut_ptr(), 1); + sender_cb(161, vec![0].as_mut_ptr(), 1); } SendDatagramError::TooLarge => { println!("DBG: Rust Too large"); - SEND_FN.unwrap()(162, vec![0].as_mut_ptr(), 1); + sender_cb(162, vec![0].as_mut_ptr(), 1); } SendDatagramError::UnsupportedByPeer => { println!("DBG: Rust not supported by peer"); - SEND_FN.unwrap()(163, vec![0].as_mut_ptr(), 1); + sender_cb(163, vec![0].as_mut_ptr(), 1); } }; } @@ -47,45 +80,54 @@ pub unsafe extern "C" fn proc_send_datagram( } /// Receive a datagram. +/// Error Codes : +/// 170 : Connection closed +/// 171 : ApplicationClosed +/// 172 : ConnectionClosed +/// 173 : LocalH3Error +/// 174 : QuicProto +/// 175 : LocallyClosed #[no_mangle] pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: *mut u8) -> usize { assert!(!conn_ptr.is_null()); let client = &mut *conn_ptr; - - match client.datagrams() { + let sender_cb = client.cb; + match client.read_datagram() { Ok(dgram) => { from_raw_parts_mut(buff, dgram.len()).clone_from_slice(&dgram); dgram.len() } - Err(err) => { - let mut msg = err.to_string(); - SEND_FN.unwrap()(156, msg.as_mut_ptr(), msg.len() as u32); + Err(error) => { + let message = error.to_string(); + send_error(ConnectionErrorWrapper(error).into(), message, sender_cb); 0 } } } -/// Open a bidirectional stream. (Temporary because unsafe) -#[allow(improper_ctypes_definitions)] +/// Open a bidirectional stream. +/// Error Codes : +/// 150 : Connection closed #[no_mangle] pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiStreams { assert!(!connptr.is_null()); let _client = &mut *connptr; + let sender_cb = _client.cb; let stream = _client.open_bi(); match stream { Ok((send, recv)) => { let bidi = BidiStreams { + source: connptr, send: Some(send), recv: Some(recv), }; Box::into_raw(Box::new(bidi)) } - Err(err) => { - println!("Error opening bidirectional stream: {:?}", err); - let mut msg = err.to_string(); - SEND_FN.unwrap()(155, msg.as_mut_ptr(), msg.len() as u32); + Err(error) => { + let message = error.to_string(); + send_error(ConnectionErrorWrapper(error).into(), message, sender_cb); std::ptr::null_mut() } } @@ -97,15 +139,17 @@ pub unsafe extern "C" fn proc_open_uni(connptr: *mut Conn) -> *mut BidiS assert!(!connptr.is_null()); let _client = &mut *connptr; + let cb = _client.cb; let stream = _client.open_uni(); match stream { Ok(stream) => Box::into_raw(Box::new(BidiStreams { + source: connptr, send: Some(stream), recv: None, })), - Err(err) => { - let mut msg = err.to_string(); - SEND_FN.unwrap()(152, msg.as_mut_ptr(), msg.len() as u32); + Err(error) => { + let message = error.to_string(); + send_error(ConnectionErrorWrapper(error).into(), message, cb); std::ptr::null_mut() } } @@ -117,15 +161,17 @@ pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut Bid assert!(!connptr.is_null()); let _client = &mut *connptr; + let cb = _client.cb; let stream = _client.accept_uni(); match stream { Ok(stream) => Box::into_raw(Box::new(BidiStreams { + source: connptr, send: None, recv: Some(stream), })), Err(err) => { - let mut msg = err.to_string(); - SEND_FN.unwrap()(188, msg.as_mut_ptr(), msg.len() as u32); + let msg = err.to_string(); + send_error(160, msg, cb); std::ptr::null_mut() } } @@ -137,18 +183,21 @@ pub unsafe extern "C" fn proc_accept_bi(connptr: *mut Conn) -> *mut Bidi assert!(!connptr.is_null()); let _client = &mut *connptr; + let cb = _client.cb; let stream = _client.accept_bi(); + // let cb = _client.unwrap(); match stream { Ok((send, recv)) => { let bidi = BidiStreams { + source: connptr, send: Some(send), recv: Some(recv), }; Box::into_raw(Box::new(bidi)) } Err(err) => { - let mut msg = err.to_string(); - SEND_FN.unwrap()(152, msg.as_mut_ptr(), msg.len() as u32); + let msg = err.to_string(); + send_error(160, msg, cb); std::ptr::null_mut() } } @@ -165,14 +214,15 @@ pub unsafe extern "C" fn proc_write( assert!(buflen > 0); let bidi_streams = &mut *stream_ptr; + let source = &mut *bidi_streams.source; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); let writer = bidi_streams.send.as_mut().unwrap(); let writenlen = RUNTIME.block_on(async move { match writer.write(buf).await { Ok(len) => len, Err(err) => { - let mut str = err.to_string(); - SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); + let msg = err.to_string(); + send_error(153, msg, source.cb); 0 } } @@ -189,14 +239,15 @@ pub unsafe extern "C" fn proc_write_all( assert!(!stream_ptr.is_null()); assert!(buflen > 0); let stream = &mut *stream_ptr; + let source = &mut *stream.source; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); let writer = stream.send.as_mut().unwrap(); let writenlen = RUNTIME.block_on(async move { match writer.write_all(buf).await { Ok(_) => buflen, Err(err) => { - let mut str = err.to_string(); - SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); + let str = err.to_string(); + send_error(153, str, source.cb); 0 } } @@ -218,13 +269,14 @@ pub unsafe extern "C" fn proc_read( assert!(buflen > 0); let stream = &mut *stream_ptr; + let source = &mut *stream.source; let buf = ::std::slice::from_raw_parts_mut(buf, buflen as usize); let readlen = RUNTIME.block_on(async move { match stream.recv.as_mut().unwrap().read(buf).await { Ok(len) => len, Err(err) => { - let mut strs = err.to_string(); - SEND_FN.unwrap()(154, strs.as_mut_ptr(), strs.len() as u32); + let strs = err.to_string(); + send_error(154, strs, source.cb); None } } @@ -256,13 +308,14 @@ pub unsafe extern "C" fn proc_sendstream_id(stream_ptr: *mut BidiStreams) -> u64 pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) { assert!(!stream_ptr.is_null()); let stream = &mut *stream_ptr; + let source = &mut *stream.source; let sendstream = stream.send.as_mut().unwrap(); RUNTIME.block_on(async move { match sendstream.finish().await { Ok(_) => drop(stream_ptr.as_ref()), Err(err) => { - let mut msg = err.to_string(); - SEND_FN.unwrap()(150, msg.as_mut_ptr(), msg.len() as u32); + let msg = err.to_string(); + send_error(150, msg, source.cb); } } }); @@ -272,11 +325,13 @@ pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) { pub unsafe extern "C" fn proc_recvtream_stop(stream_ptr: *mut BidiStreams) { assert!(!stream_ptr.is_null()); let stream = &mut *stream_ptr; + let source = &mut *stream.source; RUNTIME.block_on(async move { match stream.recv.as_mut().unwrap().stop(0).await { Ok(_) => drop(stream_ptr.as_ref()), - Err(_) => { - SEND_FN.unwrap()(158, std::ptr::null_mut(), 0); + Err(err) => { + let msg = format!("Error stopping recv stream : {:?}", err); + send_error(158, msg, source.cb); } } }); From 7a9b9a02947e02299d58a478be918ec5d521dca3 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 15:17:37 +0200 Subject: [PATCH 56/72] Reversed back CB to normal use --- examples/deno/wt_client_test.ts | 4 +- examples/deno/wt_server.ts | 3 +- examples/deno/wt_server_uni_recv.ts | 30 +++++------- mod/client.ts | 5 +- mod/interface.ts | 2 - mod/server.ts | 24 +++++----- src/client.rs | 14 +++--- src/connection.rs | 10 +--- src/lib.rs | 2 +- src/server.rs | 73 ++++++++++++++++------------- src/shared.rs | 65 ++++++++++--------------- 11 files changed, 107 insertions(+), 125 deletions(-) diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts index 1d9dbab..677c238 100644 --- a/examples/deno/wt_client_test.ts +++ b/examples/deno/wt_client_test.ts @@ -42,14 +42,14 @@ Deno.test( maxTimeout: 10, keepAlive: 3, }); - server.listen(); + await server.listen(); const client = new WebTransport("https://localhost:4433", { maxTimeout: 50, keepAlive: 3, }); await client.ready; - server.on("connection", async (_) => { + server.on("connection", (_) => { setTimeout(async () => { await client.closed; }, 2000); diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index ad566f4..c487a19 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -25,7 +25,8 @@ const server = new WebTransportServer("https://localhost:4433", { keepAlive: 3, }); -server.listen(); +await server.ready; + console.log("Server listening"); server.on("connection", (conn) => { console.log("New client"); diff --git a/examples/deno/wt_server_uni_recv.ts b/examples/deno/wt_server_uni_recv.ts index 6bb1210..ac60220 100644 --- a/examples/deno/wt_server_uni_recv.ts +++ b/examples/deno/wt_server_uni_recv.ts @@ -17,43 +17,39 @@ try { console.error("Invalid certFile or keyFile"); Deno.exit(1); } - +Deno.serve(() => new Response("Welcome to Deno 🦕")); const server = new WebTransportServer("https://localhost:4433", { certFile: "./certs/localhost.crt", keyFile: "./certs/localhost.key", maxTimeout: 10, keepAlive: 3, }); +server.on("listening", () => { + console.log("Server listening"); +}); -await server.listen(); -console.log("Server listening"); server.on("connection", async (transport) => { console.log("New client"); - + setTimeout(async () => { + console.log("Client client after 5 seconds"); + await client.closed; + }, 5000); const stream = transport.incomingUnidirectionalStreams; const reader = stream.getReader(); while (true) { const { value, done } = await reader.read(); + if (done) break; const DATA = value!.getReader(); console.log("New incoming stream opened ", value); - let { value: val, done: d } = await DATA.read(); + const { value: val, done: _ } = await DATA.read(); console.log(val); if (done) break; } }); -const client = new WebTransport("https://localhost:4433"); -await client.ready; -console.log("Client connected"); +await server.ready; //after 5 seconds call closed on client -setTimeout(async () => { - console.log("Client client after 2 seconds"); - await client.closed; -}, 2000); -//after 10 seconds call close on server -setTimeout(() => { - console.log("Server closed after 5 seconds"); - server.close(); -}, 5000); +const client = new WebTransport("https://localhost:4433"); +await client.ready; diff --git a/mod/client.ts b/mod/client.ts index e09c378..e792018 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -128,8 +128,9 @@ export class WebTransport { const data = new TextDecoder().decode(pointer); if (code >= 130) { // await Promise.race([this.closed]); - await this.closed; - throw new Error(data); + console.log("We should close the connection"); + console.log(data); + // throw new Error(data); } const _event = new MessageEvent("error", { data, diff --git a/mod/interface.ts b/mod/interface.ts index 314b4ed..17092d3 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -52,7 +52,6 @@ export const symbols = { proc_server_close: { parameters: ["pointer"], result: "usize", - nonblocking: true, }, // Client symbols proc_client_init: { @@ -72,7 +71,6 @@ export const symbols = { proc_client_close: { parameters: ["pointer", "pointer"], result: "void", - nonblocking: true, }, // Shared symbols proc_recv_datagram: { diff --git a/mod/server.ts b/mod/server.ts index 037fa40..12ee788 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -142,18 +142,15 @@ export class WebTransportServer extends EventEmitter { this.emit("event", event); } - close() { + async close() { this.#NOTIFY_PTR.unref(); this.#CONNECTION_CB.unref(); - if (this.#SRV_PTR) { - // await window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); - } this.emit("close", new CloseEvent("close")); //free all the connections - this.connections.forEach((conn, id) => { + this.connections.forEach(async (conn, id) => { if (conn.state != "closed") { - window.WTLIB.symbols.proc_client_close( + await window.WTLIB.symbols.proc_client_close( this.#SRV_PTR!, conn.pointer, ); @@ -161,19 +158,22 @@ export class WebTransportServer extends EventEmitter { window.WTLIB.symbols.free_conn(conn.pointer); this.connections.delete(id); }); + if (this.#SRV_PTR) { + window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); + window.WTLIB.symbols.free_server(this.#SRV_PTR!); + this.#NOTIFY_PTR.close(); + this.#CONNECTION_CB.close(); + } - window.WTLIB.symbols.free_server(this.#SRV_PTR!); - this.#NOTIFY_PTR.close(); - this.#CONNECTION_CB.close(); this.#SRV_PTR = undefined; + console.log("Server closed"); return; } - public async listen() { - await window.WTLIB.symbols.proc_server_listen( + get ready() { + return window.WTLIB.symbols.proc_server_listen( this.#SRV_PTR!, this.#CONNECTION_CB.pointer, ); - return this; } private checkArgs(_options: WebTransportServerOptions) { if ( diff --git a/src/client.rs b/src/client.rs index ad395d2..bce4b86 100644 --- a/src/client.rs +++ b/src/client.rs @@ -10,7 +10,7 @@ pub struct WebTransportClient { pub client: Option>, pub conn_cb: Option)>, pub state: Option, - pub cb: extern "C" fn(u32, *mut u8, u32), + pub sender_cb: extern "C" fn(u32, *mut u8, u32), } impl WebTransportClient { @@ -27,7 +27,7 @@ impl WebTransportClient { } }; Ok(Self { - cb: sender_fn.unwrap(), + sender_cb: sender_fn.unwrap(), conn_cb: None, client: Some(client), state: Some(true), @@ -37,17 +37,17 @@ impl WebTransportClient { pub(crate) fn connect(&'static mut self, url: String) { RUNTIME.block_on(async move { let client = self.client.as_mut().unwrap(); - - let sender_cb = self.cb; + let sender_cb = self.sender_cb; match client.connect(url).await { Ok(conn) => { - let client = Conn::::new(conn, sender_cb); + let client = Conn::::new(conn); let client_ptr = Box::into_raw(Box::new(client)); unsafe { CLIENT_CONN_FN.unwrap()(client_ptr); } } Err(err) => { + println!("DBG: Error connecting to server. Err: {}", err.to_string()); let mut msg = err.to_string(); sender_cb(141, msg.as_mut_ptr(), msg.len() as u32); } @@ -115,10 +115,12 @@ pub unsafe extern "C" fn proc_client_close( ) -> usize { assert!(!client_ptr.is_null()); assert!(!conn.is_null()); + println!("CALLED"); let client = &mut *client_ptr; let conn = &mut *conn; client.state = Some(false); - RUNTIME.block_on(async move { conn.closed().await }); + // RUNTIME.block_on(async move { conn.close(32, Some(b"NO")) }); + conn.close(32, Some(b"NO")); 0 } diff --git a/src/connection.rs b/src/connection.rs index d22aef8..ea9067a 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -17,7 +17,6 @@ pub struct Conn { pub conn: Option, pub accepted_session: Option, pub buffer: Option<&'static mut [u8]>, - pub cb: extern "C" fn(u32, *mut u8, u32), _marker: PhantomData, } @@ -105,16 +104,12 @@ impl Conn { } impl Conn { - pub(crate) fn new( - accepted_session: SessionRequest, - cb: extern "C" fn(u32, *mut u8, u32), - ) -> Self { + pub(crate) fn new(accepted_session: SessionRequest) -> Self { Self { conn: None, accepted_session: Some(accepted_session), buffer: None, _marker: PhantomData, - cb, } } pub fn accepted(&mut self, conn: Connection) { @@ -136,12 +131,11 @@ impl Conn { } impl Conn { - pub(crate) fn new(conn: Connection, cb: extern "C" fn(u32, *mut u8, u32)) -> Self { + pub(crate) fn new(conn: Connection) -> Self { Self { conn: Some(conn), accepted_session: None, buffer: None, - cb, _marker: PhantomData, } } diff --git a/src/lib.rs b/src/lib.rs index 632851e..4edb308 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use once_cell::sync::Lazy; use tokio::runtime::Runtime; ///------------------------------------ -// static mut SEND_FN: Option = None; +static mut SEND_FN: Option = None; static mut SERVER_CONN_FN: Option)> = None; static mut CLIENT_CONN_FN: Option)> = None; static RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); diff --git a/src/server.rs b/src/server.rs index be7208c..7d8c179 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,6 @@ use crate::{ connection::{self, Conn, Server}, - RUNTIME, SERVER_CONN_FN, + RUNTIME, SEND_FN, SERVER_CONN_FN, }; use std::{path::Path, time::Duration}; use tokio::runtime::Runtime; @@ -10,7 +10,6 @@ use wtransport::{tls::Certificate, Endpoint, ServerConfig}; pub struct WebTransportServer { pub server: Option>, pub state: Option, - pub sender_fn: Option, } impl WebTransportServer { @@ -18,6 +17,7 @@ impl WebTransportServer { sender_fn: Option, config: ServerConfig, ) -> Result { + SEND_FN = sender_fn; let _guard = RUNTIME.enter(); let server = match Endpoint::server(config) { @@ -30,42 +30,44 @@ impl WebTransportServer { Ok(Self { server: Some(server), state: Some(true), - sender_fn, }) } pub(crate) async unsafe fn handle_sess_in(&mut self) -> Result<*mut Conn, u32> { - let incoming_session = self.server.as_mut().unwrap().accept().await; + let incoming_session = self.server.as_mut(); + match incoming_session { + Some(incoming_session) => { + let session_request = incoming_session.accept().await; - let session_request = incoming_session.await; + let accepted_session = match session_request.await { + Ok(session_request) => { + let client = Conn::::new(session_request); + Ok(client) + } + Err(e) => Err(e), + }; + match accepted_session { + Ok(mut sess) => match sess.accept().await { + Ok(conn) => { + sess.accepted(conn); + let client_ptr = Box::into_raw(Box::new(sess)); + Ok(client_ptr) + } + Err(e) => { + println!("Error accepting connection : {}", e.to_string()); - let accepted_session = match session_request { - Ok(session_request) => { - let client = Conn::::new(session_request, self.sender_fn.unwrap()); - Ok(client) - } - Err(e) => { - //TODO(hironichu): Map this code to an error in Typescript - Err(e) - } - }; - match accepted_session { - Ok(mut sess) => match sess.accept().await { - Ok(conn) => { - sess.accepted(conn); - let client_ptr = Box::into_raw(Box::new(sess)); - Ok(client_ptr) + Err(0) + } + }, + Err(error) => { + println!("Error accepting session : {}", error.to_string()); + Err(0) + } } - Err(e) => { - println!("Error accepting connection : {}", e.to_string()); - //TODO(hironichu): Map this code to an error in Typescript - Err(0) - } - }, - _ => { - println!("Error accepting session"); - //TODO(hironichu): Map this code to an error in Typescript - Err(0) + } + None => { + println!("Server endpoint is None (should be closed by now and not be called.."); + return Err(0); } } } @@ -153,8 +155,13 @@ pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) let server = &mut *server_ptr; server.state = Some(false); - let endpoint = server.server.as_mut().unwrap(); - endpoint.close(20, b"closed"); + let endpoint = server.server.as_mut(); + match endpoint { + Some(endpoint) => RUNTIME.block_on(async move { + endpoint.wait_idle().await; + }), + None => println!("Error closing server"), + } 0 } diff --git a/src/shared.rs b/src/shared.rs index d963d06..d1fe005 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,6 +1,6 @@ use crate::{ connection::{Conn, Server}, - RUNTIME, + RUNTIME, SEND_FN, }; use std::slice::from_raw_parts_mut; @@ -27,14 +27,13 @@ impl From for u32 { } } -unsafe fn send_error(code: u32, message: String, sender_fn: extern "C" fn(u32, *mut u8, u32)) { +unsafe fn send_error(code: u32, message: String) { let mut msg = message; - sender_fn(code, msg.as_mut_ptr(), msg.len() as u32); + SEND_FN.unwrap()(code, msg.as_mut_ptr(), msg.len() as u32); } #[repr(C)] pub struct BidiStreams { - pub source: *mut Conn, pub send: Option, pub recv: Option, } @@ -56,7 +55,6 @@ pub unsafe extern "C" fn proc_send_datagram( let client = &mut *connptr; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); let conn = client.conn.as_ref().unwrap(); - let sender_cb = client.cb; match conn.send_datagram(buf) { Ok(_) => {} Err(err) => { @@ -64,15 +62,15 @@ pub unsafe extern "C" fn proc_send_datagram( match err { SendDatagramError::NotConnected => { println!("DBG: Rust Connection closed"); - sender_cb(161, vec![0].as_mut_ptr(), 1); + SEND_FN.unwrap()(161, vec![0].as_mut_ptr(), 1); } SendDatagramError::TooLarge => { println!("DBG: Rust Too large"); - sender_cb(162, vec![0].as_mut_ptr(), 1); + SEND_FN.unwrap()(162, vec![0].as_mut_ptr(), 1); } SendDatagramError::UnsupportedByPeer => { println!("DBG: Rust not supported by peer"); - sender_cb(163, vec![0].as_mut_ptr(), 1); + SEND_FN.unwrap()(163, vec![0].as_mut_ptr(), 1); } }; } @@ -92,7 +90,7 @@ pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: * assert!(!conn_ptr.is_null()); let client = &mut *conn_ptr; - let sender_cb = client.cb; + match client.read_datagram() { Ok(dgram) => { from_raw_parts_mut(buff, dgram.len()).clone_from_slice(&dgram); @@ -100,7 +98,7 @@ pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: * } Err(error) => { let message = error.to_string(); - send_error(ConnectionErrorWrapper(error).into(), message, sender_cb); + send_error(ConnectionErrorWrapper(error).into(), message); 0 } } @@ -114,12 +112,10 @@ pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiSt assert!(!connptr.is_null()); let _client = &mut *connptr; - let sender_cb = _client.cb; let stream = _client.open_bi(); match stream { Ok((send, recv)) => { let bidi = BidiStreams { - source: connptr, send: Some(send), recv: Some(recv), }; @@ -127,7 +123,7 @@ pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiSt } Err(error) => { let message = error.to_string(); - send_error(ConnectionErrorWrapper(error).into(), message, sender_cb); + send_error(ConnectionErrorWrapper(error).into(), message); std::ptr::null_mut() } } @@ -139,17 +135,15 @@ pub unsafe extern "C" fn proc_open_uni(connptr: *mut Conn) -> *mut BidiS assert!(!connptr.is_null()); let _client = &mut *connptr; - let cb = _client.cb; let stream = _client.open_uni(); match stream { Ok(stream) => Box::into_raw(Box::new(BidiStreams { - source: connptr, send: Some(stream), recv: None, })), Err(error) => { let message = error.to_string(); - send_error(ConnectionErrorWrapper(error).into(), message, cb); + send_error(ConnectionErrorWrapper(error).into(), message); std::ptr::null_mut() } } @@ -161,17 +155,15 @@ pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut Bid assert!(!connptr.is_null()); let _client = &mut *connptr; - let cb = _client.cb; let stream = _client.accept_uni(); match stream { Ok(stream) => Box::into_raw(Box::new(BidiStreams { - source: connptr, send: None, recv: Some(stream), })), Err(err) => { - let msg = err.to_string(); - send_error(160, msg, cb); + let mut msg = err.to_string(); + SEND_FN.unwrap()(160, msg.as_mut_ptr(), msg.len() as u32); std::ptr::null_mut() } } @@ -183,21 +175,18 @@ pub unsafe extern "C" fn proc_accept_bi(connptr: *mut Conn) -> *mut Bidi assert!(!connptr.is_null()); let _client = &mut *connptr; - let cb = _client.cb; let stream = _client.accept_bi(); - // let cb = _client.unwrap(); match stream { Ok((send, recv)) => { let bidi = BidiStreams { - source: connptr, send: Some(send), recv: Some(recv), }; Box::into_raw(Box::new(bidi)) } Err(err) => { - let msg = err.to_string(); - send_error(160, msg, cb); + let mut msg = err.to_string(); + SEND_FN.unwrap()(155, msg.as_mut_ptr(), msg.len() as u32); std::ptr::null_mut() } } @@ -214,15 +203,14 @@ pub unsafe extern "C" fn proc_write( assert!(buflen > 0); let bidi_streams = &mut *stream_ptr; - let source = &mut *bidi_streams.source; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); let writer = bidi_streams.send.as_mut().unwrap(); let writenlen = RUNTIME.block_on(async move { match writer.write(buf).await { Ok(len) => len, Err(err) => { - let msg = err.to_string(); - send_error(153, msg, source.cb); + let mut str = err.to_string(); + SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); 0 } } @@ -239,15 +227,14 @@ pub unsafe extern "C" fn proc_write_all( assert!(!stream_ptr.is_null()); assert!(buflen > 0); let stream = &mut *stream_ptr; - let source = &mut *stream.source; let buf = ::std::slice::from_raw_parts(buf, buflen as usize); let writer = stream.send.as_mut().unwrap(); let writenlen = RUNTIME.block_on(async move { match writer.write_all(buf).await { Ok(_) => buflen, Err(err) => { - let str = err.to_string(); - send_error(153, str, source.cb); + let mut str = err.to_string(); + SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); 0 } } @@ -269,14 +256,13 @@ pub unsafe extern "C" fn proc_read( assert!(buflen > 0); let stream = &mut *stream_ptr; - let source = &mut *stream.source; let buf = ::std::slice::from_raw_parts_mut(buf, buflen as usize); let readlen = RUNTIME.block_on(async move { match stream.recv.as_mut().unwrap().read(buf).await { Ok(len) => len, Err(err) => { - let strs = err.to_string(); - send_error(154, strs, source.cb); + let mut strs = err.to_string(); + SEND_FN.unwrap()(154, strs.as_mut_ptr(), strs.len() as u32); None } } @@ -308,14 +294,13 @@ pub unsafe extern "C" fn proc_sendstream_id(stream_ptr: *mut BidiStreams) -> u64 pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) { assert!(!stream_ptr.is_null()); let stream = &mut *stream_ptr; - let source = &mut *stream.source; let sendstream = stream.send.as_mut().unwrap(); RUNTIME.block_on(async move { match sendstream.finish().await { Ok(_) => drop(stream_ptr.as_ref()), Err(err) => { - let msg = err.to_string(); - send_error(150, msg, source.cb); + let mut msg = err.to_string(); + SEND_FN.unwrap()(150, msg.as_mut_ptr(), msg.len() as u32); } } }); @@ -325,13 +310,11 @@ pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) { pub unsafe extern "C" fn proc_recvtream_stop(stream_ptr: *mut BidiStreams) { assert!(!stream_ptr.is_null()); let stream = &mut *stream_ptr; - let source = &mut *stream.source; RUNTIME.block_on(async move { match stream.recv.as_mut().unwrap().stop(0).await { Ok(_) => drop(stream_ptr.as_ref()), - Err(err) => { - let msg = format!("Error stopping recv stream : {:?}", err); - send_error(158, msg, source.cb); + Err(_) => { + SEND_FN.unwrap()(158, std::ptr::null_mut(), 0); } } }); From f2faa80351ef25434adceeb8a97e1b2df502b169 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 16:36:31 +0200 Subject: [PATCH 57/72] Changed CB workflow.. --- examples/deno/wt_client.ts | 4 +- examples/deno/wt_client_test.ts | 2 +- examples/deno/wt_client_uni_send.ts | 6 +- examples/deno/wt_server.ts | 7 +- mod/client.ts | 114 +++++------ mod/connection.ts | 298 +++++++--------------------- mod/crypto.ts | 2 +- mod/interface.ts | 151 -------------- mod/lib.ts | 2 +- mod/mod.ts | 7 +- mod/server.ts | 67 +++---- mod/streams.ts | 204 +++++++++++++++++++ mod/symbols.ts | 168 ++++++++++++++++ mod/utils.ts | 3 +- src/client.rs | 55 ++--- src/connection.rs | 16 +- src/lib.rs | 9 +- src/server.rs | 18 +- src/shared.rs | 110 ++++++---- 19 files changed, 652 insertions(+), 591 deletions(-) create mode 100644 mod/streams.ts create mode 100644 mod/symbols.ts diff --git a/examples/deno/wt_client.ts b/examples/deno/wt_client.ts index 8d3033a..977a71f 100644 --- a/examples/deno/wt_client.ts +++ b/examples/deno/wt_client.ts @@ -11,7 +11,7 @@ const client = new WebTransport(connectAddr, { // console.log(client); -await client.ready(); +await client.ready; console.log("Client connected"); const writer = client.datagrams.writable.getWriter(); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); @@ -20,7 +20,7 @@ writer?.write(new Uint8Array([1, 2, 3, 4, 5])); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); writer?.write(new Uint8Array([1, 2, 3, 4, 5])); -// //await messages +// // //await messages for await (const read of client.datagrams.readable) { console.log(read); } diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts index 677c238..245e40c 100644 --- a/examples/deno/wt_client_test.ts +++ b/examples/deno/wt_client_test.ts @@ -42,7 +42,7 @@ Deno.test( maxTimeout: 10, keepAlive: 3, }); - await server.listen(); + await server.ready; const client = new WebTransport("https://localhost:4433", { maxTimeout: 50, diff --git a/examples/deno/wt_client_uni_send.ts b/examples/deno/wt_client_uni_send.ts index c19e278..22bbf18 100644 --- a/examples/deno/wt_client_uni_send.ts +++ b/examples/deno/wt_client_uni_send.ts @@ -23,6 +23,6 @@ await writer.write( ); console.log("Stream opened"); -Deno.serve((req) => { - return new Response("Hello " + req.url); -}); +// Deno.serve((req) => { +// return new Response("Hello " + req.url); +// }); diff --git a/examples/deno/wt_server.ts b/examples/deno/wt_server.ts index c487a19..fabf878 100644 --- a/examples/deno/wt_server.ts +++ b/examples/deno/wt_server.ts @@ -28,9 +28,14 @@ const server = new WebTransportServer("https://localhost:4433", { await server.ready; console.log("Server listening"); -server.on("connection", (conn) => { +server.on("connection", async (conn) => { console.log("New client"); const bidiStream = conn.datagrams; const writer = bidiStream.writable.getWriter(); writer.write(new Uint8Array([1, 2, 3, 4, 5])); + + //read incoming datagrams + for await (const read of bidiStream.readable) { + console.log(read); + } }); diff --git a/mod/client.ts b/mod/client.ts index e792018..9533a84 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -1,35 +1,33 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } -import WebTransportConnection, { - WebTransportDatagramDuplexStream, -} from "./connection.ts"; - +import WebTransportConnection from "./connection.ts"; +import { WebTransportDatagramDuplexStream } from "./streams.ts"; import { type WebTransportOptions, WebTransportOptions as ServerOpts, } from "./interface.ts"; -import { encodeBuf } from "./utils.ts"; +import { decoder, encodeBuf } from "./utils.ts"; export class WebTransport { #CONN_PTR: Deno.PointerValue | undefined; - #NOTIFY_PTR = new Deno.UnsafeCallback( - { - parameters: ["u32", "pointer", "u32"], - result: "void", - }, - this.notify.bind(this), - ); - - #CONNECTION_CB = new Deno.UnsafeCallback( - { - parameters: ["pointer"], - result: "void", - }, - this.connection.bind(this), - ); + // #NOTIFY_PTR = new Deno.UnsafeCallback( + // { + // parameters: ["u32", "pointer", "u32"], + // result: "void", + // }, + // this.notify.bind(this), + // ); + + // #CONNECTION_CB = new Deno.UnsafeCallback( + // { + // parameters: ["pointer"], + // result: "void", + // }, + // this.connection.bind(this), + // ); public datagrams!: WebTransportDatagramDuplexStream; - public readonly ready: Promise; + // public readonly ready: Promise; public conn?: WebTransportConnection; protected remote: URL; @@ -42,15 +40,10 @@ export class WebTransport { _client = new URL(_client); } - try { - this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( - this.#NOTIFY_PTR.pointer, - _options.keepAlive, - _options.maxTimeout, - ); - } catch (e) { - console.error(e); - } + this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( + _options.keepAlive, + _options.maxTimeout, + ); if (!this.#CONN_PTR) { throw new Error("Failed to initialize client"); @@ -59,23 +52,8 @@ export class WebTransport { this.remote = _client; /// ref the callback to prevent it from being garbage collected - this.#NOTIFY_PTR.ref(); - this.#CONNECTION_CB.ref(); - this.ready = new Promise((resolve, reject) => { - const encoded = encodeBuf(this.remote.href); - window.WTLIB.symbols.proc_client_connect( - this.#CONN_PTR!, - this.#CONNECTION_CB.pointer, - encoded[0], - encoded[1], - ); - if (this.conn) { - resolve(this.conn); - } else { - this.conn = undefined; - reject("Failed to connect to server"); - } - }); + // this.#NOTIFY_PTR.ref(); + // this.#CONNECTION_CB.ref(); } /** * @callback connection @@ -87,7 +65,6 @@ export class WebTransport { if (!client || client == null) { return; } - Promise.resolve(this.ready); const CONN_BUFFER = new Uint8Array(65536); this.conn = new WebTransportConnection( @@ -110,26 +87,25 @@ export class WebTransport { * * @description This function is called when a new event is received from the server */ - private async notify( + private notify( _code: unknown | number, buffer: Deno.PointerValue, buflen: number, ) { const code = _code as number; - + console.log("[CB CLIENT] Got code", code); if (buflen < 0) { return; } - const pointer = Deno.UnsafePointerView.getArrayBuffer( - buffer as unknown as NonNullable, + buffer!, buflen, ); - const data = new TextDecoder().decode(pointer); + const data = decoder.decode(pointer); if (code >= 130) { - // await Promise.race([this.closed]); - console.log("We should close the connection"); - console.log(data); + // Promise.race([this.closed]); + console.error("[CB CLIENT] We should close the connection"); + console.error(data); // throw new Error(data); } const _event = new MessageEvent("error", { @@ -138,10 +114,7 @@ export class WebTransport { dispatchEvent(_event); } get closed() { - return new Promise((resolve) => { - this.#NOTIFY_PTR.unref(); - this.#CONNECTION_CB.unref(); - + return new Promise(() => { //close the datagrams if (this.datagrams) { this.datagrams.close(); @@ -159,12 +132,29 @@ export class WebTransport { this.#CONN_PTR, ); } - - resolve(true); + // this.#NOTIFY_PTR.unref(); + // this.#CONNECTION_CB.unref(); // this.#NOTIFY_PTR.close(); // this.#CONNECTION_CB.close(); }); } + get ready() { + return new Promise((resolve, reject) => { + const encoded = encodeBuf(this.remote.href); + const conn = window.WTLIB.symbols.proc_client_connect( + this.#CONN_PTR!, + encoded[0], + encoded[1], + ); + this.connection(conn); + if (this.conn) { + resolve(this.conn); + } else { + this.conn = undefined; + reject("Failed to connect to server"); + } + }); + } } export default WebTransport; diff --git a/mod/connection.ts b/mod/connection.ts index 3b1b396..1255ea1 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -1,4 +1,12 @@ // import { FFI_CODES } from "./code.ts"; + +import { + WebTransportBidirectionalStream, + WebTransportDatagramDuplexStream, + WebTransportReceiveStream, + WebTransportSendStream, +} from "./streams.ts"; + /// interface WebTransportSendStreamOptions { sendOrder?: number | null; @@ -8,209 +16,6 @@ interface WebTransportCloseInfo { reason?: string; } -export class WebTransportDatagramDuplexStream { - #READ_BUFFER?: Uint8Array; - readonly incomingHighWaterMark = 1; - readonly incomingMaxAge = 0; - readonly maxDatagramSize = 1024; - readonly outgoingHighWaterMark = 1; - readonly outgoingMaxAge = 0; - constructor( - private connection: WebTransportConnection, - _buffer: Uint8Array, - ) { - this.#READ_BUFFER = _buffer; - } - get writable() { - const connection = this.connection; - return new WritableStream({ - start(controller) { - if (!connection.pointer) { - controller.error("Connection is closed"); - return; - } - }, - write(chunk: Uint8Array, controller) { - try { - window.WTLIB.symbols.proc_send_datagram( - connection.pointer!, - chunk, - chunk.byteLength, - ); - return; - } catch (e) { - controller.error(e); - return; - } - }, - abort(e) { - console.error("[Error] Write aborted", e); - }, - close() { - console.log("[Info] Write closed"); - }, - }); - } - get readable() { - const connection = this.connection; - const buffer = this.#READ_BUFFER ?? new Uint8Array(1024); - const StreamBuffer = new ReadableStream({ - async pull(controller) { - try { - const nread = await window.WTLIB.symbols.proc_recv_datagram( - connection.pointer!, - buffer, - ); - if (nread > 0) { - controller.enqueue( - buffer.subarray(0, nread as number), - ); - } - } catch (e) { - controller.error(e); - } - }, - cancel() { - }, - }); - return StreamBuffer; - } - close() { - this.#READ_BUFFER = undefined; - this.readable.cancel(); - this.writable.close(); - } -} - -export class WebTransportBidirectionalStream { - readonly readable: ReadableStream; - readonly writable: WritableStream; - - constructor( - public ptr: Deno.PointerValue, - ) { - this.readable = WebTransportReceiveStream.from(this.ptr); - this.writable = WebTransportSendStream.from(this.ptr); - } -} -export class WebTransportReceiveStream { - private static readonly ptr: Deno.PointerValue; - constructor(private readonly ptr: Deno.PointerValue) { - } - static from( - ptr: Deno.PointerValue, - DEFAULT_CHUNK_SIZE = 1024, - ) { - return new ReadableStream({ - type: "bytes", - start( - _, - ) { - }, - pull(controller) { - readRepeatedly().catch((e) => controller.error(e)); - async function readRepeatedly() { - if (!ptr || ptr === null) { - throw new Error("Stream is closed"); - } - let bytesRead; - if (controller.byobRequest) { - const v = controller.byobRequest.view; - bytesRead = await window.WTLIB.symbols.proc_read( - ptr, - v?.buffer!, - v?.byteLength!, - ); - if (bytesRead === 0) { - console.log("BYOB REQUEST"); - controller.close(); - } - controller.byobRequest.respond(bytesRead as number); - } else { - const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); - bytesRead = await window.WTLIB.symbols.proc_read( - ptr, - buffer, - DEFAULT_CHUNK_SIZE, - ); - if (bytesRead === 0) { - controller.close(); - } else { - controller.enqueue( - new Uint8Array(buffer, 0, bytesRead as number), - ); - } - } - if (bytesRead === 0) { - return; - } - return readRepeatedly(); - } - }, - async cancel(reason?: string): Promise { - if (!ptr || ptr === null) { - return; - } - console.error("Canceled: ", reason); - await window.WTLIB.symbols.proc_recvtream_stop(ptr).catch( - (e) => { - console.error(e); - }, - ); - }, - }); - } -} - -export class WebTransportSendStream { - static from( - ptr: Deno.PointerValue, - ) { - return new WritableStream({ - async write( - chunk: Uint8Array, - controller: WritableStreamDefaultController, - ) { - let written = 0; - if (!ptr || ptr === null) { - controller.error("Stream is closed"); - return; - } - try { - written = await window.WTLIB.symbols.proc_write_all( - ptr, - chunk, - chunk.byteLength, - ) as number; - if (written === 0) { - controller.error("Write failed"); - return; - } - } catch (e) { - console.error(e); - return; - } - }, - async abort() { - if (!ptr || ptr === null) { - return; - } - await window.WTLIB.symbols.proc_sendstream_finish( - ptr, - ); - }, - async close() { - if (!ptr || ptr === null) { - return; - } - await window.WTLIB.symbols.proc_sendstream_finish( - ptr, - ); - }, - }); - } -} - export default class WebTransportConnection { state: | "connected" @@ -219,14 +24,13 @@ export default class WebTransportConnection { | "failed" | "connecting" = "closed" as const; - // readonly #CONN_PTR!: Deno.PointerValue; public readonly datagrams: WebTransportDatagramDuplexStream; - public readonly incomingBidirectionalStreams: ReadableStream< - WebTransportBidirectionalStream - >; - public readonly incomingUnidirectionalStreams: ReadableStream< - ReadableStream - >; + // public readonly incomingBidirectionalStreams: ReadableStream< + // WebTransportBidirectionalStream + // >; + // public readonly incomingUnidirectionalStreams: ReadableStream< + // ReadableStream + // >; constructor( public readonly pointer: Deno.PointerValue, @@ -235,41 +39,68 @@ export default class WebTransportConnection { this.state = "connected"; // this.#CONN_PTR = pointer; this.datagrams = new WebTransportDatagramDuplexStream(this, buffer); - - this.incomingBidirectionalStreams = new ReadableStream< + } + get incomingBidirectionalStreams() { + const pointer = this.pointer; + const errorPTR = this.error.pointer; + return new ReadableStream< WebTransportBidirectionalStream >({ async start(controller) { try { + if ( + (!pointer || pointer === null) || + (!errorPTR || errorPTR === null) + ) { + controller.close(); + return; + } const stream = await window.WTLIB.symbols .proc_accept_bi( pointer, + errorPTR, ); - if (!stream) { + if (!stream || stream === null) { + console.error("[incoming BIDI] Stream not accepted"); controller.close(); return; } - const jsStream = new WebTransportBidirectionalStream( - stream, + controller.enqueue( + new WebTransportBidirectionalStream( + stream, + ), ); - controller.enqueue(jsStream); } catch (e) { controller.error(e); } }, cancel() { + console.info("[incoming BIDI] Cancelled"); }, }); - this.incomingUnidirectionalStreams = new ReadableStream< + } + get incomingUnidirectionalStreams() { + const pointer = this.pointer; + const errorPTR = this.error.pointer; + return new ReadableStream< ReadableStream >({ async start(controller) { + if ( + (!pointer || pointer === null) || + (!errorPTR || errorPTR === null) + ) { + controller.close(); + return; + } try { const stream = await window.WTLIB.symbols .proc_accept_uni( pointer, + errorPTR, ); - if (!stream) { + if (!stream || stream === null) { + console.error("[incoming UNI] Stream not accepted"); controller.close(); return; } @@ -281,22 +112,23 @@ export default class WebTransportConnection { } }, cancel() { + console.info("[incoming UNI] Cancelled"); }, }); } - public async createBidirectionalStream( _options?: WebTransportSendStreamOptions, ): Promise { //The following operation block the thread until the stream is created. - if (!this.pointer) { + if (!this.pointer || this.pointer === null) { throw new Error("Connection is closed"); } const _streams = await window.WTLIB.symbols.proc_open_bi( this.pointer, + this.error.pointer, ); - if (!_streams) { - throw new Error("Failed to create stream"); + if (!_streams || _streams === null) { + throw new Error("Failed to create bi stream"); } return new WebTransportBidirectionalStream(_streams); } @@ -304,31 +136,35 @@ export default class WebTransportConnection { public async createUnidirectionalStream( _options?: WebTransportSendStreamOptions, ): Promise { - if (!this.pointer) { + if (!this.pointer || this.pointer === null) { throw new Error("Connection is closed"); } //The following operation block the thread until the stream is created. const _streams = await window.WTLIB.symbols.proc_open_uni( this.pointer, + this.error.pointer, ); - if (!_streams) { - throw new Error("Failed to create stream"); + if (!_streams || _streams === null) { + throw new Error("Failed to create uni stream"); } const stream = WebTransportSendStream.from(_streams); return stream; } - async close(_closeInfo?: WebTransportCloseInfo) { - this.state = "closed"; - if (!this.pointer || this.pointer === null) { - throw new Error("Connection is closed"); - } + private error = new Deno.UnsafeCallback({ + parameters: ["pointer"], + result: "void", + }, (_pointer) => { + }); + close(_closeInfo?: WebTransportCloseInfo) { if (!this.pointer || this.pointer === null) { throw new Error("Connection is closed"); } - await window.WTLIB.symbols.proc_client_close( + + this.state = "closed"; + window.WTLIB.symbols.proc_client_close( this.pointer, this.pointer, ); diff --git a/mod/crypto.ts b/mod/crypto.ts index 651e322..6b2f63d 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -1,7 +1,7 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } -import { join } from "https://deno.land/std@0.184.0/path/mod.ts"; +import { join } from "./deps.ts"; import { encodeBuf } from "./utils.ts"; export function base64ToArrayBuffer(base64: string) { const binaryString = atob(base64); diff --git a/mod/interface.ts b/mod/interface.ts index 17092d3..5b8a50b 100644 --- a/mod/interface.ts +++ b/mod/interface.ts @@ -26,154 +26,3 @@ export type WebTransportServerOptions = & Partial; export type WebTransportOptions = typeof WebTransportOptions; -export const symbols = { - // Server symbols - proc_server_init: { - parameters: [ - "function", //Callback - "u16", //Port - "bool", //Migration - "u64", //KeepAlive - "u64", //MaxTimeout - "buffer", //Cert - "usize", //CertLen - "buffer", //Key - "usize", //KeyLen - ], - result: "pointer", - callback: true, - }, - proc_server_listen: { - parameters: ["pointer", "function"], - result: "pointer", - callback: true, - nonblocking: true, - }, - proc_server_close: { - parameters: ["pointer"], - result: "usize", - }, - // Client symbols - proc_client_init: { - parameters: [ - "function", - "u64", //KeepAlive - "u64", //MaxTimeout - ], - result: "pointer", - callback: true, - }, - proc_client_connect: { - parameters: ["pointer", "function", "buffer", "usize"], - result: "void", - callback: true, - }, - proc_client_close: { - parameters: ["pointer", "pointer"], - result: "void", - }, - // Shared symbols - proc_recv_datagram: { - parameters: ["pointer", "buffer"], - result: "usize", - nonblocking: true, - }, - proc_send_datagram: { - parameters: ["pointer", "buffer", "usize"], - result: "void", - nonblocking: false, - }, - proc_accept_bi: { - parameters: ["pointer"], //Option<> - result: "pointer", - nonblocking: true, - }, - proc_open_bi: { - parameters: ["pointer"], //Option<> - result: "pointer", - nonblocking: true, - }, - - proc_accept_uni: { - parameters: ["pointer"], - result: "pointer", - nonblocking: true, - }, - proc_open_uni: { - parameters: ["pointer"], - result: "pointer", - nonblocking: true, - }, - - proc_read: { - parameters: ["pointer", "buffer", "u32"], - result: "usize", - nonblocking: true, - }, - proc_write: { - parameters: ["pointer", "buffer", "u32"], - result: "usize", - nonblocking: true, - }, - proc_write_all: { - parameters: ["pointer", "buffer", "u32"], - result: "usize", - nonblocking: true, - }, - proc_recvstream_id: { - parameters: ["pointer"], - result: "u64", - }, - proc_sendstream_id: { - parameters: ["pointer"], - result: "u64", - }, - proc_sendstream_priority: { - parameters: ["pointer"], - result: "u64", - }, - proc_sendstream_set_priority: { - parameters: ["pointer", "u64"], - result: "u64", - }, - proc_sendstream_finish: { - parameters: ["pointer"], - result: "void", - nonblocking: true, - }, - proc_recvtream_stop: { - parameters: ["pointer"], - result: "void", - nonblocking: true, - }, - // Crypto symbols - proc_gencert: { - parameters: [ - "buffer", - "usize", - "i64", - "i64", - "buffer", - "buffer", - "buffer", - "buffer", - ], - result: "bool", - }, - free_server: { - parameters: ["pointer"], - result: "void", - }, - free_conn: { - parameters: ["pointer"], - result: "void", - }, - free_all_client: { - parameters: ["pointer", "pointer"], - result: "void", - }, - free_client: { - parameters: ["pointer"], - result: "void", - }, -} as const; diff --git a/mod/lib.ts b/mod/lib.ts index 067c24f..07bfa95 100644 --- a/mod/lib.ts +++ b/mod/lib.ts @@ -1,6 +1,6 @@ import { toFileUrl } from "./deps.ts"; import LIB_URL from "../utils/download_lib.ts"; -import { symbols } from "./interface.ts"; +import { symbols } from "./symbols.ts"; let local = false; let download_lib: URL; diff --git a/mod/mod.ts b/mod/mod.ts index 7269d32..0bc1c00 100644 --- a/mod/mod.ts +++ b/mod/mod.ts @@ -1,8 +1,5 @@ -import { - symbols, - WebTransportOptions, - WebTransportServerOptions, -} from "./interface.ts"; +import { WebTransportOptions, WebTransportServerOptions } from "./interface.ts"; +import { symbols } from "./symbols.ts"; import { LIB } from "./lib.ts"; if (window.WTLIB_STATE) { diff --git a/mod/server.ts b/mod/server.ts index 12ee788..3256aea 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -8,7 +8,7 @@ import { type WebTransportServerOptions, WebTransportServerOptions as ServerOpts, } from "./interface.ts"; -import { encodeBuf } from "./utils.ts"; +import { decoder, encodeBuf } from "./utils.ts"; export type WebTransportServerEvents = { listening: [Event]; @@ -65,7 +65,6 @@ export class WebTransportServer extends EventEmitter { const keybuf = encodeBuf(key); this.#SRV_PTR = window.WTLIB.symbols.proc_server_init( - this.#NOTIFY_PTR.pointer, parseInt(_url.port), true, _options.keepAlive, @@ -80,7 +79,7 @@ export class WebTransportServer extends EventEmitter { throw new Error("Failed to initialize server"); } - this.#NOTIFY_PTR.ref(); + // this.#NOTIFY_PTR.ref(); this.#CONNECTION_CB.ref(); this.emit("listening", new Event("listening")); } @@ -124,52 +123,52 @@ export class WebTransportServer extends EventEmitter { buffer: Deno.PointerValue, buflen: number, ) { - console.info("CB NOTIFY CALLEd"); - const code = _code as bigint; - console.log(code); - if (buflen < 0) { - return; - } - const pointer = Deno.UnsafePointerView.getArrayBuffer( - buffer as unknown as NonNullable, - buflen, - ); - const event = new MessageEvent("message", { - data: pointer, - }); + //TODO(hironichu): This callback should implement more arguments to get an optional pointer to the connection its referring to + const code = _code as number; - //TODO(hironichu): Implement Error/event catching from rust to free the memory once a connection drop or if something else happens. - this.emit("event", event); - } + console.info("[CB SERVER] Got code : ", code); - async close() { - this.#NOTIFY_PTR.unref(); - this.#CONNECTION_CB.unref(); + if (buflen > 1) { + const pointer = Deno.UnsafePointerView.getArrayBuffer( + buffer as unknown as NonNullable, + buflen, + ); + const event = new MessageEvent("message", { + data: decoder.decode(pointer), + }); + console.log("Event data : ", event.data); + this.emit("event", event); + } + if (code >= 130) { + // Promise.race([this.closed]); + console.error("[CB SERVER] We should close the connection"); + // throw new Error(data); + } + } - this.emit("close", new CloseEvent("close")); + close() { //free all the connections - this.connections.forEach(async (conn, id) => { - if (conn.state != "closed") { - await window.WTLIB.symbols.proc_client_close( - this.#SRV_PTR!, + this.connections.forEach((conn, id) => { + if (conn.state != "closed" && this.#SRV_PTR && conn.pointer) { + window.WTLIB.symbols.proc_client_close( + this.#SRV_PTR, conn.pointer, ); + window.WTLIB.symbols.free_conn(conn.pointer); + conn.state = "closed"; } - window.WTLIB.symbols.free_conn(conn.pointer); this.connections.delete(id); }); if (this.#SRV_PTR) { window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); - window.WTLIB.symbols.free_server(this.#SRV_PTR!); - this.#NOTIFY_PTR.close(); - this.#CONNECTION_CB.close(); } - + this.#NOTIFY_PTR.close(); + this.#CONNECTION_CB.close(); this.#SRV_PTR = undefined; - console.log("Server closed"); - return; + console.info("[SERVER] Server closed"); } get ready() { + console.info("[SERVER] Server ready"); return window.WTLIB.symbols.proc_server_listen( this.#SRV_PTR!, this.#CONNECTION_CB.pointer, diff --git a/mod/streams.ts b/mod/streams.ts new file mode 100644 index 0000000..9bd9b00 --- /dev/null +++ b/mod/streams.ts @@ -0,0 +1,204 @@ +import type WebTransportConnection from "./connection.ts"; + +export class WebTransportDatagramDuplexStream { + #READ_BUFFER?: Uint8Array; + readonly incomingHighWaterMark = 1; + readonly incomingMaxAge = 0; + readonly maxDatagramSize = 1024; + readonly outgoingHighWaterMark = 1; + readonly outgoingMaxAge = 0; + constructor( + private connection: WebTransportConnection, + _buffer: Uint8Array, + ) { + this.#READ_BUFFER = _buffer; + } + get writable() { + const connection = this.connection; + return new WritableStream({ + start(controller) { + if (!connection.pointer) { + controller.error("Connection is closed"); + return; + } + }, + write(chunk: Uint8Array, controller) { + try { + window.WTLIB.symbols.proc_send_datagram( + connection.pointer!, + chunk, + chunk.byteLength, + ); + return; + } catch (e) { + controller.error(e); + return; + } + }, + abort(e) { + console.error("[Error] Write aborted", e); + }, + close() { + console.log("[Info] Write closed"); + }, + }); + } + get readable() { + const connection = this.connection; + const buffer = this.#READ_BUFFER ?? new Uint8Array(1024); + const StreamBuffer = new ReadableStream({ + async pull(controller) { + try { + const nread = await window.WTLIB.symbols.proc_recv_datagram( + connection.pointer!, + buffer, + ); + if (nread > 0) { + controller.enqueue( + buffer.subarray(0, nread as number), + ); + } + } catch (e) { + controller.error(e); + } + }, + cancel() { + }, + }); + return StreamBuffer; + } + close() { + this.#READ_BUFFER = undefined; + this.readable.cancel(); + this.writable.close(); + } +} + +export class WebTransportBidirectionalStream { + readonly readable: ReadableStream; + readonly writable: WritableStream; + + constructor( + public ptr: Deno.PointerValue, + ) { + this.readable = WebTransportReceiveStream.from(this.ptr); + this.writable = WebTransportSendStream.from(this.ptr); + } +} +export class WebTransportReceiveStream { + private static readonly ptr: Deno.PointerValue; + constructor(private readonly ptr: Deno.PointerValue) { + } + static from( + ptr: Deno.PointerValue, + DEFAULT_CHUNK_SIZE = 1024, + ) { + return new ReadableStream({ + type: "bytes", + start( + _, + ) { + }, + pull(controller) { + readRepeatedly().catch((e) => controller.error(e)); + async function readRepeatedly() { + if (!ptr || ptr === null) { + throw new Error("Stream is closed"); + } + let bytesRead; + if (controller.byobRequest) { + const v = controller.byobRequest.view; + bytesRead = await window.WTLIB.symbols.proc_read( + ptr, + v?.buffer!, + v?.byteLength!, + ); + if (bytesRead === 0) { + console.log("BYOB REQUEST"); + controller.close(); + } + controller.byobRequest.respond(bytesRead as number); + } else { + const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); + bytesRead = await window.WTLIB.symbols.proc_read( + ptr, + buffer, + DEFAULT_CHUNK_SIZE, + ); + if (bytesRead === 0) { + controller.close(); + } else { + controller.enqueue( + new Uint8Array(buffer, 0, bytesRead as number), + ); + } + } + if (bytesRead === 0) { + return; + } + return readRepeatedly(); + } + }, + async cancel(reason?: string): Promise { + if (!ptr || ptr === null) { + return; + } + console.error("Canceled: ", reason); + await window.WTLIB.symbols.proc_recvtream_stop(ptr).catch( + (e) => { + console.error(e); + }, + ); + }, + }); + } +} + +export class WebTransportSendStream { + static from( + ptr: Deno.PointerValue, + ) { + return new WritableStream({ + async write( + chunk: Uint8Array, + controller: WritableStreamDefaultController, + ) { + let written = 0; + if (!ptr || ptr === null) { + controller.error("Stream is closed"); + return; + } + try { + written = await window.WTLIB.symbols.proc_write_all( + ptr, + chunk, + chunk.byteLength, + ) as number; + if (written === 0) { + controller.error("Write failed"); + return; + } + } catch (e) { + console.error(e); + return; + } + }, + async abort() { + if (!ptr || ptr === null) { + return; + } + await window.WTLIB.symbols.proc_sendstream_finish( + ptr, + ); + }, + async close() { + if (!ptr || ptr === null) { + return; + } + await window.WTLIB.symbols.proc_sendstream_finish( + ptr, + ); + }, + }); + } +} diff --git a/mod/symbols.ts b/mod/symbols.ts new file mode 100644 index 0000000..aa0b1c6 --- /dev/null +++ b/mod/symbols.ts @@ -0,0 +1,168 @@ +export const symbols = { + // Server symbols + proc_server_init: { + parameters: [ + // "function", //Callback + "u16", + "bool", + "u64", + "u64", + "buffer", + "usize", + "buffer", + "usize", //KeyLen + ], + result: "pointer", + callback: false, + }, + proc_server_listen: { + parameters: ["pointer", "function"], + result: "pointer", + callback: true, + nonblocking: true, + }, + proc_server_close: { + parameters: ["pointer"], + result: "usize", + }, + // Client symbols + proc_client_init: { + parameters: [ + // "function", //Event Callback + "u64", + "u64", //MaxTimeout + ], + result: "pointer", + callback: true, + }, + proc_client_connect: { + parameters: [ + "pointer", + // "function", //Connection Callback + "buffer", + "usize", //HostLen + ], + result: "pointer", + // callback: true, + }, + proc_client_close: { + parameters: ["pointer", "pointer"], + result: "void", + }, + // Shared symbols + proc_recv_datagram: { + parameters: ["pointer", "buffer"], + result: "usize", + nonblocking: true, + }, + proc_send_datagram: { + parameters: ["pointer", "buffer", "usize"], + result: "void", + nonblocking: false, + }, + proc_accept_bi: { + parameters: [ + "pointer", + "function", //Event Callback + ], + result: "pointer", + nonblocking: true, + }, + proc_open_bi: { + parameters: [ + "pointer", + "function", //Event Callback + ], + result: "pointer", + nonblocking: true, + }, + + proc_accept_uni: { + parameters: [ + "pointer", + "function", //Event Callback + ], + result: "pointer", + nonblocking: true, + }, + proc_open_uni: { + parameters: [ + "pointer", + "function", //Event Callback + ], + result: "pointer", + nonblocking: true, + }, + + proc_read: { + parameters: ["pointer", "buffer", "u32"], + result: "usize", + nonblocking: true, + }, + proc_write: { + parameters: ["pointer", "buffer", "u32"], + result: "usize", + nonblocking: true, + }, + proc_write_all: { + parameters: ["pointer", "buffer", "u32"], + result: "usize", + nonblocking: true, + }, + proc_recvstream_id: { + parameters: ["pointer"], + result: "u64", + }, + proc_sendstream_id: { + parameters: ["pointer"], + result: "u64", + }, + proc_sendstream_priority: { + parameters: ["pointer"], + result: "u64", + }, + proc_sendstream_set_priority: { + parameters: ["pointer", "u64"], + result: "u64", + }, + proc_sendstream_finish: { + parameters: ["pointer"], + result: "void", + nonblocking: true, + }, + proc_recvtream_stop: { + parameters: ["pointer"], + result: "void", + nonblocking: true, + }, + // Crypto symbols + proc_gencert: { + parameters: [ + "buffer", + "usize", + "i64", + "i64", + "buffer", + "buffer", + "buffer", + "buffer", + ], + result: "bool", + }, + free_server: { + parameters: ["pointer"], + result: "void", + }, + free_conn: { + parameters: ["pointer"], + result: "void", + }, + free_all_client: { + parameters: ["pointer", "pointer"], + result: "void", + }, + free_client: { + parameters: ["pointer"], + result: "void", + }, +} as const; diff --git a/mod/utils.ts b/mod/utils.ts index 04e0b70..a002fea 100644 --- a/mod/utils.ts +++ b/mod/utils.ts @@ -8,8 +8,9 @@ export function encodeBuf(v: string | Uint8Array): [Uint8Array, number] { const encoded = encoder.encode(v); return [encoded, encoded.byteLength]; } - +export const decoder = new TextDecoder(); export default { + decoder, encoder, encodeBuf, }; diff --git a/src/client.rs b/src/client.rs index bce4b86..79d56d4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,6 @@ use crate::{ - connection::{self, Client, Conn}, - CLIENT_CONN_FN, RUNTIME, + connection::{Client, Conn}, + RUNTIME, }; use std::{io::Error, time::Duration}; use wtransport::endpoint::endpoint_side::Client as endClient; @@ -8,16 +8,12 @@ use wtransport::{ClientConfig, Endpoint}; pub struct WebTransportClient { pub client: Option>, - pub conn_cb: Option)>, + // pub conn_cb: Option)>, pub state: Option, - pub sender_cb: extern "C" fn(u32, *mut u8, u32), } impl WebTransportClient { - pub(crate) fn new( - sender_fn: Option, - config: ClientConfig, - ) -> Result { + pub(crate) fn new(config: ClientConfig) -> Result { let _guard = RUNTIME.enter(); let client = match Endpoint::client(config) { @@ -27,42 +23,41 @@ impl WebTransportClient { } }; Ok(Self { - sender_cb: sender_fn.unwrap(), - conn_cb: None, + // conn_cb: None, client: Some(client), state: Some(true), }) } - pub(crate) fn connect(&'static mut self, url: String) { + pub(crate) fn connect(&'static mut self, url: String) -> *mut Conn { RUNTIME.block_on(async move { let client = self.client.as_mut().unwrap(); - let sender_cb = self.sender_cb; + match client.connect(url).await { Ok(conn) => { let client = Conn::::new(conn); - let client_ptr = Box::into_raw(Box::new(client)); - unsafe { - CLIENT_CONN_FN.unwrap()(client_ptr); - } + return Box::into_raw(Box::new(client)); } - Err(err) => { - println!("DBG: Error connecting to server. Err: {}", err.to_string()); - let mut msg = err.to_string(); - sender_cb(141, msg.as_mut_ptr(), msg.len() as u32); + Err(_err) => { + //return null ptr + return std::ptr::null_mut(); + // println!("DBG: Error connecting to server. Err: {}", err.to_string()); + // let mut msg = err.to_string(); + // errocb(141, msg.as_mut_ptr(), msg.len() as u32); + //sender_cb(141, msg.as_mut_ptr(), msg.len() as u32); } } - }); + }) } } #[no_mangle] pub extern "C" fn proc_client_init( - send_func: Option, + // send_func: Option, keepalive: u64, timeout: u64, ) -> *mut WebTransportClient { - assert!(!send_func.is_none()); + // assert!(!send_func.is_none()); let keepalive = if keepalive == 0 { None @@ -82,29 +77,23 @@ pub extern "C" fn proc_client_init( .max_idle_timeout(timeout) .unwrap() .build(); - let client = WebTransportClient::new(send_func, config); + let client = WebTransportClient::new(config); match client { Ok(client) => Box::into_raw(Box::new(client)), - Err(error) => { - let message = error.kind(); - let mut msg = message.to_string(); - send_func.unwrap()(10, msg.as_mut_ptr(), msg.len() as u32); - std::ptr::null_mut() - } + Err(_error) => std::ptr::null_mut(), } } #[no_mangle] pub unsafe extern "C" fn proc_client_connect( client: *mut WebTransportClient, - cb: Option)>, + // cb: Option)>, url: *const u8, url_len: usize, ) { let url = ::std::slice::from_raw_parts(url, url_len); let url = std::str::from_utf8(url).unwrap(); let client = &mut *client; - client.conn_cb = cb; - CLIENT_CONN_FN = cb; + // client.conn_cb = cb; client.connect(url.to_string()); } diff --git a/src/connection.rs b/src/connection.rs index ea9067a..678b609 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -94,12 +94,16 @@ impl Conn { Some(reason) => reason, None => b"closed", }; - self.conn - .as_ref() - .unwrap() - .close(VarInt::from_u32(code), reason); - - drop(self.conn.take()) + let conn = self.conn.as_ref(); + match conn { + Some(conn) => { + conn.close(VarInt::from_u32(code), reason); + drop(self.conn.take()) + } + None => { + println!("Connection is None"); + } + } } } diff --git a/src/lib.rs b/src/lib.rs index 4edb308..1b416b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,13 +3,12 @@ use once_cell::sync::Lazy; use tokio::runtime::Runtime; ///------------------------------------ -static mut SEND_FN: Option = None; -static mut SERVER_CONN_FN: Option)> = None; -static mut CLIENT_CONN_FN: Option)> = None; +// static mut SEND_FN: Option = None; + static RUNTIME: Lazy = Lazy::new(|| Runtime::new().unwrap()); ///------------------------------------ mod certificate; -pub mod client; +mod client; mod connection; -pub mod server; +mod server; mod shared; diff --git a/src/server.rs b/src/server.rs index 7d8c179..b537a5d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,6 @@ use crate::{ connection::{self, Conn, Server}, - RUNTIME, SEND_FN, SERVER_CONN_FN, + RUNTIME, }; use std::{path::Path, time::Duration}; use tokio::runtime::Runtime; @@ -13,11 +13,7 @@ pub struct WebTransportServer { } impl WebTransportServer { - pub(crate) unsafe fn new( - sender_fn: Option, - config: ServerConfig, - ) -> Result { - SEND_FN = sender_fn; + pub(crate) unsafe fn new(config: ServerConfig) -> Result { let _guard = RUNTIME.enter(); let server = match Endpoint::server(config) { @@ -75,7 +71,6 @@ impl WebTransportServer { #[no_mangle] pub unsafe extern "C" fn proc_server_init( - send_func: Option, port: u16, migration: bool, keepalive: u64, @@ -85,7 +80,6 @@ pub unsafe extern "C" fn proc_server_init( key_path: *const u8, key_path_len: usize, ) -> *mut WebTransportServer { - assert!(!send_func.is_none()); assert!(port > 0); let cert_path = ::std::slice::from_raw_parts(cert_path, cert_path_len); @@ -114,7 +108,7 @@ pub unsafe extern "C" fn proc_server_init( .unwrap() .allow_migration(migration) .build(); - let server = WebTransportServer::new(send_func, config); + let server = WebTransportServer::new(config); match server { Ok(server) => { let server_ptr = Box::into_raw(Box::new(server)); @@ -129,17 +123,17 @@ pub unsafe extern "C" fn proc_server_init( #[no_mangle] pub unsafe extern "C" fn proc_server_listen( server_ptr: *mut WebTransportServer, - cb: Option)>, + cb: extern "C" fn(*mut Conn), ) { assert!(!server_ptr.is_null()); let server = &mut *server_ptr; - SERVER_CONN_FN = cb; + RUNTIME.spawn(async move { loop { let conn = server.handle_sess_in().await; match conn { Ok(conn) => { - SERVER_CONN_FN.unwrap()(conn); + cb(conn); } Err(e) => { println!("Error accepting sess in : {}", e); diff --git a/src/shared.rs b/src/shared.rs index d1fe005..701ada9 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -1,14 +1,11 @@ use crate::{ connection::{Conn, Server}, - RUNTIME, SEND_FN, + RUNTIME, }; use std::slice::from_raw_parts_mut; use tokio::runtime::Runtime; -use wtransport::{ - error::{ConnectionError, SendDatagramError}, - RecvStream, SendStream, -}; +use wtransport::{error::ConnectionError, RecvStream, SendStream}; //impl a way to identify ConnectionError with a number for JS pub struct ConnectionErrorWrapper(pub ConnectionError); @@ -27,9 +24,9 @@ impl From for u32 { } } -unsafe fn send_error(code: u32, message: String) { +unsafe fn send_error(code: u32, message: String, errorcb: extern "C" fn(u32, *mut u8, u32)) { let mut msg = message; - SEND_FN.unwrap()(code, msg.as_mut_ptr(), msg.len() as u32); + errorcb(code, msg.as_mut_ptr(), msg.len() as u32); } #[repr(C)] @@ -49,6 +46,7 @@ pub unsafe extern "C" fn proc_send_datagram( connptr: *mut Conn, buf: *const u8, buflen: u32, + errorcb: extern "C" fn(u32, *mut u8, u32), ) { assert!(!connptr.is_null()); @@ -58,21 +56,22 @@ pub unsafe extern "C" fn proc_send_datagram( match conn.send_datagram(buf) { Ok(_) => {} Err(err) => { + send_error(200, err.to_string(), errorcb); //TODO: Handle error better - match err { - SendDatagramError::NotConnected => { - println!("DBG: Rust Connection closed"); - SEND_FN.unwrap()(161, vec![0].as_mut_ptr(), 1); - } - SendDatagramError::TooLarge => { - println!("DBG: Rust Too large"); - SEND_FN.unwrap()(162, vec![0].as_mut_ptr(), 1); - } - SendDatagramError::UnsupportedByPeer => { - println!("DBG: Rust not supported by peer"); - SEND_FN.unwrap()(163, vec![0].as_mut_ptr(), 1); - } - }; + // match err { + // SendDatagramError::NotConnected => { + // println!("DBG: Rust Connection closed"); + // SEND_FN.unwrap()(161, vec![0].as_mut_ptr(), 1); + // } + // SendDatagramError::TooLarge => { + // println!("DBG: Rust Too large"); + // SEND_FN.unwrap()(162, vec![0].as_mut_ptr(), 1); + // } + // SendDatagramError::UnsupportedByPeer => { + // println!("DBG: Rust not supported by peer"); + // SEND_FN.unwrap()(163, vec![0].as_mut_ptr(), 1); + // } + // }; } } } @@ -86,7 +85,11 @@ pub unsafe extern "C" fn proc_send_datagram( /// 174 : QuicProto /// 175 : LocallyClosed #[no_mangle] -pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: *mut u8) -> usize { +pub unsafe extern "C" fn proc_recv_datagram( + conn_ptr: *mut Conn, + buff: *mut u8, + errorcb: extern "C" fn(u32, *mut u8, u32), +) -> usize { assert!(!conn_ptr.is_null()); let client = &mut *conn_ptr; @@ -98,7 +101,7 @@ pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: * } Err(error) => { let message = error.to_string(); - send_error(ConnectionErrorWrapper(error).into(), message); + send_error(ConnectionErrorWrapper(error).into(), message, errorcb); 0 } } @@ -108,7 +111,10 @@ pub unsafe extern "C" fn proc_recv_datagram(conn_ptr: *mut Conn, buff: * /// Error Codes : /// 150 : Connection closed #[no_mangle] -pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiStreams { +pub unsafe extern "C" fn proc_open_bi( + connptr: *mut Conn, + errorcb: extern "C" fn(u32, *mut u8, u32), +) -> *mut BidiStreams { assert!(!connptr.is_null()); let _client = &mut *connptr; @@ -123,7 +129,7 @@ pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiSt } Err(error) => { let message = error.to_string(); - send_error(ConnectionErrorWrapper(error).into(), message); + send_error(ConnectionErrorWrapper(error).into(), message, errorcb); std::ptr::null_mut() } } @@ -131,7 +137,10 @@ pub unsafe extern "C" fn proc_open_bi(connptr: *mut Conn) -> *mut BidiSt /// Open a unidirectional stream. #[no_mangle] -pub unsafe extern "C" fn proc_open_uni(connptr: *mut Conn) -> *mut BidiStreams { +pub unsafe extern "C" fn proc_open_uni( + connptr: *mut Conn, + errorcb: extern "C" fn(u32, *mut u8, u32), +) -> *mut BidiStreams { assert!(!connptr.is_null()); let _client = &mut *connptr; @@ -143,7 +152,7 @@ pub unsafe extern "C" fn proc_open_uni(connptr: *mut Conn) -> *mut BidiS })), Err(error) => { let message = error.to_string(); - send_error(ConnectionErrorWrapper(error).into(), message); + send_error(ConnectionErrorWrapper(error).into(), message, errorcb); std::ptr::null_mut() } } @@ -151,7 +160,10 @@ pub unsafe extern "C" fn proc_open_uni(connptr: *mut Conn) -> *mut BidiS /// Accept a unidirectional stream. #[no_mangle] -pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut BidiStreams { +pub unsafe extern "C" fn proc_accept_uni( + connptr: *mut Conn, + errorcb: extern "C" fn(u32, *mut u8, u32), +) -> *mut BidiStreams { assert!(!connptr.is_null()); let _client = &mut *connptr; @@ -163,7 +175,7 @@ pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut Bid })), Err(err) => { let mut msg = err.to_string(); - SEND_FN.unwrap()(160, msg.as_mut_ptr(), msg.len() as u32); + send_error(ConnectionErrorWrapper(err).into(), msg, errorcb); std::ptr::null_mut() } } @@ -171,7 +183,10 @@ pub unsafe extern "C" fn proc_accept_uni(connptr: *mut Conn) -> *mut Bid /// Accept a bidirectional stream. #[no_mangle] -pub unsafe extern "C" fn proc_accept_bi(connptr: *mut Conn) -> *mut BidiStreams { +pub unsafe extern "C" fn proc_accept_bi( + connptr: *mut Conn, + errorcb: extern "C" fn(u32, *mut u8, u32), +) -> *mut BidiStreams { assert!(!connptr.is_null()); let _client = &mut *connptr; @@ -186,7 +201,7 @@ pub unsafe extern "C" fn proc_accept_bi(connptr: *mut Conn) -> *mut Bidi } Err(err) => { let mut msg = err.to_string(); - SEND_FN.unwrap()(155, msg.as_mut_ptr(), msg.len() as u32); + send_error(ConnectionErrorWrapper(err).into(), msg, errorcb); std::ptr::null_mut() } } @@ -198,6 +213,7 @@ pub unsafe extern "C" fn proc_write( stream_ptr: *mut BidiStreams, buf: *const u8, buflen: u32, + errorcb: extern "C" fn(u32, *mut u8, u32), ) -> usize { assert!(!stream_ptr.is_null()); assert!(buflen > 0); @@ -210,7 +226,7 @@ pub unsafe extern "C" fn proc_write( Ok(len) => len, Err(err) => { let mut str = err.to_string(); - SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); + // SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); 0 } } @@ -223,6 +239,7 @@ pub unsafe extern "C" fn proc_write_all( stream_ptr: *mut BidiStreams, buf: *const u8, buflen: u32, + errorcb: extern "C" fn(u32, *mut u8, u32), ) -> u32 { assert!(!stream_ptr.is_null()); assert!(buflen > 0); @@ -234,7 +251,7 @@ pub unsafe extern "C" fn proc_write_all( Ok(_) => buflen, Err(err) => { let mut str = err.to_string(); - SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); + // SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); 0 } } @@ -251,6 +268,7 @@ pub unsafe extern "C" fn proc_read( stream_ptr: *mut BidiStreams, buf: *mut u8, buflen: u32, + errorcb: extern "C" fn(u32, *mut u8, u32), ) -> usize { assert!(!stream_ptr.is_null()); assert!(buflen > 0); @@ -262,7 +280,7 @@ pub unsafe extern "C" fn proc_read( Ok(len) => len, Err(err) => { let mut strs = err.to_string(); - SEND_FN.unwrap()(154, strs.as_mut_ptr(), strs.len() as u32); + // SEND_FN.unwrap()(154, strs.as_mut_ptr(), strs.len() as u32); None } } @@ -291,33 +309,41 @@ pub unsafe extern "C" fn proc_sendstream_id(stream_ptr: *mut BidiStreams) -> u64 /// Close a send stream. #[no_mangle] -pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) { +pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) -> u32 { assert!(!stream_ptr.is_null()); let stream = &mut *stream_ptr; let sendstream = stream.send.as_mut().unwrap(); RUNTIME.block_on(async move { match sendstream.finish().await { - Ok(_) => drop(stream_ptr.as_ref()), + Ok(_) => { + drop(stream_ptr.as_ref()); + 1 + } Err(err) => { let mut msg = err.to_string(); - SEND_FN.unwrap()(150, msg.as_mut_ptr(), msg.len() as u32); + // SEND_FN.unwrap()(150, msg.as_mut_ptr(), msg.len() as u32); + 0 } } - }); + }) } #[no_mangle] -pub unsafe extern "C" fn proc_recvtream_stop(stream_ptr: *mut BidiStreams) { +pub unsafe extern "C" fn proc_recvtream_stop(stream_ptr: *mut BidiStreams) -> u32 { assert!(!stream_ptr.is_null()); let stream = &mut *stream_ptr; RUNTIME.block_on(async move { match stream.recv.as_mut().unwrap().stop(0).await { - Ok(_) => drop(stream_ptr.as_ref()), + Ok(_) => { + // drop(stream_ptr); + 1 + } Err(_) => { - SEND_FN.unwrap()(158, std::ptr::null_mut(), 0); + // SEND_FN.unwrap()(158, std::ptr::null_mut(), 0); + 0 } } - }); + }) } #[no_mangle] pub unsafe extern "C" fn proc_sendstream_priority(stream_ptr: *mut BidiStreams) -> i32 { From f3e8cab6306b6ba075577abd93f75fcbb452f4b4 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 20:57:16 +0200 Subject: [PATCH 58/72] Some fixes --- Cargo.toml | 1 + examples/deno/wt_server_uni_recv.ts | 16 ++----------- src/lib.rs | 1 - src/server.rs | 31 +++++++++++++++++++++++++ src/shared.rs | 35 +++++++++++++++++++---------- 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 07c4cb0..48e5192 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,3 +51,4 @@ ring = "=0.16.20" time = "=0.3.29" anyhow = "=1.0.75" serde = { version = "=1.0", features = ["derive"] } +serde_json = "1.0.107" diff --git a/examples/deno/wt_server_uni_recv.ts b/examples/deno/wt_server_uni_recv.ts index ac60220..be39b6b 100644 --- a/examples/deno/wt_server_uni_recv.ts +++ b/examples/deno/wt_server_uni_recv.ts @@ -30,22 +30,10 @@ server.on("listening", () => { server.on("connection", async (transport) => { console.log("New client"); - setTimeout(async () => { - console.log("Client client after 5 seconds"); - await client.closed; - }, 5000); + const stream = transport.incomingUnidirectionalStreams; const reader = stream.getReader(); - while (true) { - const { value, done } = await reader.read(); - if (done) break; - const DATA = value!.getReader(); - console.log("New incoming stream opened ", value); - const { value: val, done: _ } = await DATA.read(); - console.log(val); - - if (done) break; - } + const first = await reader.read(); }); await server.ready; diff --git a/src/lib.rs b/src/lib.rs index 1b416b0..28c29c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -use connection::{Client, Conn, Server}; use once_cell::sync::Lazy; use tokio::runtime::Runtime; diff --git a/src/server.rs b/src/server.rs index b537a5d..8b35afe 100644 --- a/src/server.rs +++ b/src/server.rs @@ -142,6 +142,37 @@ pub unsafe extern "C" fn proc_server_listen( } }); } +#[no_mangle] +pub extern "C" fn proc_server_client_authority(conn: *mut Conn) -> *const u8 { + assert!(!conn.is_null()); + let conn = unsafe { &mut *conn }; + let authority = conn.authority(); + authority.as_ptr() +} +#[no_mangle] +pub extern "C" fn proc_server_client_headers( + conn: *mut Conn, + buflen: *mut u32, +) -> *const u8 { + assert!(!conn.is_null()); + let conn = unsafe { &mut *conn }; + let mut json = serde_json::to_string(&conn.headers()).unwrap(); + unsafe { + *buflen = json.len() as u32; + } + json.push('\0'); + json.as_ptr() +} + +#[no_mangle] +pub extern "C" fn proc_server_client_path(conn: *mut Conn) -> *const u8 { + assert!(!conn.is_null()); + let conn = unsafe { &mut *conn }; + let path = conn.path(); + let mut path = path.to_string(); + path.push('\0'); + path.as_ptr() +} #[no_mangle] pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) -> usize { diff --git a/src/shared.rs b/src/shared.rs index 701ada9..e798fae 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -174,7 +174,7 @@ pub unsafe extern "C" fn proc_accept_uni( recv: Some(stream), })), Err(err) => { - let mut msg = err.to_string(); + let msg = err.to_string(); send_error(ConnectionErrorWrapper(err).into(), msg, errorcb); std::ptr::null_mut() } @@ -200,7 +200,7 @@ pub unsafe extern "C" fn proc_accept_bi( Box::into_raw(Box::new(bidi)) } Err(err) => { - let mut msg = err.to_string(); + let msg = err.to_string(); send_error(ConnectionErrorWrapper(err).into(), msg, errorcb); std::ptr::null_mut() } @@ -225,7 +225,8 @@ pub unsafe extern "C" fn proc_write( match writer.write(buf).await { Ok(len) => len, Err(err) => { - let mut str = err.to_string(); + let str = err.to_string(); + send_error(153, str, errorcb); // SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); 0 } @@ -250,7 +251,8 @@ pub unsafe extern "C" fn proc_write_all( match writer.write_all(buf).await { Ok(_) => buflen, Err(err) => { - let mut str = err.to_string(); + let str = err.to_string(); + send_error(153, str, errorcb); // SEND_FN.unwrap()(153, str.as_mut_ptr(), str.len() as u32); 0 } @@ -279,8 +281,8 @@ pub unsafe extern "C" fn proc_read( match stream.recv.as_mut().unwrap().read(buf).await { Ok(len) => len, Err(err) => { - let mut strs = err.to_string(); - // SEND_FN.unwrap()(154, strs.as_mut_ptr(), strs.len() as u32); + let strs = err.to_string(); + send_error(154, strs, errorcb); None } } @@ -315,12 +317,9 @@ pub unsafe extern "C" fn proc_sendstream_finish(stream_ptr: *mut BidiStreams) -> let sendstream = stream.send.as_mut().unwrap(); RUNTIME.block_on(async move { match sendstream.finish().await { - Ok(_) => { - drop(stream_ptr.as_ref()); - 1 - } - Err(err) => { - let mut msg = err.to_string(); + Ok(_) => 1, + Err(_err) => { + // let mut msg = err.to_string(); // SEND_FN.unwrap()(150, msg.as_mut_ptr(), msg.len() as u32); 0 } @@ -364,6 +363,18 @@ pub unsafe extern "C" fn proc_sendstream_set_priority( priority } +//add all the methods to get authority, headers, etc +#[no_mangle] +pub unsafe extern "C" fn free_streams(stream_ptr: *mut BidiStreams) { + let _stream = &mut *stream_ptr; + drop(_stream.send.take()); + drop(_stream.recv.take()); +} +#[no_mangle] +pub unsafe extern "C" fn proc_closed(conn: *mut Conn) { + let _conn = &mut *conn; + RUNTIME.block_on(async move { _conn.closed().await }); +} #[no_mangle] pub unsafe extern "C" fn free_conn(_: *mut Conn) {} From bd7fe53cece7c2374a9acada815b351553787abb Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:08:11 +0200 Subject: [PATCH 59/72] More stable fixes --- examples/deno/wt_client_uni_send.ts | 6 +- examples/deno/wt_server_uni_recv.ts | 17 +- examples/deno/wt_server_uni_send.ts | 2 +- mod/client.ts | 265 ++++++++++++++++++---------- mod/connection.ts | 43 +++-- mod/server.ts | 3 +- mod/streams.ts | 158 ++++++++++++----- mod/symbols.ts | 38 +++- src/client.rs | 6 +- src/connection.rs | 14 +- src/server.rs | 19 +- src/shared.rs | 14 +- 12 files changed, 385 insertions(+), 200 deletions(-) diff --git a/examples/deno/wt_client_uni_send.ts b/examples/deno/wt_client_uni_send.ts index 22bbf18..e0d0375 100644 --- a/examples/deno/wt_client_uni_send.ts +++ b/examples/deno/wt_client_uni_send.ts @@ -23,6 +23,6 @@ await writer.write( ); console.log("Stream opened"); -// Deno.serve((req) => { -// return new Response("Hello " + req.url); -// }); +Deno.serve({ port: 9999 }, (req) => { + return new Response("Hello " + req.url); +}); diff --git a/examples/deno/wt_server_uni_recv.ts b/examples/deno/wt_server_uni_recv.ts index be39b6b..1124dc3 100644 --- a/examples/deno/wt_server_uni_recv.ts +++ b/examples/deno/wt_server_uni_recv.ts @@ -18,11 +18,15 @@ try { Deno.exit(1); } Deno.serve(() => new Response("Welcome to Deno 🦕")); +const client = new WebTransport("https://localhost:4433", { + maxTimeout: 5, + keepAlive: 0, +}); const server = new WebTransportServer("https://localhost:4433", { certFile: "./certs/localhost.crt", keyFile: "./certs/localhost.key", maxTimeout: 10, - keepAlive: 3, + keepAlive: 0, }); server.on("listening", () => { console.log("Server listening"); @@ -31,13 +35,16 @@ server.on("listening", () => { server.on("connection", async (transport) => { console.log("New client"); - const stream = transport.incomingUnidirectionalStreams; - const reader = stream.getReader(); - const first = await reader.read(); + const streams = transport.incomingUnidirectionalStreams; + const reader = streams.getReader(); + const firststream = await reader.read(); + const _incoming = firststream.value!; + // for await (const data of incoming) { + // console.log(data); + // } }); await server.ready; //after 5 seconds call closed on client -const client = new WebTransport("https://localhost:4433"); await client.ready; diff --git a/examples/deno/wt_server_uni_send.ts b/examples/deno/wt_server_uni_send.ts index 53c6485..3854c6a 100644 --- a/examples/deno/wt_server_uni_send.ts +++ b/examples/deno/wt_server_uni_send.ts @@ -25,7 +25,7 @@ const server = new WebTransportServer("https://localhost:4433", { keepAlive: 3, }); -server.listen(); +await server.ready; console.log("Server listening"); server.on("connection", async (conn) => { console.log("New client"); diff --git a/mod/client.ts b/mod/client.ts index 9533a84..1c5a0d6 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -1,36 +1,25 @@ if (import.meta.main) { throw new Error("This module is not meant to be imported."); } -import WebTransportConnection from "./connection.ts"; -import { WebTransportDatagramDuplexStream } from "./streams.ts"; +import { WebTransportSendStreamOptions } from "./connection.ts"; +import { + WebTransportBidirectionalStream, + WebTransportDatagramDuplexStream, + WebTransportReceiveStream, + WebTransportSendStream, +} from "./streams.ts"; import { type WebTransportOptions, WebTransportOptions as ServerOpts, } from "./interface.ts"; -import { decoder, encodeBuf } from "./utils.ts"; +import { encodeBuf } from "./utils.ts"; export class WebTransport { - #CONN_PTR: Deno.PointerValue | undefined; - // #NOTIFY_PTR = new Deno.UnsafeCallback( - // { - // parameters: ["u32", "pointer", "u32"], - // result: "void", - // }, - // this.notify.bind(this), - // ); - - // #CONNECTION_CB = new Deno.UnsafeCallback( - // { - // parameters: ["pointer"], - // result: "void", - // }, - // this.connection.bind(this), - // ); - public datagrams!: WebTransportDatagramDuplexStream; - // public readonly ready: Promise; + #CLIENT_PTR: Deno.PointerValue | undefined; - public conn?: WebTransportConnection; + private conn?: Deno.PointerValue; protected remote: URL; + private buffer!: Uint8Array; constructor( _client: URL | string, _options: WebTransportOptions = ServerOpts, @@ -40,79 +29,152 @@ export class WebTransport { _client = new URL(_client); } - this.#CONN_PTR = window.WTLIB.symbols.proc_client_init( + this.#CLIENT_PTR = window.WTLIB.symbols.proc_client_init( _options.keepAlive, _options.maxTimeout, ); - if (!this.#CONN_PTR) { + if (!this.#CLIENT_PTR) { throw new Error("Failed to initialize client"); } - /// Set the current remote to the client this.remote = _client; - - /// ref the callback to prevent it from being garbage collected - // this.#NOTIFY_PTR.ref(); - // this.#CONNECTION_CB.ref(); } - /** - * @callback connection - * @param {Deno.PointerValue} client - * @returns {void} - * @description This function is called when a new connection is received from the server - */ - private connection(client: Deno.PointerValue) { - if (!client || client == null) { - return; - } - const CONN_BUFFER = new Uint8Array(65536); + get incomingBidirectionalStreams() { + const pointer = this.conn; + const errorPTR = this.error.pointer; + return new ReadableStream< + WebTransportBidirectionalStream + >({ + async start(controller) { + try { + if ( + (!pointer || pointer === null) || + (!errorPTR || errorPTR === null) + ) { + controller.close(); + return; + } + const stream = await window.WTLIB.symbols + .proc_accept_bi( + pointer, + errorPTR, + ); + if (!stream || stream === null) { + console.error("[incoming BIDI] Stream not accepted"); + controller.close(); + return; + } + controller.enqueue( + new WebTransportBidirectionalStream( + stream, + errorPTR, + ), + ); + } catch (e) { + controller.error(e); + } + }, + cancel() { + console.info("[incoming BIDI] Cancelled"); + }, + }); + } + get incomingUnidirectionalStreams() { + const pointer = this.conn!; + const errorPTR = this.error.pointer; + return new ReadableStream< + ReadableStream + >({ + async start(controller) { + if ( + (!pointer || pointer === null) || + (!errorPTR || errorPTR === null) + ) { + controller.close(); + return; + } + try { + const stream = await window.WTLIB.symbols + .proc_accept_uni( + pointer, + errorPTR, + ); + if (!stream || stream === null) { + console.error("[incoming UNI] Stream not accepted"); + controller.close(); + return; + } - this.conn = new WebTransportConnection( - client, - CONN_BUFFER, + controller.enqueue(WebTransportReceiveStream.from( + stream, + undefined, + errorPTR, + )); + } catch (e) { + controller.error(e); + } + }, + cancel() { + console.info("[incoming UNI] Cancelled"); + }, + }); + } + get datagrams() { + return new WebTransportDatagramDuplexStream( + this.conn!, + this.buffer, + this.error.pointer, ); + } - this.datagrams = new WebTransportDatagramDuplexStream( + public async createBidirectionalStream( + _options?: WebTransportSendStreamOptions, + ): Promise { + if (!this.conn) throw new Error("Connection is closed"); + if (!this.conn || this.conn === null) { + throw new Error("Connection is closed"); + } + const _streams = await window.WTLIB.symbols.proc_open_bi( this.conn, - CONN_BUFFER, + this.error.pointer, + ); + if (!_streams || _streams === null) { + throw new Error("Failed to create bi stream"); + } + return new WebTransportBidirectionalStream( + _streams, + this.error.pointer, ); - return this.conn; } - /** - * @callback notify - * @param {number} code - * @param {Deno.PointerValue} buffer - * @param {number} buflen - * @returns {void} - * - * @description This function is called when a new event is received from the server - */ - private notify( - _code: unknown | number, - buffer: Deno.PointerValue, - buflen: number, - ) { - const code = _code as number; - console.log("[CB CLIENT] Got code", code); - if (buflen < 0) { - return; + + public async createUnidirectionalStream( + _options?: WebTransportSendStreamOptions, + ): Promise { + if (!this.conn || this.conn === null) { + throw new Error("Connection is closed"); } - const pointer = Deno.UnsafePointerView.getArrayBuffer( - buffer!, - buflen, + //The following operation block the thread until the stream is created. + const _streams = await window.WTLIB.symbols.proc_open_uni( + this.conn, + this.error.pointer, ); - const data = decoder.decode(pointer); - if (code >= 130) { - // Promise.race([this.closed]); - console.error("[CB CLIENT] We should close the connection"); - console.error(data); - // throw new Error(data); + + if (!_streams || _streams === null) { + throw new Error("Failed to create uni stream"); } - const _event = new MessageEvent("error", { - data, - }); - dispatchEvent(_event); + const stream = WebTransportSendStream.from( + _streams, + this.error.pointer, + ); + + return stream; } + error = new Deno.UnsafeCallback({ + parameters: ["u32", "buffer", "u32"], + result: "void", + }, (code, _pointer, _buflen) => { + console.log("CB CALLED : ", code); + }); get closed() { return new Promise(() => { //close the datagrams @@ -121,15 +183,15 @@ export class WebTransport { } //close all the streams - if (this.#CONN_PTR && this.conn) { + if (this.#CLIENT_PTR && this.conn) { window.WTLIB.symbols.proc_client_close( - this.#CONN_PTR, - this.conn!.pointer, + this.#CLIENT_PTR, + this.conn!, ); } - if (this.#CONN_PTR && !this.conn) { + if (this.#CLIENT_PTR && !this.conn) { window.WTLIB.symbols.free_client( - this.#CONN_PTR, + this.#CLIENT_PTR, ); } // this.#NOTIFY_PTR.unref(); @@ -139,21 +201,30 @@ export class WebTransport { }); } get ready() { - return new Promise((resolve, reject) => { - const encoded = encodeBuf(this.remote.href); - const conn = window.WTLIB.symbols.proc_client_connect( - this.#CONN_PTR!, - encoded[0], - encoded[1], - ); - this.connection(conn); - if (this.conn) { - resolve(this.conn); - } else { - this.conn = undefined; - reject("Failed to connect to server"); - } - }); + return new Promise( + (resolve, reject) => { + if (!this.#CLIENT_PTR) { + reject("Client is not initialized"); + return; + } + const encoded = encodeBuf(this.remote.href); + window.WTLIB.symbols.proc_client_connect( + this.#CLIENT_PTR, + encoded[0], + encoded[1], + ).then((conn) => { + if (!conn || conn === null) { + reject("Failed to connect"); + return; + } + this.buffer = new Uint8Array(1024); + this.conn = conn; + resolve(this); + }).catch((e) => { + reject(e); + }); + }, + ); } } diff --git a/mod/connection.ts b/mod/connection.ts index 1255ea1..32bb3c1 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -8,10 +8,10 @@ import { } from "./streams.ts"; /// -interface WebTransportSendStreamOptions { +export interface WebTransportSendStreamOptions { sendOrder?: number | null; } -interface WebTransportCloseInfo { +export interface WebTransportCloseInfo { errorCode?: number; reason?: string; } @@ -24,21 +24,18 @@ export default class WebTransportConnection { | "failed" | "connecting" = "closed" as const; - public readonly datagrams: WebTransportDatagramDuplexStream; - // public readonly incomingBidirectionalStreams: ReadableStream< - // WebTransportBidirectionalStream - // >; - // public readonly incomingUnidirectionalStreams: ReadableStream< - // ReadableStream - // >; - constructor( public readonly pointer: Deno.PointerValue, - buffer: Uint8Array, + private readonly buffer: Uint8Array, ) { this.state = "connected"; - // this.#CONN_PTR = pointer; - this.datagrams = new WebTransportDatagramDuplexStream(this, buffer); + } + get datagrams() { + return new WebTransportDatagramDuplexStream( + this.pointer, + this.buffer, + this.error.pointer, + ); } get incomingBidirectionalStreams() { const pointer = this.pointer; @@ -68,6 +65,7 @@ export default class WebTransportConnection { controller.enqueue( new WebTransportBidirectionalStream( stream, + errorPTR, ), ); } catch (e) { @@ -106,6 +104,8 @@ export default class WebTransportConnection { } controller.enqueue(WebTransportReceiveStream.from( stream, + undefined, + errorPTR, )); } catch (e) { controller.error(e); @@ -130,7 +130,10 @@ export default class WebTransportConnection { if (!_streams || _streams === null) { throw new Error("Failed to create bi stream"); } - return new WebTransportBidirectionalStream(_streams); + return new WebTransportBidirectionalStream( + _streams, + this.error.pointer, + ); } public async createUnidirectionalStream( @@ -148,15 +151,19 @@ export default class WebTransportConnection { if (!_streams || _streams === null) { throw new Error("Failed to create uni stream"); } - const stream = WebTransportSendStream.from(_streams); + const stream = WebTransportSendStream.from( + _streams, + this.error.pointer, + ); return stream; } - private error = new Deno.UnsafeCallback({ - parameters: ["pointer"], + error = new Deno.UnsafeCallback({ + parameters: ["u32", "buffer", "u32"], result: "void", - }, (_pointer) => { + }, (code, _pointer, _buflen) => { + console.log("CB CALLED : ", code); }); close(_closeInfo?: WebTransportCloseInfo) { if (!this.pointer || this.pointer === null) { diff --git a/mod/server.ts b/mod/server.ts index 3256aea..f4e0d63 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -90,7 +90,7 @@ export class WebTransportServer extends EventEmitter { * @description This function is called when a new connection is received from the server */ private connection(client: Deno.PointerValue) { - const SHARED_BUF = new SharedArrayBuffer(65536); + const SHARED_BUF = new ArrayBuffer(65536); const CONN_BUFFER = new Uint8Array(SHARED_BUF); // window.WTLIB.symbols.proc_init_datagrams( // client, @@ -147,6 +147,7 @@ export class WebTransportServer extends EventEmitter { } close() { + console.info("[JS] SERVER CLOSE CALLED"); //free all the connections this.connections.forEach((conn, id) => { if (conn.state != "closed" && this.#SRV_PTR && conn.pointer) { diff --git a/mod/streams.ts b/mod/streams.ts index 9bd9b00..f76d7d3 100644 --- a/mod/streams.ts +++ b/mod/streams.ts @@ -1,5 +1,6 @@ -import type WebTransportConnection from "./connection.ts"; - +/** + * @class WebTransportDatagramDuplexStream + */ export class WebTransportDatagramDuplexStream { #READ_BUFFER?: Uint8Array; readonly incomingHighWaterMark = 1; @@ -8,32 +9,29 @@ export class WebTransportDatagramDuplexStream { readonly outgoingHighWaterMark = 1; readonly outgoingMaxAge = 0; constructor( - private connection: WebTransportConnection, - _buffer: Uint8Array, + private readonly connection: Deno.PointerValue, + readonly _buffer: Uint8Array, + private readonly cb: Deno.PointerValue, ) { this.#READ_BUFFER = _buffer; } get writable() { const connection = this.connection; + const error = this.cb; return new WritableStream({ start(controller) { - if (!connection.pointer) { + if (!connection || connection === null) { controller.error("Connection is closed"); return; } }, - write(chunk: Uint8Array, controller) { - try { - window.WTLIB.symbols.proc_send_datagram( - connection.pointer!, - chunk, - chunk.byteLength, - ); - return; - } catch (e) { - controller.error(e); - return; - } + write(chunk: Uint8Array) { + window.WTLIB.symbols.proc_send_datagram( + connection!, + chunk, + chunk.byteLength, + error, + ); }, abort(e) { console.error("[Error] Write aborted", e); @@ -46,45 +44,63 @@ export class WebTransportDatagramDuplexStream { get readable() { const connection = this.connection; const buffer = this.#READ_BUFFER ?? new Uint8Array(1024); - const StreamBuffer = new ReadableStream({ - async pull(controller) { - try { - const nread = await window.WTLIB.symbols.proc_recv_datagram( - connection.pointer!, - buffer, - ); - if (nread > 0) { - controller.enqueue( - buffer.subarray(0, nread as number), - ); - } - } catch (e) { - controller.error(e); + const cb = this.cb; + // const DEFAULT_CHUNK_SIZE = this.maxDatagramSize; + return new ReadableStream({ + type: "bytes", + async start(controller) { + if (!connection || connection === null) { + controller.error("Connection is closed"); + return; } + const nread = await window.WTLIB.symbols.proc_recv_datagram( + connection, + buffer, + cb, + ) as number; + if (nread <= 0) { + return; + } + controller.enqueue( + buffer.subarray(0, nread as number), + ); }, cancel() { }, }); - return StreamBuffer; } close() { + console.log("[Info] Closing datagram duplex stream"); this.#READ_BUFFER = undefined; this.readable.cancel(); this.writable.close(); } } - +/** + * @class WebTransportBidirectionalStream + * @description This class is used to read and write data from a bidirectional stream + */ export class WebTransportBidirectionalStream { readonly readable: ReadableStream; readonly writable: WritableStream; constructor( - public ptr: Deno.PointerValue, + public readonly ptr: Deno.PointerValue, + private readonly errorCB: Deno.PointerValue, ) { - this.readable = WebTransportReceiveStream.from(this.ptr); - this.writable = WebTransportSendStream.from(this.ptr); + this.readable = WebTransportReceiveStream.from( + this.ptr, + undefined, + errorCB, + ); + this.writable = WebTransportSendStream.from(this.ptr, errorCB); } } + +/** + * @class WebTransportReceiveStream + * @description This class is used to read data from a stream (uni or bidirectional) + */ export class WebTransportReceiveStream { private static readonly ptr: Deno.PointerValue; constructor(private readonly ptr: Deno.PointerValue) { @@ -92,19 +108,16 @@ export class WebTransportReceiveStream { static from( ptr: Deno.PointerValue, DEFAULT_CHUNK_SIZE = 1024, + cb: Deno.PointerValue, ) { return new ReadableStream({ type: "bytes", - start( - _, - ) { - }, - pull(controller) { + start(controller) { readRepeatedly().catch((e) => controller.error(e)); async function readRepeatedly() { - if (!ptr || ptr === null) { - throw new Error("Stream is closed"); - } + // return socket.select2().then(() => { + // Since the socket can become readable even when there's + // no pending BYOB requests, we need to handle both cases. let bytesRead; if (controller.byobRequest) { const v = controller.byobRequest.view; @@ -112,18 +125,20 @@ export class WebTransportReceiveStream { ptr, v?.buffer!, v?.byteLength!, + cb, ); if (bytesRead === 0) { - console.log("BYOB REQUEST"); controller.close(); } controller.byobRequest.respond(bytesRead as number); + console.log(`byobRequest with ${bytesRead} bytes`); } else { const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); bytesRead = await window.WTLIB.symbols.proc_read( ptr, buffer, DEFAULT_CHUNK_SIZE, + cb, ); if (bytesRead === 0) { controller.close(); @@ -132,15 +147,62 @@ export class WebTransportReceiveStream { new Uint8Array(buffer, 0, bytesRead as number), ); } + console.log( + `enqueue() ${bytesRead} bytes (no byobRequest)`, + ); } + if (bytesRead === 0) { return; + // no more bytes in source } return readRepeatedly(); + // }); } }, + // pull(controller) { + // readRepeatedly().catch((e) => controller.error(e)); + // async function readRepeatedly() { + // if (!ptr || ptr === null) { + // throw new Error("Stream is closed"); + // } + // let bytesRead; + // if (controller.byobRequest) { + // const v = controller.byobRequest.view; + // bytesRead = await window.WTLIB.symbols.proc_read( + // ptr, + // v?.buffer!, + // v?.byteLength!, + // ); + // if (bytesRead === 0) { + // console.log("BYOB REQUEST"); + // controller.close(); + // } + // controller.byobRequest.respond(bytesRead as number); + // } else { + // const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); + // bytesRead = await window.WTLIB.symbols.proc_read( + // ptr, + // buffer, + // DEFAULT_CHUNK_SIZE, + // ); + // if (bytesRead === 0) { + // controller.close(); + // } else { + // controller.enqueue( + // new Uint8Array(buffer, 0, bytesRead as number), + // ); + // } + // } + // if (bytesRead === 0) { + // return; + // } + // return readRepeatedly(); + // } + // }, async cancel(reason?: string): Promise { if (!ptr || ptr === null) { + console.debug("Stream is closed"); return; } console.error("Canceled: ", reason); @@ -154,9 +216,14 @@ export class WebTransportReceiveStream { } } +/** + * @class WebTransportSendStream + * @description This class is used to write data to a stream (uni or bidirectional) + */ export class WebTransportSendStream { static from( ptr: Deno.PointerValue, + cb: Deno.PointerValue, ) { return new WritableStream({ async write( @@ -173,6 +240,7 @@ export class WebTransportSendStream { ptr, chunk, chunk.byteLength, + cb, ) as number; if (written === 0) { controller.error("Write failed"); diff --git a/mod/symbols.ts b/mod/symbols.ts index aa0b1c6..34305fa 100644 --- a/mod/symbols.ts +++ b/mod/symbols.ts @@ -24,6 +24,19 @@ export const symbols = { proc_server_close: { parameters: ["pointer"], result: "usize", + nonblocking: true, + }, + proc_server_client_headers: { + parameters: ["pointer", "buffer"], + result: "pointer", + }, + proc_server_client_path: { + parameters: ["pointer", "buffer"], + result: "pointer", + }, + proc_server_client_authority: { + parameters: ["pointer", "buffer"], + result: "pointer", }, // Client symbols proc_client_init: { @@ -33,17 +46,15 @@ export const symbols = { "u64", //MaxTimeout ], result: "pointer", - callback: true, }, proc_client_connect: { parameters: [ "pointer", - // "function", //Connection Callback "buffer", "usize", //HostLen ], result: "pointer", - // callback: true, + nonblocking: true, }, proc_client_close: { parameters: ["pointer", "pointer"], @@ -51,14 +62,16 @@ export const symbols = { }, // Shared symbols proc_recv_datagram: { - parameters: ["pointer", "buffer"], + parameters: ["pointer", "buffer", "function"], result: "usize", nonblocking: true, + callback: true, }, proc_send_datagram: { - parameters: ["pointer", "buffer", "usize"], + parameters: ["pointer", "buffer", "usize", "function"], result: "void", - nonblocking: false, + nonblocking: true, + callback: true, }, proc_accept_bi: { parameters: [ @@ -67,6 +80,7 @@ export const symbols = { ], result: "pointer", nonblocking: true, + callback: true, }, proc_open_bi: { parameters: [ @@ -75,6 +89,7 @@ export const symbols = { ], result: "pointer", nonblocking: true, + callback: true, }, proc_accept_uni: { @@ -84,6 +99,7 @@ export const symbols = { ], result: "pointer", nonblocking: true, + callback: true, }, proc_open_uni: { parameters: [ @@ -92,22 +108,26 @@ export const symbols = { ], result: "pointer", nonblocking: true, + callback: true, }, proc_read: { - parameters: ["pointer", "buffer", "u32"], + parameters: ["pointer", "buffer", "usize", "function"], result: "usize", nonblocking: true, + callback: true, }, proc_write: { - parameters: ["pointer", "buffer", "u32"], + parameters: ["pointer", "buffer", "usize", "function"], result: "usize", nonblocking: true, + callback: true, }, proc_write_all: { - parameters: ["pointer", "buffer", "u32"], + parameters: ["pointer", "buffer", "usize", "function"], result: "usize", nonblocking: true, + callback: true, }, proc_recvstream_id: { parameters: ["pointer"], diff --git a/src/client.rs b/src/client.rs index 79d56d4..f250366 100644 --- a/src/client.rs +++ b/src/client.rs @@ -86,14 +86,13 @@ pub extern "C" fn proc_client_init( #[no_mangle] pub unsafe extern "C" fn proc_client_connect( client: *mut WebTransportClient, - // cb: Option)>, url: *const u8, url_len: usize, ) { let url = ::std::slice::from_raw_parts(url, url_len); let url = std::str::from_utf8(url).unwrap(); let client = &mut *client; - // client.conn_cb = cb; + client.connect(url.to_string()); } @@ -104,11 +103,10 @@ pub unsafe extern "C" fn proc_client_close( ) -> usize { assert!(!client_ptr.is_null()); assert!(!conn.is_null()); - println!("CALLED"); + println!("CLIENT CLOSE CALLED"); let client = &mut *client_ptr; let conn = &mut *conn; client.state = Some(false); - // RUNTIME.block_on(async move { conn.close(32, Some(b"NO")) }); conn.close(32, Some(b"NO")); 0 } diff --git a/src/connection.rs b/src/connection.rs index 678b609..09f32bc 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -96,13 +96,8 @@ impl Conn { }; let conn = self.conn.as_ref(); match conn { - Some(conn) => { - conn.close(VarInt::from_u32(code), reason); - drop(self.conn.take()) - } - None => { - println!("Connection is None"); - } + Some(conn) => conn.close(VarInt::from_u32(code), reason), + None => println!("Connection is None"), } } } @@ -116,19 +111,24 @@ impl Conn { _marker: PhantomData, } } + pub fn accepted(&mut self, conn: Connection) { self.conn = Some(conn); } + pub async fn accept(&mut self) -> Result { let accepted_session = self.accepted_session.take().unwrap(); accepted_session.accept().await } + pub fn path(&self) -> &str { self.accepted_session.as_ref().unwrap().path() } + pub fn authority(&self) -> &str { self.accepted_session.as_ref().unwrap().authority() } + pub fn headers(&self) -> &HashMap { self.accepted_session.as_ref().unwrap().headers() } diff --git a/src/server.rs b/src/server.rs index 8b35afe..b340e41 100644 --- a/src/server.rs +++ b/src/server.rs @@ -143,12 +143,19 @@ pub unsafe extern "C" fn proc_server_listen( }); } #[no_mangle] -pub extern "C" fn proc_server_client_authority(conn: *mut Conn) -> *const u8 { +pub extern "C" fn proc_server_client_authority( + conn: *mut Conn, + buflen: *mut u32, +) -> *const u8 { assert!(!conn.is_null()); let conn = unsafe { &mut *conn }; let authority = conn.authority(); + unsafe { + *buflen = authority.len() as u32; + } authority.as_ptr() } + #[no_mangle] pub extern "C" fn proc_server_client_headers( conn: *mut Conn, @@ -165,11 +172,17 @@ pub extern "C" fn proc_server_client_headers( } #[no_mangle] -pub extern "C" fn proc_server_client_path(conn: *mut Conn) -> *const u8 { +pub extern "C" fn proc_server_client_path( + conn: *mut Conn, + buflen: *mut u32, +) -> *const u8 { assert!(!conn.is_null()); let conn = unsafe { &mut *conn }; let path = conn.path(); let mut path = path.to_string(); + unsafe { + *buflen = path.len() as u32; + } path.push('\0'); path.as_ptr() } @@ -177,7 +190,7 @@ pub extern "C" fn proc_server_client_path(conn: *mut Conn) - #[no_mangle] pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) -> usize { assert!(!server_ptr.is_null()); - + println!("SERVER CLOSE CALLED"); let server = &mut *server_ptr; server.state = Some(false); let endpoint = server.server.as_mut(); diff --git a/src/shared.rs b/src/shared.rs index e798fae..764e7ee 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -212,14 +212,14 @@ pub unsafe extern "C" fn proc_accept_bi( pub unsafe extern "C" fn proc_write( stream_ptr: *mut BidiStreams, buf: *const u8, - buflen: u32, + buflen: usize, errorcb: extern "C" fn(u32, *mut u8, u32), ) -> usize { assert!(!stream_ptr.is_null()); assert!(buflen > 0); let bidi_streams = &mut *stream_ptr; - let buf = ::std::slice::from_raw_parts(buf, buflen as usize); + let buf = ::std::slice::from_raw_parts(buf, buflen); let writer = bidi_streams.send.as_mut().unwrap(); let writenlen = RUNTIME.block_on(async move { match writer.write(buf).await { @@ -239,13 +239,13 @@ pub unsafe extern "C" fn proc_write( pub unsafe extern "C" fn proc_write_all( stream_ptr: *mut BidiStreams, buf: *const u8, - buflen: u32, + buflen: usize, errorcb: extern "C" fn(u32, *mut u8, u32), -) -> u32 { +) -> usize { assert!(!stream_ptr.is_null()); assert!(buflen > 0); let stream = &mut *stream_ptr; - let buf = ::std::slice::from_raw_parts(buf, buflen as usize); + let buf = ::std::slice::from_raw_parts(buf, buflen); let writer = stream.send.as_mut().unwrap(); let writenlen = RUNTIME.block_on(async move { match writer.write_all(buf).await { @@ -269,14 +269,14 @@ pub unsafe extern "C" fn proc_write_all( pub unsafe extern "C" fn proc_read( stream_ptr: *mut BidiStreams, buf: *mut u8, - buflen: u32, + buflen: usize, errorcb: extern "C" fn(u32, *mut u8, u32), ) -> usize { assert!(!stream_ptr.is_null()); assert!(buflen > 0); let stream = &mut *stream_ptr; - let buf = ::std::slice::from_raw_parts_mut(buf, buflen as usize); + let buf = ::std::slice::from_raw_parts_mut(buf, buflen); let readlen = RUNTIME.block_on(async move { match stream.recv.as_mut().unwrap().read(buf).await { Ok(len) => len, From c3414fbbfdf59acb97dcde71a61e1ff31805769c Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:10:14 +0200 Subject: [PATCH 60/72] Example fixes --- examples/deno/wt_server_uni_send.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/deno/wt_server_uni_send.ts b/examples/deno/wt_server_uni_send.ts index 3854c6a..e31b785 100644 --- a/examples/deno/wt_server_uni_send.ts +++ b/examples/deno/wt_server_uni_send.ts @@ -30,7 +30,7 @@ console.log("Server listening"); server.on("connection", async (conn) => { console.log("New client"); const sendStream = await conn.createUnidirectionalStream(); - console.log("Send Stream open"); + console.log("Stream open"); const writer = sendStream.getWriter(); // //wait 5 seeconds and send 2 message every 2 secondes // await new Promise((resolve) => setTimeout(resolve, 2000)); From 5cc2031c0667837f364dd18d5eaeb1257ea7f23b Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:30:25 +0200 Subject: [PATCH 61/72] fix tests --- deno.jsonc | 2 +- examples/deno/wt_server_test.ts | 6 ++- mod/server.ts | 69 ++++++--------------------------- mod/symbols.ts | 2 +- src/server.rs | 4 +- 5 files changed, 20 insertions(+), 63 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index 9dac27d..c29a759 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -20,7 +20,7 @@ "util:fmt": "deno fmt --unstable", "util:lint": "deno lint --unstable", //Tests - "test": "deno test -A --unstable ", + "test": "deno task demo:gencert && deno test -A --unstable ", //Non CI build "build": "cargo build" }, diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 0b7855f..ae58270 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -5,12 +5,13 @@ import { assert } from "https://deno.land/std@0.202.0/assert/mod.ts"; import { WebTransportServer } from "../../mod/server.ts"; // //add certs cleanup methods after tests // const certPath = join(Deno.cwd(), "./certs/"); -async function _sleep(msec: number) { +async function sleep(msec: number) { await new Promise((res, _rej) => setTimeout(res, msec)); } -Deno.test({ name: "Server startup/close" }, () => { +Deno.test({ name: "Server startup/close" }, async () => { //generate a certificate + sleep(2); const [cert, key] = GenerateCertKeyFile( "localhost", 0, @@ -22,6 +23,7 @@ Deno.test({ name: "Server startup/close" }, () => { maxTimeout: 10, keepAlive: 3, }); + await server.ready; server.close(); //try to start a UDP socket on the same port to see if it's closed const sock = Deno.listenDatagram({ diff --git a/mod/server.ts b/mod/server.ts index f4e0d63..a0b64d6 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -24,14 +24,6 @@ export class WebTransportServer extends EventEmitter { public connections: Map = new Map(); #SRV_PTR: Deno.PointerValue | undefined; - #STATE_PTR = new Uint32Array(1); - #NOTIFY_PTR = new Deno.UnsafeCallback( - { - parameters: ["u32", "pointer", "u32"], - result: "void", - }, - this.notify.bind(this), - ); #CONNECTION_CB = new Deno.UnsafeCallback( { parameters: ["pointer"], @@ -79,7 +71,6 @@ export class WebTransportServer extends EventEmitter { throw new Error("Failed to initialize server"); } - // this.#NOTIFY_PTR.ref(); this.#CONNECTION_CB.ref(); this.emit("listening", new Event("listening")); } @@ -92,12 +83,7 @@ export class WebTransportServer extends EventEmitter { private connection(client: Deno.PointerValue) { const SHARED_BUF = new ArrayBuffer(65536); const CONN_BUFFER = new Uint8Array(SHARED_BUF); - // window.WTLIB.symbols.proc_init_datagrams( - // client, - // CONN_BUFFER, - // CONN_BUFFER.byteLength, - // ); - //Setting up the stream for the new connection + const conn = new WebTransportConnection( client, CONN_BUFFER, @@ -109,44 +95,8 @@ export class WebTransportServer extends EventEmitter { ); this.emit("connection", conn); } - /** - * @callback notify - * @param {number} code - * @param {Deno.PointerValue} buffer - * @param {number} buflen - * @returns {void} - * - * @description This function is called when a new event is received from the server - */ - private notify( - _code: unknown | number, - buffer: Deno.PointerValue, - buflen: number, - ) { - //TODO(hironichu): This callback should implement more arguments to get an optional pointer to the connection its referring to - const code = _code as number; - - console.info("[CB SERVER] Got code : ", code); - if (buflen > 1) { - const pointer = Deno.UnsafePointerView.getArrayBuffer( - buffer as unknown as NonNullable, - buflen, - ); - const event = new MessageEvent("message", { - data: decoder.decode(pointer), - }); - console.log("Event data : ", event.data); - this.emit("event", event); - } - if (code >= 130) { - // Promise.race([this.closed]); - console.error("[CB SERVER] We should close the connection"); - // throw new Error(data); - } - } - - close() { + async close() { console.info("[JS] SERVER CLOSE CALLED"); //free all the connections this.connections.forEach((conn, id) => { @@ -161,10 +111,9 @@ export class WebTransportServer extends EventEmitter { this.connections.delete(id); }); if (this.#SRV_PTR) { - window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); + await window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); } - this.#NOTIFY_PTR.close(); - this.#CONNECTION_CB.close(); + this.#SRV_PTR = undefined; console.info("[SERVER] Server closed"); } @@ -198,8 +147,14 @@ export class WebTransportServer extends EventEmitter { "Invalid certificate or key file path (empty string)", ); } - Deno.statSync(_options.certFile); - Deno.statSync(_options.keyFile); + try { + Deno.statSync(_options.certFile); + Deno.statSync(_options.keyFile); + } catch { + throw new TypeError( + "Invalid certificate or key file path", + ); + } // certificate = _options.certFile; key = _options.keyFile; diff --git a/mod/symbols.ts b/mod/symbols.ts index 34305fa..db813f1 100644 --- a/mod/symbols.ts +++ b/mod/symbols.ts @@ -19,7 +19,7 @@ export const symbols = { parameters: ["pointer", "function"], result: "pointer", callback: true, - nonblocking: true, + nonblocking: false, }, proc_server_close: { parameters: ["pointer"], diff --git a/src/server.rs b/src/server.rs index b340e41..815f4df 100644 --- a/src/server.rs +++ b/src/server.rs @@ -23,6 +23,7 @@ impl WebTransportServer { return Err(1); } }; + Ok(Self { server: Some(server), state: Some(true), @@ -130,8 +131,7 @@ pub unsafe extern "C" fn proc_server_listen( RUNTIME.spawn(async move { loop { - let conn = server.handle_sess_in().await; - match conn { + match server.handle_sess_in().await { Ok(conn) => { cb(conn); } From 1c5c076fff2a635e1e9e36e4a6a6d085923b562e Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:30:58 +0200 Subject: [PATCH 62/72] lint --- mod/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/server.ts b/mod/server.ts index a0b64d6..b6e8ad5 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -8,7 +8,7 @@ import { type WebTransportServerOptions, WebTransportServerOptions as ServerOpts, } from "./interface.ts"; -import { decoder, encodeBuf } from "./utils.ts"; +import { encodeBuf } from "./utils.ts"; export type WebTransportServerEvents = { listening: [Event]; From 569267825785e6503faa438cab86922fca74d482 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:35:01 +0200 Subject: [PATCH 63/72] remove certificate generation in tests --- examples/deno/wt_server_test.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index ae58270..35e4209 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -12,14 +12,10 @@ async function sleep(msec: number) { Deno.test({ name: "Server startup/close" }, async () => { //generate a certificate sleep(2); - const [cert, key] = GenerateCertKeyFile( - "localhost", - 0, - 10, - ); + const server = new WebTransportServer("https://localhost:4433", { - certFile: cert, - keyFile: key, + certFile: "./certs/localhost.crt", + keyFile: "./certs/localhost.key", maxTimeout: 10, keepAlive: 3, }); From 16bea161c8fac1260b46ef5efbd2f504a8beaec4 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:37:32 +0200 Subject: [PATCH 64/72] fix --- mod/crypto.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mod/crypto.ts b/mod/crypto.ts index 6b2f63d..a7751f7 100644 --- a/mod/crypto.ts +++ b/mod/crypto.ts @@ -91,10 +91,12 @@ export function GenerateCertKeyFile( const keypath = join(path, `${keyFileName ?? domainStr + ".key"}`); try { Deno.writeFileSync(certpath, cert, { - mode: 0o444, + mode: 0o766, + create: true, }); Deno.writeFileSync(keypath, key, { - mode: 0o444, + mode: 0o766, + create: true, }); return [certpath, keypath]; } catch (e) { From b72c82d531128b2f4c08857f813eedc569037c2c Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:38:05 +0200 Subject: [PATCH 65/72] fmt --- examples/deno/wt_server_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 35e4209..cba8a91 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1,4 +1,4 @@ -import { GenerateCertKeyFile } from "../../mod/crypto.ts"; +// import { GenerateCertKeyFile } from "../../mod/crypto.ts"; import "../../mod/mod.ts"; import { assert } from "https://deno.land/std@0.202.0/assert/mod.ts"; From f8906e5f8f5fb5e88c5452a2d29d8534a300af54 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:41:12 +0200 Subject: [PATCH 66/72] Fix Server panic when already listening (Return null instead) --- src/server.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/server.rs b/src/server.rs index 815f4df..99c87ac 100644 --- a/src/server.rs +++ b/src/server.rs @@ -110,14 +110,10 @@ pub unsafe extern "C" fn proc_server_init( .allow_migration(migration) .build(); let server = WebTransportServer::new(config); + match server { - Ok(server) => { - let server_ptr = Box::into_raw(Box::new(server)); - server_ptr - } - Err(_) => { - panic!("Error creating server") - } + Ok(server) => Box::into_raw(Box::new(server)), + Err(_) => std::ptr::null_mut(), } } From 67025fc7a3002e520aa08e914119699ad1d1db2e Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:44:01 +0200 Subject: [PATCH 67/72] Add more linux safety when listening --- examples/deno/wt_server_test.ts | 2 +- mod/server.ts | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index cba8a91..27a8070 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -20,7 +20,7 @@ Deno.test({ name: "Server startup/close" }, async () => { keepAlive: 3, }); await server.ready; - server.close(); + await server.close(); //try to start a UDP socket on the same port to see if it's closed const sock = Deno.listenDatagram({ hostname: "0.0.0.0", diff --git a/mod/server.ts b/mod/server.ts index b6e8ad5..8e3dc92 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -118,11 +118,17 @@ export class WebTransportServer extends EventEmitter { console.info("[SERVER] Server closed"); } get ready() { - console.info("[SERVER] Server ready"); - return window.WTLIB.symbols.proc_server_listen( - this.#SRV_PTR!, - this.#CONNECTION_CB.pointer, - ); + return new Promise((resolve, reject) => { + console.info("[SERVER] Server ready"); + const rest = window.WTLIB.symbols.proc_server_listen( + this.#SRV_PTR!, + this.#CONNECTION_CB.pointer, + ); + if (!rest) { + reject("Failed to listen"); + } + resolve(this); + }); } private checkArgs(_options: WebTransportServerOptions) { if ( From d858f569db937ccdb6ba99aea869158b196ee7bd Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Sun, 24 Sep 2023 23:51:07 +0200 Subject: [PATCH 68/72] fix tests --- examples/deno/wt_client_test.ts | 58 ++++++++++++++++----------------- mod/client.ts | 1 - mod/connection.ts | 1 - mod/server.ts | 3 +- mod/symbols.ts | 2 +- src/client.rs | 8 +---- 6 files changed, 32 insertions(+), 41 deletions(-) diff --git a/examples/deno/wt_client_test.ts b/examples/deno/wt_client_test.ts index 245e40c..a5c961c 100644 --- a/examples/deno/wt_client_test.ts +++ b/examples/deno/wt_client_test.ts @@ -28,33 +28,33 @@ import "../../mod/mod.ts"; // await browser.close(); // }, // ); -Deno.test( - { - name: "Client connect/close (unsafe)", - sanitizeOps: false, - sanitizeResources: false, - }, - async () => { - //THis causes panic??????? - const server = new WebTransportServer("https://localhost:4433", { - certFile: "./certs/localhost.crt", - keyFile: "./certs/localhost.key", - maxTimeout: 10, - keepAlive: 3, - }); - await server.ready; +// Deno.test( +// { +// name: "Client connect/close (unsafe)", +// sanitizeOps: false, +// sanitizeResources: false, +// }, +// async () => { +// //THis causes panic??????? +// const server = new WebTransportServer("https://localhost:4433", { +// certFile: "./certs/localhost.crt", +// keyFile: "./certs/localhost.key", +// maxTimeout: 10, +// keepAlive: 3, +// }); +// await server.ready; +// server.on("connection", (client) => { +// setTimeout(async () => { +// client.close(); +// await server.close(); +// }, 2000); +// }); +// const client = new WebTransport("https://localhost:4433", { +// maxTimeout: 50, +// keepAlive: 3, +// }); +// await client.ready; - const client = new WebTransport("https://localhost:4433", { - maxTimeout: 50, - keepAlive: 3, - }); - await client.ready; - server.on("connection", (_) => { - setTimeout(async () => { - await client.closed; - }, 2000); - }); - console.log("Closing"); - server.close(); - }, -); +// console.log("Closing"); +// }, +// ); diff --git a/mod/client.ts b/mod/client.ts index 1c5a0d6..a93a2b4 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -185,7 +185,6 @@ export class WebTransport { if (this.#CLIENT_PTR && this.conn) { window.WTLIB.symbols.proc_client_close( - this.#CLIENT_PTR, this.conn!, ); } diff --git a/mod/connection.ts b/mod/connection.ts index 32bb3c1..9510bc9 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -173,7 +173,6 @@ export default class WebTransportConnection { this.state = "closed"; window.WTLIB.symbols.proc_client_close( this.pointer, - this.pointer, ); } } diff --git a/mod/server.ts b/mod/server.ts index 8e3dc92..7ea828f 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -102,7 +102,6 @@ export class WebTransportServer extends EventEmitter { this.connections.forEach((conn, id) => { if (conn.state != "closed" && this.#SRV_PTR && conn.pointer) { window.WTLIB.symbols.proc_client_close( - this.#SRV_PTR, conn.pointer, ); window.WTLIB.symbols.free_conn(conn.pointer); @@ -113,7 +112,7 @@ export class WebTransportServer extends EventEmitter { if (this.#SRV_PTR) { await window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); } - + this.#CONNECTION_CB.close(); this.#SRV_PTR = undefined; console.info("[SERVER] Server closed"); } diff --git a/mod/symbols.ts b/mod/symbols.ts index db813f1..c0caaeb 100644 --- a/mod/symbols.ts +++ b/mod/symbols.ts @@ -57,7 +57,7 @@ export const symbols = { nonblocking: true, }, proc_client_close: { - parameters: ["pointer", "pointer"], + parameters: ["pointer"], result: "void", }, // Shared symbols diff --git a/src/client.rs b/src/client.rs index f250366..384ee51 100644 --- a/src/client.rs +++ b/src/client.rs @@ -97,16 +97,10 @@ pub unsafe extern "C" fn proc_client_connect( } #[no_mangle] -pub unsafe extern "C" fn proc_client_close( - client_ptr: *mut WebTransportClient, - conn: *mut Conn, -) -> usize { - assert!(!client_ptr.is_null()); +pub unsafe extern "C" fn proc_client_close(conn: *mut Conn) -> usize { assert!(!conn.is_null()); println!("CLIENT CLOSE CALLED"); - let client = &mut *client_ptr; let conn = &mut *conn; - client.state = Some(false); conn.close(32, Some(b"NO")); 0 } From 54c220c725e8333ce90f8462d381dab679c43135 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 25 Sep 2023 00:11:38 +0200 Subject: [PATCH 69/72] Fixed gracefull server close --- examples/deno/wt_server_test.ts | 1 - mod/server.ts | 12 ++++++----- mod/symbols.ts | 5 +++++ src/server.rs | 37 +++++++++++++++++++++++++++------ 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 27a8070..51fe6af 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -11,7 +11,6 @@ async function sleep(msec: number) { Deno.test({ name: "Server startup/close" }, async () => { //generate a certificate - sleep(2); const server = new WebTransportServer("https://localhost:4433", { certFile: "./certs/localhost.crt", diff --git a/mod/server.ts b/mod/server.ts index 7ea828f..cb22272 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -100,17 +100,19 @@ export class WebTransportServer extends EventEmitter { console.info("[JS] SERVER CLOSE CALLED"); //free all the connections this.connections.forEach((conn, id) => { - if (conn.state != "closed" && this.#SRV_PTR && conn.pointer) { - window.WTLIB.symbols.proc_client_close( - conn.pointer, - ); + if (conn.state != "closed" && conn.pointer) { + // window.WTLIB.symbols.proc_client_close( + // conn.pointer, + // ); window.WTLIB.symbols.free_conn(conn.pointer); conn.state = "closed"; } this.connections.delete(id); }); if (this.#SRV_PTR) { - await window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); + await window.WTLIB.symbols.proc_server_close_clients(this.#SRV_PTR); + window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); + window.WTLIB.symbols.free_server(this.#SRV_PTR); } this.#CONNECTION_CB.close(); this.#SRV_PTR = undefined; diff --git a/mod/symbols.ts b/mod/symbols.ts index c0caaeb..4b994c4 100644 --- a/mod/symbols.ts +++ b/mod/symbols.ts @@ -22,6 +22,11 @@ export const symbols = { nonblocking: false, }, proc_server_close: { + parameters: ["pointer"], + result: "usize", + nonblocking: false, + }, + proc_server_close_clients: { parameters: ["pointer"], result: "usize", nonblocking: true, diff --git a/src/server.rs b/src/server.rs index 99c87ac..e15c1b8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -127,6 +127,17 @@ pub unsafe extern "C" fn proc_server_listen( RUNTIME.spawn(async move { loop { + match server.state { + Some(true) => {} + Some(false) => { + println!("Server state is false, exiting"); + return; + } + None => { + println!("Server state is None, exiting"); + return; + } + } match server.handle_sess_in().await { Ok(conn) => { cb(conn); @@ -186,19 +197,33 @@ pub extern "C" fn proc_server_client_path( #[no_mangle] pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) -> usize { assert!(!server_ptr.is_null()); - println!("SERVER CLOSE CALLED"); let server = &mut *server_ptr; - server.state = Some(false); let endpoint = server.server.as_mut(); match endpoint { - Some(endpoint) => RUNTIME.block_on(async move { - endpoint.wait_idle().await; - }), + Some(endpoint) => { + endpoint.close(30, b"Server closing"); + } None => println!("Error closing server"), } 0 } - +#[no_mangle] +pub unsafe extern "C" fn proc_server_close_clients(server_ptr: *mut WebTransportServer) -> usize { + assert!(!server_ptr.is_null()); + println!("CLIENT CLOSE CALLED"); + let server = &mut *server_ptr; + server.state = Some(false); + let endpoint = server.server.as_mut(); + match endpoint { + Some(endpoint) => { + RUNTIME.block_on(async move { + endpoint.wait_idle().await; + }); + } + None => println!("Error closing clients connections"), + } + 0 +} //free all above once #[no_mangle] pub unsafe extern "C" fn free_all_server(_a: *mut WebTransportServer, _c: *mut Runtime) {} From c6981a76882669adf9ebe91cbc7993ea2fb8cdc5 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 25 Sep 2023 00:12:13 +0200 Subject: [PATCH 70/72] fmt --- examples/deno/wt_server_test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 51fe6af..533cc86 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -5,7 +5,7 @@ import { assert } from "https://deno.land/std@0.202.0/assert/mod.ts"; import { WebTransportServer } from "../../mod/server.ts"; // //add certs cleanup methods after tests // const certPath = join(Deno.cwd(), "./certs/"); -async function sleep(msec: number) { +async function _sleep(msec: number) { await new Promise((res, _rej) => setTimeout(res, msec)); } From d898a643d4a8bf9443abfe51fb97be39ef158914 Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 25 Sep 2023 13:49:29 +0200 Subject: [PATCH 71/72] Fixed crash on close --- mod/server.ts | 20 +++++++++++++++----- src/server.rs | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/mod/server.ts b/mod/server.ts index cb22272..568ed1c 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -99,6 +99,13 @@ export class WebTransportServer extends EventEmitter { async close() { console.info("[JS] SERVER CLOSE CALLED"); //free all the connections + if (this.#SRV_PTR) { + if (this.connections.size > 0) { + await window.WTLIB.symbols.proc_server_close_clients( + this.#SRV_PTR, + ); + } + } this.connections.forEach((conn, id) => { if (conn.state != "closed" && conn.pointer) { // window.WTLIB.symbols.proc_client_close( @@ -109,12 +116,15 @@ export class WebTransportServer extends EventEmitter { } this.connections.delete(id); }); - if (this.#SRV_PTR) { - await window.WTLIB.symbols.proc_server_close_clients(this.#SRV_PTR); - window.WTLIB.symbols.proc_server_close(this.#SRV_PTR); - window.WTLIB.symbols.free_server(this.#SRV_PTR); - } + this.#CONNECTION_CB.close(); + await new Promise((r) => { + setTimeout(() => { + window.WTLIB.symbols.proc_server_close(this.#SRV_PTR!); + r(true); + }, 100); + }); + window.WTLIB.symbols.free_server(this.#SRV_PTR!); this.#SRV_PTR = undefined; console.info("[SERVER] Server closed"); } diff --git a/src/server.rs b/src/server.rs index e15c1b8..4bce2e1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -210,7 +210,7 @@ pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) #[no_mangle] pub unsafe extern "C" fn proc_server_close_clients(server_ptr: *mut WebTransportServer) -> usize { assert!(!server_ptr.is_null()); - println!("CLIENT CLOSE CALLED"); + println!("SERVER CLIENTS CLOSE CALLED"); let server = &mut *server_ptr; server.state = Some(false); let endpoint = server.server.as_mut(); From 7f2dc71affff7951af62d76abbd87f1acf14ba5c Mon Sep 17 00:00:00 2001 From: Nassim Zen Date: Mon, 25 Sep 2023 23:12:27 +0200 Subject: [PATCH 72/72] Need more fixes --- deno.jsonc | 11 ++- examples/deno/wt_client_bidi.ts | 29 ++++---- examples/deno/wt_server_bidi_recv.ts | 57 +++++++++++++++ ..._server_bidi.ts => wt_server_bidi_send.ts} | 30 +++++--- examples/deno/wt_server_test.ts | 36 ++++------ mod/client.ts | 7 +- mod/connection.ts | 8 ++- mod/server.ts | 6 +- mod/streams.ts | 71 ++++--------------- src/client.rs | 4 +- src/server.rs | 2 +- 11 files changed, 149 insertions(+), 112 deletions(-) create mode 100644 examples/deno/wt_server_bidi_recv.ts rename examples/deno/{wt_server_bidi.ts => wt_server_bidi_send.ts} (56%) diff --git a/deno.jsonc b/deno.jsonc index c29a759..d092f0e 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -7,7 +7,8 @@ "demo:server": "deno run -A --unstable ./examples/deno/wt_server.ts", "demo:server-uni-recv": "deno run -A --unstable ./examples/deno/wt_server_uni_recv.ts", "demo:server-uni-send": "deno run -A --unstable ./examples/deno/wt_server_uni_send.ts", - "demo:server-bidi": "deno run -A --unstable ./examples/deno/wt_server_bidi.ts", + "demo:server-bidi-send": "deno run -A --unstable ./examples/deno/wt_server_bidi_send.ts", + "demo:server-bidi-recv": "deno run -A --unstable ./examples/deno/wt_server_bidi_recv.ts", "demo:client": "deno run -A --unstable ./examples/deno/wt_client.ts", "demo:bidi": "deno run -A --unstable ./examples/deno/wt_client_bidi.ts", "demo:uni-send": "deno run -A --unstable ./examples/deno/wt_client_uni_send.ts", @@ -37,5 +38,13 @@ ], "lineWidth": 80, "indentWidth": 4 + }, + "lint": { + "exclude": [ + "./target", + "./.git", + "./.github", + "./.vscode" + ] } } diff --git a/examples/deno/wt_client_bidi.ts b/examples/deno/wt_client_bidi.ts index befd9f5..b814480 100644 --- a/examples/deno/wt_client_bidi.ts +++ b/examples/deno/wt_client_bidi.ts @@ -3,23 +3,24 @@ import "../../mod/mod.ts"; //get the connect address from args 1 const connectAddr = Deno.args[0] ?? "https://localhost:4433"; -const client = new WebTransport(connectAddr, { +const transport = new WebTransport(connectAddr, { maxTimeout: 10, keepAlive: 3, }); -const transport = await client.ready; +await transport.ready; console.log("Client connected"); -const stream = transport.incomingBidirectionalStreams; -const reader = stream.getReader(); +const stream = await transport.createBidirectionalStream(); +const decoder = new TextDecoder(); +const stdin = Deno.stdin.readable; //get the last oppened stream -reader.read().then((value) => { - console.log("new stream opened ", value); -}); -// for await (const streams of uni) { -// const writer = streams.writable.getWriter(); -// for await (const reads of streams.readable) { -// console.log(reads); -// writer.write(reads); -// } -// } +await Promise.all([ + (async () => { + for await (const data of stream!.readable) { + console.log("Recevied : ", decoder.decode(data)); + } + })(), + (() => { + stdin.pipeTo(stream!.writable); + })(), +]); diff --git a/examples/deno/wt_server_bidi_recv.ts b/examples/deno/wt_server_bidi_recv.ts new file mode 100644 index 0000000..c95f984 --- /dev/null +++ b/examples/deno/wt_server_bidi_recv.ts @@ -0,0 +1,57 @@ +//TO BE IMPLEMENTED +import "../../mod/mod.ts"; + +//get cert path from args 1 and 2 (cert and key) or use default +const certFile = Deno.args[0] ?? "./certs/localhost.crt"; +const keyFile = Deno.args[1] ?? "./certs/localhost.key"; +//check if certFile and keyFile are valid non-empty strings +if (typeof certFile !== "string" || typeof keyFile !== "string") { + console.error("Invalid certFile or keyFile"); + Deno.exit(1); +} +//check path +try { + Deno.statSync(certFile); + Deno.statSync(keyFile); +} catch { + console.error("Invalid certFile or keyFile"); + Deno.exit(1); +} + +const server = new WebTransportServer("https://localhost:4433", { + certFile: "./certs/localhost.crt", + keyFile: "./certs/localhost.key", + maxTimeout: 10, + keepAlive: 3, +}); + +await server.ready; +console.log("Server listening"); +const decodeer = new TextDecoder(); +const stdin = Deno.stdin.readable; +server.on("connection", async (conn) => { + console.log("New connection"); + const bidiStream = conn.incomingBidirectionalStreams; + const BIDireader = bidiStream.getReader(); + const { value: firststream } = await BIDireader.read(); + + console.log("READING STD IN"); + await Promise.all([ + (async () => { + for await (const data of firststream!.readable) { + const decoded = decodeer.decode(data).trim().replaceAll( + "\t", + " ", + ); + if (decoded === "exit") { + await server.close(); + return; + } + console.log("Recevied : ", decoded); + } + })(), + (() => { + stdin.pipeTo(firststream!.writable); + })(), + ]); +}); diff --git a/examples/deno/wt_server_bidi.ts b/examples/deno/wt_server_bidi_send.ts similarity index 56% rename from examples/deno/wt_server_bidi.ts rename to examples/deno/wt_server_bidi_send.ts index ad7080e..64ed7a4 100644 --- a/examples/deno/wt_server_bidi.ts +++ b/examples/deno/wt_server_bidi_send.ts @@ -25,15 +25,29 @@ const server = new WebTransportServer("https://localhost:4433", { keepAlive: 3, }); -server.listen(); +await server.ready; console.log("Server listening"); +const decoder = new TextDecoder(); +const stdin = Deno.stdin.readable; server.on("connection", async (conn) => { + console.log("New connection"); const bidiStream = await conn.createBidirectionalStream(); - const writer = bidiStream.writable.getWriter(); - writer.write(new Uint8Array([1, 2, 3, 4, 5])); - writer.write(new Uint8Array([1, 2, 3, 4, 5])); - writer.write(new Uint8Array([1, 2, 3, 4, 5])); - for await (const reads of bidiStream.readable) { - console.log(reads); - } + await Promise.all([ + (async () => { + for await (const data of bidiStream!.readable) { + const decoded = decoder.decode(data).trim().replaceAll( + "\t", + " ", + ); + if (decoded.trim() === "exit") { + console.log("Closing server"); + await server.close(); + } + console.log("Recevied : ", decoded); + } + })(), + (() => { + stdin.pipeTo(bidiStream!.writable); + })(), + ]); }); diff --git a/examples/deno/wt_server_test.ts b/examples/deno/wt_server_test.ts index 533cc86..73e1be2 100644 --- a/examples/deno/wt_server_test.ts +++ b/examples/deno/wt_server_test.ts @@ -1,31 +1,25 @@ // import { GenerateCertKeyFile } from "../../mod/crypto.ts"; import "../../mod/mod.ts"; -import { assert } from "https://deno.land/std@0.202.0/assert/mod.ts"; +// import { assert } from "https://deno.land/std@0.202.0/assert/mod.ts"; -import { WebTransportServer } from "../../mod/server.ts"; +// import { WebTransportServer } from "../../mod/server.ts"; // //add certs cleanup methods after tests // const certPath = join(Deno.cwd(), "./certs/"); async function _sleep(msec: number) { await new Promise((res, _rej) => setTimeout(res, msec)); } -Deno.test({ name: "Server startup/close" }, async () => { - //generate a certificate +// Deno.test({ name: "Server startup/close" }, async () => { +// //generate a certificate - const server = new WebTransportServer("https://localhost:4433", { - certFile: "./certs/localhost.crt", - keyFile: "./certs/localhost.key", - maxTimeout: 10, - keepAlive: 3, - }); - await server.ready; - await server.close(); - //try to start a UDP socket on the same port to see if it's closed - const sock = Deno.listenDatagram({ - hostname: "0.0.0.0", - port: 4433, - transport: "udp", - }); - assert(sock, "Server did not close"); - sock.close(); -}); +// const server = new WebTransportServer("https://localhost:4433", { +// certFile: "./certs/localhost.crt", +// keyFile: "./certs/localhost.key", +// maxTimeout: 10, +// keepAlive: 3, +// }); +// await server.ready; +// await server.close().catch((e) => { +// console.log(e); +// }); +// }); diff --git a/mod/client.ts b/mod/client.ts index a93a2b4..2d76ac8 100644 --- a/mod/client.ts +++ b/mod/client.ts @@ -172,8 +172,11 @@ export class WebTransport { error = new Deno.UnsafeCallback({ parameters: ["u32", "buffer", "u32"], result: "void", - }, (code, _pointer, _buflen) => { - console.log("CB CALLED : ", code); + }, async (code, _pointer, _buflen) => { + if (code === 154) { + console.log("Timed out, closing connection"); + await this.closed; + } }); get closed() { return new Promise(() => { diff --git a/mod/connection.ts b/mod/connection.ts index 9510bc9..ef9d9c2 100644 --- a/mod/connection.ts +++ b/mod/connection.ts @@ -58,7 +58,9 @@ export default class WebTransportConnection { errorPTR, ); if (!stream || stream === null) { - console.error("[incoming BIDI] Stream not accepted"); + console.error( + "[incoming BIDI] Stream not accepted", + ); controller.close(); return; } @@ -163,7 +165,9 @@ export default class WebTransportConnection { parameters: ["u32", "buffer", "u32"], result: "void", }, (code, _pointer, _buflen) => { - console.log("CB CALLED : ", code); + if (code === 154) { + this.close(); + } }); close(_closeInfo?: WebTransportCloseInfo) { if (!this.pointer || this.pointer === null) { diff --git a/mod/server.ts b/mod/server.ts index 568ed1c..031d515 100644 --- a/mod/server.ts +++ b/mod/server.ts @@ -108,9 +108,9 @@ export class WebTransportServer extends EventEmitter { } this.connections.forEach((conn, id) => { if (conn.state != "closed" && conn.pointer) { - // window.WTLIB.symbols.proc_client_close( - // conn.pointer, - // ); + window.WTLIB.symbols.proc_client_close( + conn.pointer, + ); window.WTLIB.symbols.free_conn(conn.pointer); conn.state = "closed"; } diff --git a/mod/streams.ts b/mod/streams.ts index f76d7d3..c8ea829 100644 --- a/mod/streams.ts +++ b/mod/streams.ts @@ -82,7 +82,7 @@ export class WebTransportDatagramDuplexStream { */ export class WebTransportBidirectionalStream { readonly readable: ReadableStream; - readonly writable: WritableStream; + readonly writable: WritableStream; constructor( public readonly ptr: Deno.PointerValue, @@ -93,6 +93,7 @@ export class WebTransportBidirectionalStream { undefined, errorCB, ); + console.log(this.ptr); this.writable = WebTransportSendStream.from(this.ptr, errorCB); } } @@ -110,6 +111,7 @@ export class WebTransportReceiveStream { DEFAULT_CHUNK_SIZE = 1024, cb: Deno.PointerValue, ) { + console.log("BIDI READER"); return new ReadableStream({ type: "bytes", start(controller) { @@ -131,7 +133,6 @@ export class WebTransportReceiveStream { controller.close(); } controller.byobRequest.respond(bytesRead as number); - console.log(`byobRequest with ${bytesRead} bytes`); } else { const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); bytesRead = await window.WTLIB.symbols.proc_read( @@ -147,9 +148,6 @@ export class WebTransportReceiveStream { new Uint8Array(buffer, 0, bytesRead as number), ); } - console.log( - `enqueue() ${bytesRead} bytes (no byobRequest)`, - ); } if (bytesRead === 0) { @@ -160,46 +158,7 @@ export class WebTransportReceiveStream { // }); } }, - // pull(controller) { - // readRepeatedly().catch((e) => controller.error(e)); - // async function readRepeatedly() { - // if (!ptr || ptr === null) { - // throw new Error("Stream is closed"); - // } - // let bytesRead; - // if (controller.byobRequest) { - // const v = controller.byobRequest.view; - // bytesRead = await window.WTLIB.symbols.proc_read( - // ptr, - // v?.buffer!, - // v?.byteLength!, - // ); - // if (bytesRead === 0) { - // console.log("BYOB REQUEST"); - // controller.close(); - // } - // controller.byobRequest.respond(bytesRead as number); - // } else { - // const buffer = new ArrayBuffer(DEFAULT_CHUNK_SIZE); - // bytesRead = await window.WTLIB.symbols.proc_read( - // ptr, - // buffer, - // DEFAULT_CHUNK_SIZE, - // ); - // if (bytesRead === 0) { - // controller.close(); - // } else { - // controller.enqueue( - // new Uint8Array(buffer, 0, bytesRead as number), - // ); - // } - // } - // if (bytesRead === 0) { - // return; - // } - // return readRepeatedly(); - // } - // }, + async cancel(reason?: string): Promise { if (!ptr || ptr === null) { console.debug("Stream is closed"); @@ -235,19 +194,15 @@ export class WebTransportSendStream { controller.error("Stream is closed"); return; } - try { - written = await window.WTLIB.symbols.proc_write_all( - ptr, - chunk, - chunk.byteLength, - cb, - ) as number; - if (written === 0) { - controller.error("Write failed"); - return; - } - } catch (e) { - console.error(e); + + written = await window.WTLIB.symbols.proc_write( + ptr, + chunk, + chunk.byteLength, + cb, + ) as number; + if (written === 0) { + controller.error("Write failed"); return; } }, diff --git a/src/client.rs b/src/client.rs index 384ee51..06ecbc2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -99,9 +99,9 @@ pub unsafe extern "C" fn proc_client_connect( #[no_mangle] pub unsafe extern "C" fn proc_client_close(conn: *mut Conn) -> usize { assert!(!conn.is_null()); - println!("CLIENT CLOSE CALLED"); + let conn = &mut *conn; - conn.close(32, Some(b"NO")); + conn.close(30, Some(b"Closed by client")); 0 } diff --git a/src/server.rs b/src/server.rs index 4bce2e1..b3eeeff 100644 --- a/src/server.rs +++ b/src/server.rs @@ -210,7 +210,7 @@ pub unsafe extern "C" fn proc_server_close(server_ptr: *mut WebTransportServer) #[no_mangle] pub unsafe extern "C" fn proc_server_close_clients(server_ptr: *mut WebTransportServer) -> usize { assert!(!server_ptr.is_null()); - println!("SERVER CLIENTS CLOSE CALLED"); + let server = &mut *server_ptr; server.state = Some(false); let endpoint = server.server.as_mut();