// // Simple WebTransport test // /* import-globals-from head_webtransport.js */ "use strict"; var h3Port; var host; var { setTimeout } = ChromeUtils.importESModule( "resource://gre/modules/Timer.sys.mjs" ); registerCleanupFunction(async () => { Services.prefs.clearUserPref("network.dns.localDomains"); Services.prefs.clearUserPref("network.webtransport.datagrams.enabled"); }); add_task(async function setup() { Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com"); Services.prefs.setBoolPref("network.webtransport.datagrams.enabled", true); h3Port = Services.env.get("MOZHTTP3_PORT"); Assert.notEqual(h3Port, null); Assert.notEqual(h3Port, ""); host = "foo.example.com:" + h3Port; do_get_profile(); let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( Ci.nsIX509CertDB ); // `../unit/` so that unit_ipc tests can use as well addCertFromFile(certdb, "../unit/http2-ca.pem", "CTu,u,u"); }); add_task(async function test_wt_datagram() { let webTransport = NetUtil.newWebTransport(); let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); let pReady = new Promise(resolve => { listener.ready = resolve; }); let pData = new Promise(resolve => { listener.onDatagram = resolve; }); let pSize = new Promise(resolve => { listener.onMaxDatagramSize = resolve; }); let pOutcome = new Promise(resolve => { listener.onDatagramOutcome = resolve; }); webTransport.asyncConnect( NetUtil.newURI(`https://${host}/success`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); await pReady; webTransport.getMaxDatagramSize(); let size = await pSize; info("max size:" + size); let rawData = new Uint8Array(size); rawData.fill(42); webTransport.sendDatagram(rawData, 1); let { id, outcome } = await pOutcome; Assert.equal(id, 1); Assert.equal(outcome, Ci.WebTransportSessionEventListener.SENT); let received = await pData; Assert.deepEqual(received, rawData); webTransport.getMaxDatagramSize(); size = await pSize; info("max size:" + size); rawData = new Uint8Array(size + 1); webTransport.sendDatagram(rawData, 2); pOutcome = new Promise(resolve => { listener.onDatagramOutcome = resolve; }); ({ id, outcome } = await pOutcome); Assert.equal(id, 2); Assert.equal( outcome, Ci.WebTransportSessionEventListener.DROPPED_TOO_MUCH_DATA ); webTransport.closeSession(0, ""); }); add_task(async function test_connect_wt() { let webTransport = NetUtil.newWebTransport(); await new Promise(resolve => { let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); listener.ready = resolve; webTransport.asyncConnect( NetUtil.newURI(`https://${host}/success`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); }); webTransport.closeSession(0, ""); }); add_task(async function test_redirect_wt() { let webTransport = NetUtil.newWebTransport(); await new Promise(resolve => { let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); listener.closed = resolve; webTransport.asyncConnect( NetUtil.newURI(`https://${host}/redirect`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); }); }); add_task(async function test_reject() { let webTransport = NetUtil.newWebTransport(); await new Promise(resolve => { let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); listener.closed = resolve; webTransport.asyncConnect( NetUtil.newURI(`https://${host}/reject`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); }); }); async function test_closed(path) { let webTransport = NetUtil.newWebTransport(); let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); let pReady = new Promise(resolve => { listener.ready = resolve; }); let pClose = new Promise(resolve => { listener.closed = resolve; }); webTransport.asyncConnect( NetUtil.newURI(`https://${host}${path}`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); await pReady; await pClose; } add_task(async function test_closed_0ms() { await test_closed("/closeafter0ms"); }); add_task(async function test_closed_100ms() { await test_closed("/closeafter100ms"); }); add_task(async function test_wt_stream_create() { let webTransport = NetUtil.newWebTransport().QueryInterface( Ci.nsIWebTransport ); await new Promise(resolve => { let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); listener.ready = resolve; webTransport.asyncConnect( NetUtil.newURI(`https://${host}/success`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); }); await streamCreatePromise(webTransport, true); await streamCreatePromise(webTransport, false); webTransport.closeSession(0, ""); }); add_task(async function test_wt_stream_send_and_stats() { let webTransport = NetUtil.newWebTransport().QueryInterface( Ci.nsIWebTransport ); await new Promise(resolve => { let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); listener.ready = resolve; webTransport.asyncConnect( NetUtil.newURI(`https://${host}/success`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); }); let stream = await streamCreatePromise(webTransport, false); let outputStream = stream.outputStream; let data = "1234567890ABC"; outputStream.write(data, data.length); // We need some time to send the packet out. // eslint-disable-next-line mozilla/no-arbitrary-setTimeout await new Promise(resolve => setTimeout(resolve, 2000)); let stats = await sendStreamStatsPromise(stream); Assert.equal(stats.bytesSent, data.length); webTransport.closeSession(0, ""); }); add_task(async function test_wt_receive_stream_and_stats() { let webTransport = NetUtil.newWebTransport().QueryInterface( Ci.nsIWebTransport ); let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); let pReady = new Promise(resolve => { listener.ready = resolve; }); let pStreamReady = new Promise(resolve => { listener.streamAvailable = resolve; }); webTransport.asyncConnect( NetUtil.newURI(`https://${host}/create_unidi_stream_and_hello`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); await pReady; let stream = await pStreamReady; let data = await new Promise(resolve => { let handler = new inputStreamReader().QueryInterface( Ci.nsIInputStreamCallback ); handler.finish = resolve; let inputStream = stream.inputStream; inputStream.asyncWait(handler, 0, 0, Services.tm.currentThread); }); info("data: " + data); Assert.equal(data, "qwerty"); let stats = await receiveStreamStatsPromise(stream); Assert.equal(stats.bytesReceived, data.length); stream.sendStopSending(0); webTransport.closeSession(0, ""); }); add_task(async function test_wt_outgoing_bidi_stream() { let webTransport = NetUtil.newWebTransport().QueryInterface( Ci.nsIWebTransport ); await new Promise(resolve => { let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); listener.ready = resolve; webTransport.asyncConnect( NetUtil.newURI(`https://${host}/success`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); }); let stream = await streamCreatePromise(webTransport, true); let outputStream = stream.outputStream; let data = "1234567"; outputStream.write(data, data.length); let received = await new Promise(resolve => { let handler = new inputStreamReader().QueryInterface( Ci.nsIInputStreamCallback ); handler.finish = resolve; let inputStream = stream.inputStream; inputStream.asyncWait(handler, 0, 0, Services.tm.currentThread); }); info("received: " + received); Assert.equal(received, data); let stats = await sendStreamStatsPromise(stream); Assert.equal(stats.bytesSent, data.length); stats = await receiveStreamStatsPromise(stream); Assert.equal(stats.bytesReceived, data.length); webTransport.closeSession(0, ""); }); add_task(async function test_wt_incoming_bidi_stream() { let webTransport = NetUtil.newWebTransport().QueryInterface( Ci.nsIWebTransport ); let listener = new WebTransportListener().QueryInterface( Ci.WebTransportSessionEventListener ); let pReady = new Promise(resolve => { listener.ready = resolve; }); let pStreamReady = new Promise(resolve => { listener.streamAvailable = resolve; }); webTransport.asyncConnect( NetUtil.newURI(`https://${host}/create_bidi_stream`), Services.scriptSecurityManager.getSystemPrincipal(), Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, listener ); await pReady; let stream = await pStreamReady; let outputStream = stream.outputStream; let data = "12345678"; outputStream.write(data, data.length); let received = await new Promise(resolve => { let handler = new inputStreamReader().QueryInterface( Ci.nsIInputStreamCallback ); handler.finish = resolve; let inputStream = stream.inputStream; inputStream.asyncWait(handler, 0, 0, Services.tm.currentThread); }); info("received: " + received); Assert.equal(received, data); let stats = await sendStreamStatsPromise(stream); Assert.equal(stats.bytesSent, data.length); stats = await receiveStreamStatsPromise(stream); Assert.equal(stats.bytesReceived, data.length); webTransport.closeSession(0, ""); });