Skip to content

Commit

Permalink
h3i: extract connection creation to its own function
Browse files Browse the repository at this point in the history
  • Loading branch information
evanrittenhouse committed Nov 22, 2024
1 parent 57bdafd commit 3f22174
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 88 deletions.
84 changes: 83 additions & 1 deletion h3i/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ use qlog::events::h3::HttpHeader;
use quiche::ConnectionError;

use std::collections::HashMap;
use std::net::SocketAddr;
use std::time::Instant;

use crate::actions::h3::Action;
use crate::actions::h3::StreamEvent;
use crate::actions::h3::StreamEventType;
use crate::config::Config;
use crate::frame::H3iFrame;
use crate::frame::ResetStream;
use crate::frame_parser::FrameParseResult;
Expand All @@ -55,10 +57,90 @@ use qlog::events::h3::Http3Frame;
use qlog::events::EventData;
use qlog::streamer::QlogStreamer;

use quiche;
use quiche::h3::frame::Frame as QFrame;
use quiche::h3::Error;
use quiche::h3::NameValue;
use quiche::Connection;
use quiche::Result;
use quiche::{self};

const MAX_DATAGRAM_SIZE: usize = 1350;
const QUIC_VERSION: u32 = 1;

pub fn build_quiche_connection(
args: Config, peer_addr: SocketAddr, local_addr: SocketAddr,
) -> Result<Connection> {
// We'll only connect to one server.
let connect_url = if !args.omit_sni {
args.host_port.split(':').next()
} else {
None
};

// Create the configuration for the QUIC connection.
let mut config = quiche::Config::new(QUIC_VERSION).unwrap();

config.verify_peer(args.verify_peer);
config.set_application_protos(&[b"h3"]).unwrap();
config.set_max_idle_timeout(args.idle_timeout);
config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_initial_max_data(10_000_000);
config
.set_initial_max_stream_data_bidi_local(args.max_stream_data_bidi_local);
config.set_initial_max_stream_data_bidi_remote(
args.max_stream_data_bidi_remote,
);
config.set_initial_max_stream_data_uni(args.max_stream_data_uni);
config.set_initial_max_streams_bidi(args.max_streams_bidi);
config.set_initial_max_streams_uni(args.max_streams_uni);
config.set_disable_active_migration(true);
config.set_active_connection_id_limit(0);

config.set_max_connection_window(args.max_window);
config.set_max_stream_window(args.max_stream_window);

let mut keylog = None;

if let Some(keylog_path) = std::env::var_os("SSLKEYLOGFILE") {
let file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(keylog_path)
.unwrap();

keylog = Some(file);

config.log_keys();
}

config.grease(false);

// Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut scid);

let scid = quiche::ConnectionId::from_ref(&scid);

// Create a QUIC connection and initiate handshake.
let mut conn =
quiche::connect(connect_url, &scid, local_addr, peer_addr, &mut config)?;

if let Some(keylog) = &mut keylog {
if let Ok(keylog) = keylog.try_clone() {
conn.set_keylog(Box::new(keylog));
}
}

log::info!(
"connecting to {:} from {:} with scid {:?}",
peer_addr,
local_addr,
scid,
);

Ok(conn)
}

