/// Short-hand for [`Http3Server::process`] with no input datagram. pubfn process_output(&mutself, now: Instant) -> Output { self.process(None::<Datagram>, now)
}
pubfn process(&mutself, dgram: Option<Datagram<impl AsRef<[u8]>>>, now: Instant) -> Output {
qtrace!([self], "Process."); let out = self.server.process(dgram, now); self.process_http3(now); // If we do not that a dgram already try again after process_http3. match out {
Output::Datagram(d) => {
qtrace!([self], "Send packet: {:?}", d);
Output::Datagram(d)
}
_ => self.server.process(Option::<Datagram>::None, now),
}
}
/// Process HTTP3 layer. fn process_http3(&mutself, now: Instant) {
qtrace!([self], "Process http3 internal."); // `ActiveConnectionRef` `Hash` implementation doesn’t access any of the interior mutable // types. #[allow(clippy::mutable_key_type)] letmut active_conns = self.server.active_connections();
active_conns.extend( self.http3_handlers
.iter()
.filter(|(_, handler)| handler.borrow_mut().should_be_processed())
.map(|(c, _)| c)
.cloned(),
);
for conn in active_conns { self.process_events(&conn, now);
}
}
/// Get all current events. Best used just in debug/testing code, use /// `next_event` instead. pubfn events(&self) -> impl Iterator<Item = Http3ServerEvent> { self.events.events()
}
/// Return true if there are outstanding events. #[must_use] pubfn has_events(&self) -> bool { self.events.has_events()
}
/// Get events that indicate state changes on the connection. This method /// correctly handles cases where handling one event can obsolete /// previously-queued events, or cause new events to be generated. #[must_use] pubfn next_event(&self) -> Option<Http3ServerEvent> { self.events.next_event()
}
} fn prepare_data(
stream_info: Http3StreamInfo,
handler_borrowed: &mut RefMut<Http3ServerHandler>,
conn: &ConnectionRef,
handler: &HandlerRef,
now: Instant,
events: &Http3ServerEvents,
) { loop { letmut data = vec![0; MAX_EVENT_DATA_SIZE]; let res = handler_borrowed.read_data(
&mut conn.borrow_mut(),
now,
stream_info.stream_id(),
&mut data,
); iflet Ok((amount, fin)) = res { if amount > 0 || fin { if amount < MAX_EVENT_DATA_SIZE {
data.resize(amount, 0);
}
events.data(conn.clone(), handler.clone(), stream_info, data, fin);
} if amount < MAX_EVENT_DATA_SIZE || fin { break;
}
} else { // Any error will closed the handler, just ignore this event, the next event must // be a state change event. break;
}
}
}
#[cfg(test)] mod tests { use std::{
collections::HashMap,
mem,
ops::{Deref, DerefMut},
};
use neqo_common::{event::Provider, Encoder}; use neqo_crypto::{AuthenticationStatus, ZeroRttCheckResult, ZeroRttChecker}; use neqo_qpack::{encoder::QPackEncoder, QpackSettings}; use neqo_transport::{
CloseReason, Connection, ConnectionEvent, State, StreamId, StreamType, ZeroRttState,
}; use test_fixture::{
anti_replay, default_client, fixture_init, now, CountingConnectionIdGenerator,
DEFAULT_ALPN, DEFAULT_KEYS,
};
fn connect_transport(server: &mut Http3Server, client: &le='color:red'>mut Connection, resume: bool) { let c1 = client.process_output(now()); let s1 = server.process(c1.dgram(), now()); let c2 = client.process(s1.dgram(), now()); let needs_auth = client
.events()
.any(|e| e == ConnectionEvent::AuthenticationNeeded); let c2 = if needs_auth {
assert!(!resume); // c2 should just be an ACK, so absorb that. let s_ack = server.process(c2.dgram(), now());
assert!(s_ack.dgram().is_none());
fn connect_and_receive_settings() -> (Http3Server, Connection) { // Create a server and connect it to a client. // We will have a http3 server on one side and a neqo_transport // connection on the other side so that we can check what the http3 // side sends and also to simulate an incorrectly behaving http3 // client.
letmut server = default_server(); let client = connect_and_receive_settings_with_server(&mut server);
(server, client)
}
// Test http3 connection inintialization. // The server will open the control and qpack streams and send SETTINGS frame. #[test] fn server_connect() {
mem::drop(connect_and_receive_settings());
}
impl PeerConnection { /// A shortcut for sending on the control stream. fn control_send(&mutself, data: &[u8]) { let res = self.conn.stream_send(self.control_stream_id, data);
assert_eq!(res, Ok(data.len()));
}
}
impl Deref for PeerConnection { type Target = Connection; fn deref(&self) -> &Self::Target {
&self.conn
}
}
fn connect() -> (Http3Server, PeerConnection) { letmut server = default_server(); let client = connect_to(&mut server);
(server, client)
}
// Server: Test receiving a new control stream and a SETTINGS frame. #[test] fn server_receive_control_frame() {
mem::drop(connect());
}
// Server: Test that the connection will be closed if control stream // has been closed. #[test] fn server_close_control_stream() { let (mut hconn, mut peer_conn) = connect(); let control = peer_conn.control_stream_id;
peer_conn.stream_close_send(control).unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
// Server: test missing SETTINGS frame // (the first frame sent is a MAX_PUSH_ID frame). #[test] fn server_missing_settings() { let (mut hconn, mut neqo_trans_conn) = connect_and_receive_settings(); // Create client control stream. let control_stream = neqo_trans_conn.stream_create(StreamType::UniDi).unwrap(); // Send a MAX_PUSH_ID frame instead. let sent = neqo_trans_conn.stream_send(control_stream, &[0x0, 0xd, 0x1, 0xf]);
assert_eq!(sent, Ok(4)); let out = neqo_trans_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpMissingSettings);
}
// Server: receiving SETTINGS frame twice causes connection close // with error HTTP_UNEXPECTED_FRAME. #[test] fn server_receive_settings_twice() { let (mut hconn, mut peer_conn) = connect(); // send the second SETTINGS frame.
peer_conn.control_send(&[0x4, 0x6, 0x1, 0x40, 0x64, 0x7, 0x40, 0x64]); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpFrameUnexpected);
}
fn priority_update_check_id(stream_id: StreamId, valid: bool) { let (mut hconn, mut peer_conn) = connect(); // send a priority update let frame = HFrame::PriorityUpdateRequest {
element_id: stream_id.as_u64(),
priority: Priority::default(),
}; letmut e = Encoder::default();
frame.encode(&mut e);
peer_conn.control_send(e.as_ref()); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now()); // check if the given connection got closed on invalid stream ids if valid {
assert_not_closed(&hconn);
} else {
assert_closed(&hconn, &Error::HttpId);
}
}
// receive a frame that is not allowed on the control stream.
peer_conn.control_send(v);
let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpFrameUnexpected);
}
// send DATA frame on a control stream #[test] fn server_data_frame_on_control_stream() {
test_wrong_frame_on_control_stream(&[0x0, 0x2, 0x1, 0x2]);
}
// send HEADERS frame on a cortrol stream #[test] fn server_headers_frame_on_control_stream() {
test_wrong_frame_on_control_stream(&[0x1, 0x2, 0x1, 0x2]);
}
// send PUSH_PROMISE frame on a cortrol stream #[test] fn server_push_promise_frame_on_control_stream() {
test_wrong_frame_on_control_stream(&[0x5, 0x2, 0x1, 0x2]);
}
// Server: receive unknown stream type // also test getting stream id that does not fit into a single byte. #[test] fn server_received_unknown_stream() { let (mut hconn, mut peer_conn) = connect();
// create a stream with unknown type. let new_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap();
_ = peer_conn
.stream_send(new_stream_id, &[0x41, 0x19, 0x4, 0x4, 0x6, 0x0, 0x8, 0x0])
.unwrap(); let out = peer_conn.process_output(now()); let out = hconn.process(out.dgram(), now());
mem::drop(peer_conn.process(out.dgram(), now())); let out = hconn.process_output(now());
mem::drop(peer_conn.process(out.dgram(), now()));
// Server: receiving a push stream on a server should cause WrongStreamDirection #[test] fn server_received_push_stream() { let (mut hconn, mut peer_conn) = connect();
// create a push stream. let push_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap();
_ = peer_conn.stream_send(push_stream_id, &[0x1]).unwrap(); let out = peer_conn.process_output(now()); let out = hconn.process(out.dgram(), now());
mem::drop(peer_conn.conn.process(out.dgram(), now()));
assert_closed(&hconn, &Error::HttpStreamCreation);
}
/// Test reading of a slowly streamed frame. bytes are received one by one #[test] fn server_frame_reading() { let (mut hconn, mut peer_conn) = connect_and_receive_settings();
// create a control stream. let control_stream = peer_conn.stream_create(StreamType::UniDi).unwrap();
// send the stream type letmut sent = peer_conn.stream_send(control_stream, &[0x0]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
// start sending SETTINGS frame
sent = peer_conn.stream_send(control_stream, &[0x4]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x4]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x6]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x0]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x8]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x0]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_not_closed(&hconn);
// Now test PushPromise
sent = peer_conn.stream_send(control_stream, &[0x5]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x5]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x4]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x61]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x62]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x63]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
sent = peer_conn.stream_send(control_stream, &[0x64]);
assert_eq!(sent, Ok(1)); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
// PUSH_PROMISE on a control stream will cause an error
assert_closed(&hconn, &Error::HttpFrameUnexpected);
}
// Test reading of a slowly streamed frame. bytes are received one by one fn test_incomplete_frame(res: &[u8]) { let (mut hconn, mut peer_conn) = connect_and_receive_settings();
// send an incomplete reequest. let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap();
peer_conn.stream_send(stream_id, res).unwrap();
peer_conn.stream_close_send(stream_id).unwrap();
let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpFrame);
}
const REQUEST_WITH_BODY: &[u8] = &[ // headers 0x01, 0x10, 0x00, 0x00, 0xd1, 0xd7, 0x50, 0x89, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x2e, 0x43, 0xd3, 0xc1, // the first data frame. 0x0, 0x3, 0x61, 0x62, 0x63, // the second data frame. 0x0, 0x3, 0x64, 0x65, 0x66,
]; const REQUEST_BODY: &[u8] = &[0x61, 0x62, 0x63, 0x64, 0x65, 0x66];
let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); // Send only request headers for now.
peer_conn
.stream_send(stream_id, &REQUEST_WITH_BODY[..20])
.unwrap();
let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
// Check connection event. There should be 1 Header and no data events. letmut headers_frames = 0; whilelet Some(event) = hconn.next_event() { match event {
Http3ServerEvent::Headers {
stream,
headers,
fin,
} => {
check_request_header(&headers);
assert!(!fin);
headers_frames += 1;
stream
.stream_stop_sending(Error::HttpNoError.code())
.unwrap();
stream
.send_headers(&[
Header::new(":status", "200"),
Header::new("content-length", "3"),
])
.unwrap();
stream.send_data(RESPONSE_BODY).unwrap();
}
Http3ServerEvent::Data { .. } => {
panic!("We should not have a Data event");
}
Http3ServerEvent::DataWritable { .. }
| Http3ServerEvent::StreamReset { .. }
| Http3ServerEvent::StreamStopSending { .. }
| Http3ServerEvent::StateChange { .. }
| Http3ServerEvent::PriorityUpdate { .. }
| Http3ServerEvent::WebTransport(_) => {}
}
} let out = hconn.process_output(now());
let request_stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); // Send only request headers for now.
peer_conn
.stream_send(request_stream_id, &REQUEST_WITH_BODY[..20])
.unwrap();
let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
// Check connection event. There should be 1 Header and no data events. // The server will reset the stream. letmut headers_frames = 0; whilelet Some(event) = hconn.next_event() { match event {
Http3ServerEvent::Headers {
stream,
headers,
fin,
} => {
check_request_header(&headers);
assert!(!fin);
headers_frames += 1;
stream
.cancel_fetch(Error::HttpRequestRejected.code())
.unwrap();
}
Http3ServerEvent::Data { .. } => {
panic!("We should not have a Data event");
}
Http3ServerEvent::DataWritable { .. }
| Http3ServerEvent::StreamReset { .. }
| Http3ServerEvent::StreamStopSending { .. }
| Http3ServerEvent::StateChange { .. }
| Http3ServerEvent::PriorityUpdate { .. }
| Http3ServerEvent::WebTransport(_) => {}
}
} let out = hconn.process_output(now());
let out = peer_conn.process(out.dgram(), now());
hconn.process(out.dgram(), now());
// Server: Test that the connection will be closed if the local control stream // has been reset. #[test] fn server_reset_control_stream() { let (mut hconn, mut peer_conn) = connect();
peer_conn
.stream_reset_send(CLIENT_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code())
.unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
// Server: Test that the connection will be closed if the client side encoder stream // has been reset. #[test] fn server_reset_client_side_encoder_stream() { let (mut hconn, mut peer_conn) = connect();
peer_conn
.stream_reset_send(CLIENT_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code())
.unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
// Server: Test that the connection will be closed if the client side decoder stream // has been reset. #[test] fn server_reset_client_side_decoder_stream() { let (mut hconn, mut peer_conn) = connect();
peer_conn
.stream_reset_send(CLIENT_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code())
.unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
// Server: Test that the connection will be closed if the local control stream // has received a stop_sending. #[test] fn client_stop_sending_control_stream() { let (mut hconn, mut peer_conn) = connect();
peer_conn
.stream_stop_sending(SERVER_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code())
.unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
// Server: Test that the connection will be closed if the server side encoder stream // has received a stop_sending. #[test] fn server_stop_sending_encoder_stream() { let (mut hconn, mut peer_conn) = connect();
peer_conn
.stream_stop_sending(SERVER_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code())
.unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
// Server: Test that the connection will be closed if the server side decoder stream // has received a stop_sending. #[test] fn server_stop_sending_decoder_stream() { let (mut hconn, mut peer_conn) = connect();
peer_conn
.stream_stop_sending(SERVER_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code())
.unwrap(); let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
assert_closed(&hconn, &Error::HttpClosedCriticalStream);
}
/// Perform a handshake, then another with the token from the first. /// The second should always resume, but it might not always accept early data. fn zero_rtt_with_settings(conn_params: Http3Parameters, zero_rtt: ZeroRttState) { let (_, mut client) = connect(); let token = client.events().find_map(|e| { iflet ConnectionEvent::ResumptionToken(token) = e {
Some(token)
} else {
None
}
});
assert!(token.is_some());
letmut server = create_server(conn_params); letmut client = default_client();
client.enable_resumption(now(), token.unwrap()).unwrap();
/// More blocked streams does not prevent 0-RTT. #[test] fn zero_rtt_more_blocked_streams() {
zero_rtt_with_settings(
http3params(QpackSettings {
max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams + 1,
..DEFAULT_SETTINGS
}),
ZeroRttState::AcceptedClient,
);
}
/// A lower number of blocked streams also prevents 0-RTT. #[test] fn zero_rtt_fewer_blocked_streams() {
zero_rtt_with_settings(
http3params(QpackSettings {
max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams - 1,
..DEFAULT_SETTINGS
}),
ZeroRttState::Rejected,
);
}
/// The size of the encoder table is local and therefore doesn't prevent 0-RTT. #[test] fn zero_rtt_smaller_encoder_table() {
zero_rtt_with_settings(
http3params(QpackSettings {
max_table_size_encoder: DEFAULT_SETTINGS.max_table_size_encoder - 1,
..DEFAULT_SETTINGS
}),
ZeroRttState::AcceptedClient,
);
}
let request_stream_id_1 = peer_conn.stream_create(StreamType::BiDi).unwrap(); // Send only request headers for now.
peer_conn
.stream_send(request_stream_id_1, REQUEST_WITH_BODY)
.unwrap();
let request_stream_id_2 = peer_conn.stream_create(StreamType::BiDi).unwrap(); // Send only request headers for now.
peer_conn
.stream_send(request_stream_id_2, REQUEST_WITH_BODY)
.unwrap();
let out = peer_conn.process_output(now());
hconn.process(out.dgram(), now());
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.