/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // keep eslint happy until it knows about WebTransport /* global WebTransport:false */ /* global TextDecoderStream:false */ // Using multiple files to reduce racing // This file tests reading/writing to incoming/outgoing streams (uni & bidi) // "use strict"; var h3Port; var host; registerCleanupFunction(async () => { Services.prefs.clearUserPref("network.dns.localDomains"); Services.prefs.clearUserPref("network.webtransport.enabled"); Services.prefs.clearUserPref("network.webtransport.datagrams.enabled"); Services.prefs.clearUserPref("network.webtransport.redirect.enabled"); }); var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm"); function readFile(file) { let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance( Ci.nsIFileInputStream ); fstream.init(file, -1, 0, 0); let data = NetUtil.readInputStreamToString(fstream, fstream.available()); fstream.close(); return data; } function addCertFromFile(certdb, filename, trustString) { let certFile = do_get_file(filename, false); let pem = readFile(certFile) .replace(/-----BEGIN CERTIFICATE-----/, "") .replace(/-----END CERTIFICATE-----/, "") .replace(/[\r\n]/g, ""); certdb.addCertFromBase64(pem, trustString); } add_setup(async function setup() { Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com"); Services.prefs.setBoolPref("network.webtransport.enabled", true); Services.prefs.setBoolPref("network.webtransport.datagrams.enabled", true); Services.prefs.setBoolPref("network.webtransport.redirect.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, "../../../../netwerk/test/unit/http2-ca.pem", "CTu,u,u" ); }); // Read all chunks from |readable_stream|, decode chunks to a utf-8 string, then // return the string. (borrowed from wpt tests) async function read_stream_as_string(readable_stream) { const decoder = new TextDecoderStream(); const decode_stream = readable_stream.pipeThrough(decoder); const reader = decode_stream.getReader(); let chunks = ""; while (true) { const { value: chunk, done } = await reader.read(); if (done) { break; } chunks += chunk; } reader.releaseLock(); return chunks; } add_task(async function test_wt_incoming_unidi_stream() { // trigger stream creation server side and default echo let wt = new WebTransport( "https://" + host + "/create_unidi_stream_and_hello" ); await wt.ready; const streams = await wt.incomingUnidirectionalStreams; const stream_reader = streams.getReader(); const { value: recv_stream } = await stream_reader.read(); let str = await read_stream_as_string(recv_stream); stream_reader.releaseLock(); Assert.equal(str, "qwerty"); wt.close(); }); add_task(async function test_wt_incoming_and_outgoing_unidi_stream() { // create the client's incoming stream from the server side // we need it to listen to the echo back let wt = new WebTransport("https://" + host + "/create_unidi_stream"); await wt.ready; // send hello to server let expected = "uni_hello"; let writableStream = await wt.createUnidirectionalStream(); // only triggers NewStream OnWrite let wsDefaultWriter = writableStream.getWriter(); await wsDefaultWriter.ready; let data = new TextEncoder().encode(expected); await wsDefaultWriter.write(data); // triggers Http3ServerEvent::Data await wsDefaultWriter.close(); wsDefaultWriter.releaseLock(); // read the echo const streams = await wt.incomingUnidirectionalStreams; const stream_reader = streams.getReader(); const { value: recv_stream } = await stream_reader.read(); let str = await read_stream_as_string(recv_stream); Assert.equal(str, expected); stream_reader.releaseLock(); await recv_stream.closed; wt.close(); }); add_task(async function test_wt_outgoing_bidi_stream() { let wt = new WebTransport("https://" + host + "/success"); await wt.ready; // write to server let wtbds = await wt.createBidirectionalStream(); let writableStream = wtbds.writable; let wsDefaultWriter = writableStream.getWriter(); await wsDefaultWriter.ready; let expected = "xyzhello"; let data = new TextEncoder().encode(expected); await wsDefaultWriter.write(data); await wsDefaultWriter.close(); wsDefaultWriter.releaseLock(); // string goes through server and is echoed back here const str = await read_stream_as_string(wtbds.readable); Assert.equal(str, expected); wt.close(); }); add_task(async function test_wt_incoming_bidi_stream() { let wt = new WebTransport( "https://" + host + "/create_bidi_stream_and_hello" ); // await wt.ready; // causes occasional hang on release --verify const stream_reader = wt.incomingBidirectionalStreams.getReader(); const { value: bidi_stream } = await stream_reader.read(); stream_reader.releaseLock(); const str = await read_stream_as_string(bidi_stream.readable); Assert.equal(str, "asdfg"); wt.close(); });