fn handle_qlog(
qlog_streamer: Option<&mut QlogStreamer>, qlog_frame: Http3Frame,
Expand Down
92 changes: 8 additions & 84 deletions h3i/src/client/sync_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,27 @@ use crate::actions::h3::Action;
use crate::actions::h3::StreamEventType;
use crate::actions::h3::WaitType;
use crate::actions::h3::WaitingFor;
use crate::client::build_quiche_connection;
use crate::client::execute_action;
use crate::client::parse_streams;
use crate::client::ClientError;
use crate::client::ClientVariant;
use crate::client::ConnectionCloseDetails;
use crate::client::MAX_DATAGRAM_SIZE;
use crate::config::Config;

use super::ConnectionSummary;
use super::StreamMap;
use super::StreamParserMap;

const MAX_DATAGRAM_SIZE: usize = 1350;

/// Connect to a server and execute provided actions.
///
/// Constructs a socket and [quiche::Connection] based on the provided `args`,
/// then iterates over `actions`.
///
/// Returns a [ConnectionSummary] on success, [ClientError] on failure.
pub fn connect(
args: &Config, actions: &[Action],
args: Config, actions: &[Action],
) -> std::result::Result<ConnectionSummary, ClientError> {
let mut buf = [0; 65535];
let mut out = [0; MAX_DATAGRAM_SIZE];
Expand All @@ -66,13 +66,6 @@ pub fn connect(
let mut poll = mio::Poll::new().unwrap();
let mut events = mio::Events::with_capacity(1024);

// We'll only connect to one server.
let connect_url = if !args.omit_sni {
args.host_port.split(':').next()
} else {
None
};

// Resolve server address.
let peer_addr = if let Some(addr) = &args.connect_to {
addr.parse().expect("--connect-to is expected to be a string containing an IPv4 or IPv6 address with a port. E.g. 192.0.2.0:443")
Expand Down Expand Up @@ -102,84 +95,15 @@ pub fn connect(
.register(&mut socket, mio::Token(0), mio::Interest::READABLE)
.unwrap();

// Create the configuration for the QUIC connection.
let mut config = quiche::Config::new(1).unwrap();

config.verify_peer(args.verify_peer);
config.set_application_protos(&[b"h3"]).unwrap();
config.set_max_idle_timeout(args.idle_timeout);
config.set_max_recv_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_max_send_udp_payload_size(MAX_DATAGRAM_SIZE);
config.set_initial_max_data(10_000_000);
config
.set_initial_max_stream_data_bidi_local(args.max_stream_data_bidi_local);
config.set_initial_max_stream_data_bidi_remote(
args.max_stream_data_bidi_remote,
);
config.set_initial_max_stream_data_uni(args.max_stream_data_uni);
config.set_initial_max_streams_bidi(args.max_streams_bidi);
config.set_initial_max_streams_uni(args.max_streams_uni);
config.set_disable_active_migration(true);
config.set_active_connection_id_limit(0);

config.set_max_connection_window(args.max_window);
config.set_max_stream_window(args.max_stream_window);

let mut keylog = None;

if let Some(keylog_path) = std::env::var_os("SSLKEYLOGFILE") {
let file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(keylog_path)
.unwrap();

keylog = Some(file);

config.log_keys();
}
let Ok(local_addr) = socket.local_addr() else {
return Err(ClientError::Other("invalid socket".to_string()));
};

config.grease(false);
let mut conn = build_quiche_connection(args, peer_addr, local_addr)
.map_err(|_| ClientError::HandshakeFail)?;

let mut app_proto_selected = false;

// Generate a random source connection ID for the connection.
let mut scid = [0; quiche::MAX_CONN_ID_LEN];
rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut scid);

let scid = quiche::ConnectionId::from_ref(&scid);

let local_addr = socket.local_addr().unwrap();

// Create a QUIC connection and initiate handshake.
let mut conn =
quiche::connect(connect_url, &scid, local_addr, peer_addr, &mut config)
.unwrap();

if let Some(keylog) = &mut keylog {
if let Ok(keylog) = keylog.try_clone() {
conn.set_keylog(Box::new(keylog));
}
}

if let Some(dir) = std::env::var_os("QLOGDIR") {
let id = format!("{scid:?}");
let writer = make_qlog_writer(&dir, "client", &id);

conn.set_qlog(
std::boxed::Box::new(writer),
"h3i-client qlog".to_string(),
format!("{} id={}", "quiche-client qlog", id),
);
}

log::info!(
"connecting to {:} from {:} with scid {:?}",
peer_addr,
socket.local_addr().unwrap(),
scid,
);

let (write, send_info) = conn.send(&mut out).expect("initial send failed");

while let Err(e) = socket.send_to(&out[..write], send_info.to) {
Expand Down
6 changes: 3 additions & 3 deletions h3i/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn main() -> Result<(), ClientError> {
None => prompt_frames(&config),
};

match sync_client(&config, &actions) {
match sync_client(config, &actions) {
Ok(summary) => {
log::debug!(
"received connection_summary: {}",
Expand Down Expand Up @@ -283,9 +283,9 @@ fn config_from_clap() -> std::result::Result<Config, String> {
}

fn sync_client(
config: &Config, actions: &[Action],
config: Config, actions: &[Action],
) -> Result<ConnectionSummary, ClientError> {
h3i::client::sync_client::connect(&config.library_config, actions)
h3i::client::sync_client::connect(config.library_config, actions)
}

fn read_qlog(filename: &str) -> Vec<Action> {
Expand Down

0 comments on commit 3f22174

Please sign in to comment.