Skip to content

Commit

Permalink
Add a spec to 4_1_frame_format, cf. #175
Browse files Browse the repository at this point in the history
  • Loading branch information
fasterthanlime committed Jun 1, 2024
1 parent b1a1eb3 commit 7aea83f
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ cov:

# Run all tests with cargo nextest
test *args:
just build-testbed
just build-testbed httpwg-gen
export RUST_BACKTRACE="${RUST_BACKTRACE:-1}"
cargo nextest run {{args}}

Expand Down
11 changes: 11 additions & 0 deletions crates/httpwg-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ macro_rules! tests {
}
}

/// Section 4.1: Frame Format
mod _4_1_frame_format {
use super::__suite::_4_1_frame_format as __group;

#[test]
fn sends_frame_with_unknown_type() {
use __group::sends_frame_with_unknown_type as test;
$body
}
}

/// Section 4.2: Frame Size
mod _4_2_frame_size {
use super::__suite::_4_2_frame_size as __group;
Expand Down
37 changes: 31 additions & 6 deletions crates/httpwg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use fluke_buffet::{IntoHalves, Piece, PieceList, Roll, RollMut, WriteOwned};
use fluke_h2_parse::{
enumflags2,
nom::{self, Finish},
DataFlags, Frame, FrameType, GoAway, HeadersFlags, IntoPiece, KnownErrorCode, RstStream,
Settings, SettingsFlags, StreamId, PREFACE,
DataFlags, Frame, FrameType, GoAway, HeadersFlags, IntoPiece, KnownErrorCode, PingFlags,
RstStream, Settings, SettingsFlags, StreamId, PREFACE,
};
use tokio::time::Instant;
use tracing::debug;
use tracing::{debug, trace};

use crate::rfc9113::default_settings;

Expand Down Expand Up @@ -180,7 +180,7 @@ impl<IO: IntoHalves> Conn<IO> {
debug!("reached EOF");
eof = true;
} else {
debug!(%n, "read bytes (reading frame header)");
trace!(%n, "read bytes (reading frame header)");
}
}

Expand All @@ -201,7 +201,7 @@ impl<IO: IntoHalves> Conn<IO> {
let res;
(res, res_buf) = res_buf.read_into(16384, &mut r).await;
let n = res?;
debug!(%n, len = %res_buf.len(), "read bytes (reading frame payload)");
trace!(%n, len = %res_buf.len(), "read bytes (reading frame payload)");

if n == 0 {
eof = true;
Expand All @@ -220,7 +220,7 @@ impl<IO: IntoHalves> Conn<IO> {
};
assert_eq!(payload.len(), frame_len);

debug!(%frame_len, "got frame payload");
trace!(%frame_len, "got frame payload");
ev_tx.send(Ev::Frame { frame, payload }).await.unwrap();
}
Err(nom::Err::Incomplete(_)) => {
Expand Down Expand Up @@ -266,6 +266,19 @@ impl<IO: IntoHalves> Conn<IO> {
Ok(())
}

pub async fn write_ping(&mut self, ack: bool, payload: impl IntoPiece) -> eyre::Result<()> {
self.write_frame(
FrameType::Ping(if ack {
PingFlags::Ack.into()
} else {
Default::default()
})
.into_frame(StreamId::CONNECTION),
payload,
)
.await
}

pub async fn write_settings(&mut self, settings: Settings) -> eyre::Result<()> {
self.write_frame(
FrameType::Settings(Default::default()).into_frame(StreamId::CONNECTION),
Expand Down Expand Up @@ -318,6 +331,18 @@ impl<IO: IntoHalves> Conn<IO> {
}
}

/// Waits for a PING frame with Ack flag and the specified payload.
/// It will NOT ignore other PING frames, if the first frame it
/// receives doesn't have the expected payload, it will return an error.
pub async fn verify_ping_frame_with_ack(&mut self, payload: &[u8]) -> eyre::Result<()> {
let (frame, received_payload) = self.wait_for_frame(FrameT::Ping).await.unwrap();
assert!(frame.is_ack(), "expected PING frame to have ACK flag");

assert_eq!(received_payload, payload, "unexpected PING payload");

Ok(())
}

pub async fn handshake(&mut self) -> eyre::Result<()> {
// perform an HTTP/2 handshake as a client
self.w.write_all_owned(PREFACE).await?;
Expand Down
38 changes: 38 additions & 0 deletions crates/httpwg/src/rfc9113/_4_1_frame_format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Section 4.1: Frame Format

use fluke_buffet::IntoHalves;
use fluke_h2_parse::{EncodedFrameType, Frame, FrameType, StreamId};

use crate::Conn;

// Type: The 8-bit type of the frame. The frame type determines
// the format and semantics of the frame. Implementations MUST
// ignore and discard any frame that has a type that is unknown.
pub async fn sends_frame_with_unknown_type<IO: IntoHalves + 'static>(
mut conn: Conn<IO>,
) -> eyre::Result<()> {
conn.handshake().await?;

conn.write_frame(
Frame {
frame_type: FrameType::Unknown(EncodedFrameType {
ty: 0xff,
flags: 0x0,
}),
len: 8,
reserved: 0,
stream_id: StreamId::CONNECTION,
},
b"0".repeat(8),
)
.await?;

let data = b"pingback";
tracing::info!("writing PING");
conn.write_ping(false, &data[..]).await?;
tracing::info!("verifying PING ACK");
conn.verify_ping_frame_with_ack(data).await?;
tracing::info!("verified!");

Ok(())
}
1 change: 1 addition & 0 deletions crates/httpwg/src/rfc9113/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ pub fn default_settings() -> Settings {
}

pub mod _3_starting_http2;
pub mod _4_1_frame_format;
pub mod _4_2_frame_size;

0 comments on commit 7aea83f

Please sign in to comment.