mod support;
use h3wire::{Config, Event, FieldRef, HeadersKind, StreamId, UsageError};
use support::wire::frame;
use support::{Pair, Seen, Side, feed_all, server_ready};
const S0: StreamId = StreamId(0);
const S4: StreamId = StreamId(4);
const CLIENT_CONTROL: StreamId = StreamId(2);
fn f(name: &'static str, value: &'static str) -> FieldRef<'static> {
FieldRef::new(name.as_bytes(), value.as_bytes())
}
fn get() -> Vec<FieldRef<'static>> {
vec![
f(":method", "GET"),
f(":scheme", "https"),
f(":authority", "a"),
f(":path", "/"),
]
}
fn kinds(seen: &[Seen], s: StreamId) -> Vec<HeadersKind> {
seen.iter()
.filter_map(|e| match e {
Seen::Headers { stream, kind, .. } if *stream == s => Some(*kind),
_ => None,
})
.collect()
}
fn status(seen: &[Seen]) -> Vec<Vec<u8>> {
seen.iter()
.filter_map(|e| match e {
Seen::Headers { fields, .. } => fields
.iter()
.find(|(n, _, _)| n == b":status")
.map(|(_, v, _)| v.clone()),
_ => None,
})
.collect()
}
#[test]
fn ext_connect_1xx_then_2xx() {
let mut server_cfg = Config::default();
server_cfg.enable_connect_protocol = true;
let mut p = Pair::new(Config::default(), server_cfg);
p.run_to_completion_resolving();
let connect = [
f(":method", "CONNECT"),
f(":protocol", "websocket"),
f(":scheme", "https"),
f(":authority", "a"),
f(":path", "/chat"),
];
p.client.send_headers(S0, &connect, false).unwrap();
p.run_to_completion_resolving();
assert_eq!(kinds(&p.seen_by(Side::Server), S0), [HeadersKind::Request]);
p.server
.send_headers(S0, &[f(":status", "100")], false)
.unwrap();
p.run_to_completion_resolving();
p.server
.send_headers(S0, &[f(":status", "200")], false)
.unwrap();
p.run_to_completion_resolving();
let client = p.seen_by(Side::Client);
assert_eq!(
kinds(&client, S0),
[HeadersKind::Informational, HeadersKind::Response]
);
assert_eq!(status(&client), [b"100".to_vec(), b"200".to_vec()]);
p.send_body(Side::Client, S0, b"ping", false);
p.send_body(Side::Server, S0, b"pong!", false);
p.run_to_completion_resolving();
assert_eq!(p.bodies[&(Side::Server, S0)], b"ping");
assert_eq!(p.bodies[&(Side::Client, S0)], b"pong!");
assert_eq!(
p.client.send_headers(S0, &[f("x-t", "1")], true),
Err(UsageError::WrongPhase)
);
assert!(p.closed.is_empty());
}
#[test]
fn goaway_both_directions_in_one_pair() {
let mut p = Pair::new(Config::default(), Config::default());
p.run_to_completion_resolving();
p.client.send_headers(S0, &get(), true).unwrap();
p.run_to_completion_resolving();
p.client.start_shutdown().unwrap();
p.server.start_shutdown().unwrap();
p.run_to_completion_resolving();
p.server.finish_shutdown().unwrap();
p.run_to_completion_resolving();
let goaways = |side| -> Vec<u64> {
p.events(side)
.into_iter()
.filter_map(|e| match e {
Event::GoAway { id } => Some(id),
_ => None,
})
.collect()
};
assert_eq!(goaways(Side::Server), [0]);
assert_eq!(goaways(Side::Client), [(1 << 62) - 4, 4]);
p.server
.send_headers(S0, &[f(":status", "200")], true)
.unwrap();
p.run_to_completion_resolving();
assert_eq!(p.events(Side::Client).last(), Some(&Event::Finished(S0)));
assert_eq!(
p.client.send_headers(S4, &get(), true),
Err(UsageError::GoingAway)
);
assert!(p.closed.is_empty());
}
#[test]
fn write_blocked_stream_in_pair() {
let mut p = Pair::new(Config::default(), Config::default());
p.opts.write_blocked.insert((Side::Client, CLIENT_CONTROL));
p.client.send_headers(S0, &get(), true).unwrap();
p.run_to_completion_resolving();
assert_eq!(kinds(&p.seen_by(Side::Server), S0), [HeadersKind::Request]);
assert!(p.server.peer_settings().is_none());
p.opts.write_blocked.clear();
p.server
.send_headers(S0, &[f(":status", "200")], true)
.unwrap();
p.run_to_completion_resolving();
assert_eq!(p.events(Side::Client).last(), Some(&Event::Finished(S0)));
assert!(p.events(Side::Server).contains(&Event::Finished(S0)));
assert_eq!(p.events(Side::Server).last(), Some(&Event::PeerSettings));
assert!(p.closed.is_empty());
}
#[test]
fn memory_bound_holds_after_release() {
let mut section = vec![0x00, 0x00, 0xd1, 0xd7, 0xc1, 0x50, 0x01, 0x61];
section.resize(65_536, 0xdd);
let wire = frame(0x01, §ion);
let mut c = server_ready(Config::default());
let ids: Vec<StreamId> = (0..7).map(|i| StreamId(4 * i)).collect();
for &s in &ids {
feed_all(&mut c, s, &wire, false);
}
let blocks: Vec<_> = std::iter::from_fn(|| c.poll_event())
.filter_map(|e| match e {
Event::Headers { block, .. } => Some(block),
_ => None,
})
.collect();
assert_eq!(blocks.len(), 7);
assert!(c.poll_action().is_none());
assert!(c.debug_buffered_bytes() >= 7 * 65_536);
assert!(c.debug_buffered_bytes() <= c.debug_bound());
for b in blocks {
c.release(b);
}
assert!(c.debug_buffered_bytes() <= c.debug_bound());
}
#[test]
fn memory_bound_does_not_grow_with_exchanges() {
let mut p = Pair::new(Config::default(), Config::default());
p.run_to_completion_resolving();
let mut bounds = Vec::new();
for i in 0..200u64 {
let s = StreamId(4 * i);
p.client.send_headers(s, &get(), true).unwrap();
p.run_to_completion_resolving();
p.server
.send_headers(s, &[f(":status", "200")], false)
.unwrap();
p.run_to_completion_resolving();
p.send_body(Side::Server, s, b"hello", true);
p.run_to_completion_resolving();
for c in [&p.client, &p.server] {
assert!(c.debug_buffered_bytes() <= c.debug_bound());
}
if i == 9 || i == 199 {
bounds.push((p.client.debug_bound(), p.server.debug_bound()));
}
}
assert!(p.closed.is_empty());
assert_eq!(
bounds[0], bounds[1],
"debug_bound grew with the exchange count"
);
}