Skip to content

Commit

Permalink
Bug 1863995 - Make webtransport datagram test more robust, r=necko-re…
Browse files Browse the repository at this point in the history
…viewers,valentin

With neqo 0.6.7, we send ACK packet if the last time we ACK is more than one RTT. This change makes our test HTTP/3 server send an ACK packet before sending the webtransport datagram.
On windows platform, this causes a problem that the server failed to send the datagram, because ACK frame and datagram frame are in two UDP packets and the OS API prevents from sending two patckets in a short time.
Since webtransport datgram is unreiable, quic won't retransmit it. As the result, the test fails because of timeout.
To fix this problem, this patch uses another way to check if the server really gets the datagram sent from Firefox by creating another HTTP request to get the stored datagram.

Differential Revision: https://phabricator.services.mozilla.com/D194046
  • Loading branch information
KershawChang committed Nov 20, 2023
1 parent 0af4788 commit 351eb49
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 17 deletions.
33 changes: 29 additions & 4 deletions netwerk/test/http3server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use neqo_http3::{
Error, Http3OrWebTransportStream, Http3Parameters, Http3Server, Http3ServerEvent,
WebTransportRequest, WebTransportServerEvent, WebTransportSessionAcceptAction,
};
use neqo_transport::server::{Server, ActiveConnectionRef};
use neqo_transport::server::{ActiveConnectionRef, Server};
use neqo_transport::{
ConnectionEvent, ConnectionParameters, Output, RandomConnectionIdGenerator, StreamId,
StreamType,
Expand Down Expand Up @@ -84,6 +84,7 @@ struct Http3TestServer {
webtransport_bidi_stream: HashSet<Http3OrWebTransportStream>,
wt_unidi_conn_to_stream: HashMap<ActiveConnectionRef, Http3OrWebTransportStream>,
wt_unidi_echo_back: HashMap<Http3OrWebTransportStream, Http3OrWebTransportStream>,
received_datagram: Option<Vec<u8>>,
}

impl ::std::fmt::Display for Http3TestServer {
Expand All @@ -104,6 +105,7 @@ impl Http3TestServer {
webtransport_bidi_stream: HashSet::new(),
wt_unidi_conn_to_stream: HashMap::new(),
wt_unidi_echo_back: HashMap::new(),
received_datagram: None,
}
}

Expand Down Expand Up @@ -172,7 +174,8 @@ impl Http3TestServer {
// relaying Http3ServerEvent::Data to uni streams
// slows down netwerk/test/unit/test_webtransport_simple.js
// to the point of failure. Only do so when necessary.
self.wt_unidi_conn_to_stream.insert(wt_server_stream.conn.clone(), wt_server_stream);
self.wt_unidi_conn_to_stream
.insert(wt_server_stream.conn.clone(), wt_server_stream);
}
} else {
if tuple.2 {
Expand Down Expand Up @@ -365,6 +368,28 @@ impl HttpServer for Http3TestServer {
])
.unwrap();
stream.stream_close_send().unwrap();
} else if path == "/get_webtransport_datagram" {
if let Some(vec_ref) = self.received_datagram.as_ref() {
stream
.send_headers(&[
Header::new(":status", "200"),
Header::new(
"content-length",
vec_ref.len().to_string(),
),
])
.unwrap();
self.new_response(stream, vec_ref.to_vec());
self.received_datagram = None;
} else {
stream
.send_headers(&[
Header::new(":status", "404"),
Header::new("cache-control", "no-cache"),
])
.unwrap();
stream.stream_close_send().unwrap();
}
} else {
match path.trim_matches(|p| p == '/').parse::<usize>() {
Ok(v) => {
Expand Down Expand Up @@ -584,15 +609,15 @@ impl HttpServer for Http3TestServer {
}
}
Http3ServerEvent::WebTransport(WebTransportServerEvent::Datagram {
mut session,
session,
datagram,
}) => {
qdebug!(
"WebTransportServerEvent::Datagram {:?} {:?}",
session,
datagram
);
session.send_datagram(datagram.as_ref(), None).unwrap();
self.received_datagram = Some(datagram);
}
}
}
Expand Down
50 changes: 37 additions & 13 deletions netwerk/test/unit/test_webtransport_simple.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,49 @@ var { setTimeout } = ChromeUtils.importESModule(
registerCleanupFunction(async () => {
Services.prefs.clearUserPref("network.dns.localDomains");
Services.prefs.clearUserPref("network.webtransport.datagrams.enabled");
Services.prefs.clearUserPref(
"network.http.http3.alt-svc-mapping-for-testing"
);
});

add_task(async function setup() {
Services.prefs.setCharPref("network.dns.localDomains", "foo.example.com");
await http3_setup_tests("h3");

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");
});

function makeChan(url) {
let chan = NetUtil.newChannel({
uri: url,
loadUsingSystemPrincipal: true,
contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
}).QueryInterface(Ci.nsIHttpChannel);
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
return chan;
}

function channelOpenPromise(chan, flags) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async resolve => {
function finish(req, buffer) {
resolve([req, buffer]);
}
let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
internal.setWaitForHTTPSSVCRecord();

chan.asyncOpen(new ChannelListener(finish, null, flags));
});
}

function bytesFromString(str) {
return new TextEncoder().encode(str);
}

add_task(async function test_wt_datagram() {
let webTransport = NetUtil.newWebTransport();
let listener = new WebTransportListener().QueryInterface(
Expand All @@ -44,9 +68,6 @@ add_task(async function test_wt_datagram() {
let pReady = new Promise(resolve => {
listener.ready = resolve;
});
let pData = new Promise(resolve => {
listener.onDatagram = resolve;
});
let pSize = new Promise(resolve => {
listener.onMaxDatagramSize = resolve;
});
Expand Down Expand Up @@ -75,8 +96,11 @@ add_task(async function test_wt_datagram() {
Assert.equal(id, 1);
Assert.equal(outcome, Ci.WebTransportSessionEventListener.SENT);

let received = await pData;
Assert.deepEqual(received, rawData);
let chan = makeChan(`https://${host}/get_webtransport_datagram`);
let [req, buffer] = await channelOpenPromise(chan);
Assert.equal(req.protocolVersion, "h3");

Assert.deepEqual(bytesFromString(buffer), rawData);

webTransport.getMaxDatagramSize();
size = await pSize;
Expand Down

0 comments on commit 351eb49

Please sign in to comment.