use neqo_common::{Datagram, event::Provider as _, qtrace}; use neqo_http3::{
Header, Http3Client, Http3ClientEvent, Http3OrWebTransportStream, Http3Parameters, Http3Server,
Http3ServerEvent, Http3State, Priority,
}; use neqo_transport::{CloseReason, ConnectionParameters, Error, Output, StreamType}; use nss::{AuthenticationStatus, ResumptionToken}; use test_fixture::*;
fn connect_peers_with_network_propagation_delay(
hconn_c: &mut Http3Client,
hconn_s: &mut Http3Server,
net_delay: u64,
) -> (Option<Datagram>, Instant) { let net_delay = Duration::from_millis(net_delay);
assert_eq!(hconn_c.state(), Http3State::Initializing); letmut now = now(); let out = hconn_c.process_output(now); // Initial let out2 = hconn_c.process_output(now); // Initial
now += net_delay;
_ = hconn_s.process(out.dgram(), now); // ACK let out = hconn_s.process(out2.dgram(), now);
now += net_delay; let out = hconn_c.process(out.dgram(), now);
now += net_delay; let out = hconn_s.process(out.dgram(), now);
now += net_delay; let out = hconn_c.process(out.dgram(), now); // ACK
now += net_delay; let out = hconn_s.process(out.dgram(), now); // consume ACK
assert!(out.dgram().is_none()); let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded);
assert!(hconn_c.events().any(authentication_needed));
now += net_delay;
hconn_c.authenticated(AuthenticationStatus::Ok, now); let out = hconn_c.process_output(now); // Handshake
assert_eq!(hconn_c.state(), Http3State::Connected);
now += net_delay; let out = hconn_s.process(out.dgram(), now); // HANDSHAKE_DONE
now += net_delay; let out = hconn_c.process(out.dgram(), now); // Consume HANDSHAKE_DONE, send control streams.
now += net_delay; let out = hconn_s.process(out.dgram(), now); // consume and send control streams.
now += net_delay; let out = hconn_c.process(out.dgram(), now); // consume control streams.
(out.dgram(), now)
}
set_response(&request, now()); let out = hconn_s.process(None::<Datagram>, now());
drop(hconn_c.process(out.dgram(), now()));
process_client_events(&mut hconn_c);
}
/// Test [`neqo_http3::SendMessage::send_data`] to set /// [`neqo_transport::SendStream::set_writable_event_low_watermark`]. #[expect(clippy::cast_possible_truncation, reason = "OK in a test.")] #[test] fn data_writable_events_low_watermark() -> Result<(), Box<dyn std::error::Error>> { const STREAM_LIMIT: u64 = 5000; const DATA_FRAME_HEADER_SIZE: usize = 3;
// Create a client and a server. letmut hconn_c = http3_client_with_params(Http3Parameters::default().connection_parameters(
ConnectionParameters::default().max_stream_data(StreamType::BiDi, false, STREAM_LIMIT),
)); letmut hconn_s = default_http3_server();
drop(connect_peers(&mut hconn_c, &mut hconn_s));
// Client sends GET to server. let stream_id = hconn_c.fetch(
now(), "GET",
("https", "something.com", "/"),
&[],
Priority::default(),
)?;
hconn_c.stream_close_send(stream_id, now())?;
exchange_packets(&mut hconn_c, &mut hconn_s, false, None);
// Server receives GET and responds with headers. let request = receive_request(&hconn_s).unwrap();
request.send_headers(&[Header::new(":status", "200")])?;
// Sending these headers clears the server's send stream buffer and thus // emits a DataWritable event.
exchange_packets(&mut hconn_c, &mut hconn_s, false, None); let data_writable = |e| {
matches!(
e,
Http3ServerEvent::DataWritable {
stream
} if stream.stream_id() == stream_id
)
};
assert!(hconn_s.events().any(data_writable));
// Have server fill entire send buffer minus 1 byte. let all_but_one = request.available()? - DATA_FRAME_HEADER_SIZE - 1; let buf = vec![1; all_but_one]; let sent = request.send_data(&buf, now())?;
assert_eq!(sent, all_but_one);
assert_eq!(request.available()?, 1);
// Sending the buffered data clears the send stream buffer and thus emits a // DataWritable event.
exchange_packets(&mut hconn_c, &mut hconn_s, false, None);
assert!(hconn_s.events().any(data_writable));
// Sending more fails, given that each data frame needs to be preceded by a // header, i.e. needs more than 1 byte of send space to send 1 byte payload.
assert_eq!(request.available()?, 1);
assert_eq!(request.send_data(&buf, now())?, 0);
// Have the client read all the pending data. letmut recv_buf = vec![0_u8; all_but_one]; let (recvd, _) = hconn_c.read_data(now(), stream_id, &mut recv_buf)?;
assert_eq!(sent, recvd);
exchange_packets(&mut hconn_c, &mut hconn_s, false, None);
// Expect the server's available send space to be back to the stream limit.
assert_eq!(request.available()?, STREAM_LIMIT as usize);
// Expect the server to emit a DataWritable event, even though it always had // at least 1 byte available to send, i.e. it never exhausted the entire // available send space.
assert!(hconn_s.events().any(data_writable));
// Send a lot of data let buf = &[1; DATA_AMOUNT]; letmut sent = request.send_data(buf, now()).unwrap();
assert!(sent < DATA_AMOUNT);
// Exchange packets and read the data on the client side.
exchange_packets(&mut hconn_c, &mut hconn_s, false, None); let stream_id = request.stream_id(); letmut recv_buf = [0_u8; DATA_AMOUNT]; let (mut recvd, _) = hconn_c.read_data(now(), stream_id, &mut recv_buf).unwrap();
assert_eq!(sent, recvd);
exchange_packets(&mut hconn_c, &mut hconn_s, false, None);
let data_writable = |e| {
matches!(
e,
Http3ServerEvent::DataWritable {
stream
} if stream.stream_id() == stream_id
)
}; // Make sure we have a DataWritable event.
assert!(hconn_s.events().any(data_writable)); // Data can be sent again. let s = request.send_data(&buf[sent..], now()).unwrap();
assert!(s > 0);
sent += s;
// Exchange packets and read the data on the client side.
exchange_packets(&mut hconn_c, &mut hconn_s, false, None); let (r, _) = hconn_c
.read_data(now(), stream_id, &mut recv_buf[recvd..])
.unwrap();
recvd += r;
exchange_packets(&mut hconn_c, &mut hconn_s, false, None);
assert_eq!(sent, recvd);
// One more DataWritable event.
assert!(hconn_s.events().any(data_writable)); // Send more data. let s = request.send_data(&buf[sent..], now()).unwrap();
assert!(s > 0);
sent += s;
assert_eq!(sent, DATA_AMOUNT);
#[test] fn zerortt() { let (mut hconn_c, _, dgram) = connect(); let token = get_token(&mut hconn_c);
// Create a new connection with a resumption token. letmut hconn_c = default_http3_client();
hconn_c
.enable_resumption(now(), &token)
.expect("Set resumption token"); letmut hconn_s = default_http3_server();
// Create a request. let req = hconn_c
.fetch(
now(), "GET",
("https", "something.com", "/"),
&[],
Priority::default(),
)
.unwrap();
hconn_c.stream_close_send(req, now()).unwrap();
let out = hconn_c.process(dgram, now()); let out2 = hconn_c.process_output(now());
_ = hconn_s.process(out.dgram(), now()); let out = hconn_s.process(out2.dgram(), now());
let out = hconn_c.process(out.dgram(), now()); let out = hconn_s.process(out.dgram(), now());
#[test] /// When a client has an outstanding fetch, it will send keepalives. /// Test that it will successfully run until the connection times out. fn fetch_noresponse_will_idletimeout() { letmut hconn_c = default_http3_client(); letmut hconn_s = default_http3_server();
// Server needs to gracefully handle out-of-order STOP_SENDING and STREAM frame arrivals. fn server_stop_sending_and_stream_test(separate_packets: bool, stop_sending_first: bool) { let (mut client, mut server, stream_id) = common::connect_and_send_request(false);
let send_stop_sending = |c: &mut Http3Client| c.stream_stop_sending(stream_id, 0).unwrap(); let send_fin = |c: &mut Http3Client| c.stream_close_send(stream_id, now()).unwrap();
if stop_sending_first {
send_stop_sending(&mut client);
} else {
send_fin(&mut client);
} if separate_packets {
server.process(client.process_output(now()).dgram(), now());
} if stop_sending_first {
send_fin(&mut client);
} else {
send_stop_sending(&mut client);
}
server.process(client.process_output(now()).dgram(), now());
let events: Vec<_> = server.events().collect();
assert!(events.iter().any(|e| matches!(
e, Http3ServerEvent::StreamStopSending { stream, .. } if stream.stream_id() == stream_id
))); for event in events { iflet Http3ServerEvent::Headers { stream, .. } = event { // The stream is dead; any attempt to send on it should fail.
assert_eq!(
stream.send_headers(&[Header::new(":status", "200")]),
Err(neqo_http3::Error::InvalidStreamId)
);
assert_eq!(
stream.stream_close_send(now()),
Err(neqo_http3::Error::InvalidStreamId)
);
}
}
}
#[test] fn server_stop_sending_and_stream_combinations() { for separate_packets in [false, true] { for stop_sending_first in [false, true] {
server_stop_sending_and_stream_test(separate_packets, stop_sending_first);
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet am 2026-08-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.