From 9310ec08bd0a46f6b128678cefa4ff861eaa3bfe Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 17 Sep 2020 14:09:46 +0200 Subject: openpgp: Improve parsing examples to demonstrate body handling. - Demonstrate dropping, buffering of all bodies, buffering of individual bodies, and streaming. - Fixes #540. --- openpgp/src/parse.rs | 132 ++++++++++++++++++++++++++-- openpgp/src/parse/packet_pile_parser.rs | 147 +++++++++++++++++++++++++++++--- 2 files changed, 258 insertions(+), 21 deletions(-) (limited to 'openpgp') diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs index de43c1c4..d2ac00c4 100644 --- a/openpgp/src/parse.rs +++ b/openpgp/src/parse.rs @@ -3027,7 +3027,15 @@ impl PacketParserState { /// /// # Examples /// -/// Parse an OpenPGP message using a `PacketParser`: +/// These examples demonstrate how to process packet bodies by parsing +/// the simplest possible OpenPGP message containing just a single +/// literal data packet with the body "Hello world.". There are three +/// options. First, the body can be dropped. Second, it can be +/// buffered. Lastly, the body can be streamed. In general, +/// streaming should be preferred, because it avoids buffering in +/// Sequoia. +/// +/// This example demonstrates simply ignoring the packet body: /// /// ```rust /// # fn main() -> sequoia_openpgp::Result<()> { @@ -3035,19 +3043,127 @@ impl PacketParserState { /// use openpgp::Packet; /// use openpgp::parse::{Parse, PacketParserResult, PacketParser}; /// -/// let message_data: &[u8] = // ... -/// # include_bytes!("../tests/data/keys/public-key.gpg"); -/// let mut ppr = PacketParser::from_bytes(message_data)?; +/// // By default, the `PacketParser` will drop packet bodies. +/// let mut ppr = +/// PacketParser::from_bytes(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?; +/// while let PacketParserResult::Some(pp) = ppr { +/// // Get the packet out of the parser and start parsing the next +/// // packet, recursing. +/// let (packet, next_ppr) = pp.recurse()?; +/// ppr = next_ppr; +/// +/// // Process the packet. +/// if let Packet::Literal(literal) = packet { +/// // The body was dropped. +/// assert_eq!(literal.body(), b""); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// } +/// # Ok(()) } +/// ``` +/// +/// This example demonstrates how the body can be buffered by +/// configuring the `PacketParser` to buffer all packet bodies: +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; +/// use openpgp::parse::{Parse, PacketParserResult, PacketParserBuilder}; +/// +/// // By default, the `PacketParser` will drop packet bodies. Use a +/// // `PacketParserBuilder` to change that. +/// let mut ppr = +/// PacketParserBuilder::from_bytes( +/// b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")? +/// .buffer_unread_content() +/// .build()?; +/// while let PacketParserResult::Some(pp) = ppr { +/// // Get the packet out of the parser and start parsing the next +/// // packet, recursing. +/// let (packet, next_ppr) = pp.recurse()?; +/// ppr = next_ppr; +/// +/// // Process the packet. +/// if let Packet::Literal(literal) = packet { +/// // The body was buffered. +/// assert_eq!(literal.body(), b"Hello world."); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// } +/// # Ok(()) } +/// ``` +/// +/// This example demonstrates how the body can be buffered by +/// buffering an individual packet: +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; +/// use openpgp::parse::{Parse, PacketParserResult, PacketParser}; +/// +/// // By default, the `PacketParser` will drop packet bodies. +/// let mut ppr = +/// PacketParser::from_bytes(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?; /// while let PacketParserResult::Some(mut pp) = ppr { +/// if let Packet::Literal(_) = pp.packet { +/// // Buffer this packet's body. +/// pp.buffer_unread_content()?; +/// } +/// +/// // Get the packet out of the parser and start parsing the next +/// // packet, recursing. +/// let (packet, next_ppr) = pp.recurse()?; +/// ppr = next_ppr; +/// /// // Process the packet. +/// if let Packet::Literal(literal) = packet { +/// // The body was buffered. +/// assert_eq!(literal.body(), b"Hello world."); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// } +/// # Ok(()) } +/// ``` +/// +/// This example demonstrates how to stream the packet body: +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// use std::io::Read; /// +/// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; +/// use openpgp::parse::{Parse, PacketParserResult, PacketParser}; +/// +/// let mut ppr = +/// PacketParser::from_bytes(b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?; +/// while let PacketParserResult::Some(mut pp) = ppr { /// if let Packet::Literal(_) = pp.packet { -/// // Stream the content of any literal packets to stdout. -/// std::io::copy(&mut pp, &mut std::io::stdout()); +/// // Stream the body. +/// let mut buf = Vec::new(); +/// pp.read_to_end(&mut buf)?; +/// assert_eq!(buf, b"Hello world."); +/// } else { +/// unreachable!("We know it is a literal packet."); /// } /// -/// // Start parsing the next packet, recursing. -/// ppr = pp.recurse()?.1; +/// // Get the packet out of the parser and start parsing the next +/// // packet, recursing. +/// let (packet, next_ppr) = pp.recurse()?; +/// ppr = next_ppr; +/// +/// // Process the packet. +/// if let Packet::Literal(literal) = packet { +/// // The body was streamed, not buffered. +/// assert_eq!(literal.body(), b""); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } /// } /// # Ok(()) } /// ``` diff --git a/openpgp/src/parse/packet_pile_parser.rs b/openpgp/src/parse/packet_pile_parser.rs index 78483b82..fe1e989c 100644 --- a/openpgp/src/parse/packet_pile_parser.rs +++ b/openpgp/src/parse/packet_pile_parser.rs @@ -44,28 +44,145 @@ use buffered_reader::BufferedReader; /// /// # Examples /// +/// These examples demonstrate how to process packet bodies by parsing +/// the simplest possible OpenPGP message containing just a single +/// literal data packet with the body "Hello world.". There are three +/// options. First, the body can be dropped. Second, it can be +/// buffered. Lastly, the body can be streamed. In general, +/// streaming should be preferred, because it avoids buffering in +/// Sequoia. +/// +/// This example demonstrates simply ignoring the packet body: +/// /// ```rust /// # fn main() -> sequoia_openpgp::Result<()> { /// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; /// use openpgp::parse::{Parse, PacketPileParser}; /// -/// // Parse a message. -/// let message_data: &[u8] = // ... -/// # include_bytes!("../../tests/data/keys/public-key.gpg"); -/// # let mut n = 0; -/// let mut ppp = PacketPileParser::from_bytes(message_data)?; -/// while let Ok(pp) = ppp.as_ref() { -/// eprintln!("{:?}", pp); +/// // By default, the `PacketPileParser` will drop packet bodies. +/// let mut ppp = +/// PacketPileParser::from_bytes( +/// b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?; +/// while ppp.is_some() { +/// // Start parsing the next packet, recursing. /// ppp.recurse()?; -/// # n += 1; /// } /// /// let pile = ppp.finish(); -/// pile.pretty_print(); -/// # assert_eq!(n, 61); -/// # assert_eq!(pile.children().len(), 61); -/// # Ok(()) -/// # } +/// // Process the packet. +/// if let Some(Packet::Literal(literal)) = pile.path_ref(&[0]) { +/// // The body was dropped. +/// assert_eq!(literal.body(), b""); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// # Ok(()) } +/// ``` +/// +/// This example demonstrates how the body can be buffered by +/// configuring the `PacketPileParser` to buffer all packet bodies: +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// use std::convert::TryFrom; +/// +/// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; +/// use openpgp::parse::{Parse, PacketPileParser, PacketParserBuilder}; +/// +/// // By default, the `PacketPileParser` will drop packet bodies. +/// // Use a `PacketParserBuilder` to change that. +/// let mut ppb = +/// PacketParserBuilder::from_bytes( +/// b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")? +/// .buffer_unread_content(); +/// let mut ppp = PacketPileParser::try_from(ppb)?; +/// while ppp.is_some() { +/// // Start parsing the next packet, recursing. +/// ppp.recurse()?; +/// } +/// +/// let pile = ppp.finish(); +/// // Process the packet. +/// if let Some(Packet::Literal(literal)) = pile.path_ref(&[0]) { +/// // The body was buffered. +/// assert_eq!(literal.body(), b"Hello world."); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// # Ok(()) } +/// ``` +/// +/// This example demonstrates how the body can be buffered by +/// buffering an individual packet: +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; +/// use openpgp::parse::{Parse, PacketPileParser}; +/// +/// // By default, the `PacketPileParser` will drop packet bodies. +/// let mut ppp = +/// PacketPileParser::from_bytes( +/// b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?; +/// while let Ok(pp) = ppp.as_mut() { +/// if let Packet::Literal(_) = pp.packet { +/// // Buffer this packet's body. +/// pp.buffer_unread_content()?; +/// } +/// +/// // Start parsing the next packet, recursing. +/// ppp.recurse()?; +/// } +/// +/// let pile = ppp.finish(); +/// // Process the packet. +/// if let Some(Packet::Literal(literal)) = pile.path_ref(&[0]) { +/// // The body was buffered. +/// assert_eq!(literal.body(), b"Hello world."); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// # Ok(()) } +/// ``` +/// +/// This example demonstrates how to stream the packet body: +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// use std::io::Read; +/// +/// use sequoia_openpgp as openpgp; +/// use openpgp::Packet; +/// use openpgp::parse::{Parse, PacketPileParser}; +/// +/// // By default, the `PacketPileParser` will drop packet bodies. +/// let mut ppp = +/// PacketPileParser::from_bytes( +/// b"\xcb\x12b\x00\x00\x00\x00\x00Hello world.")?; +/// while let Ok(pp) = ppp.as_mut() { +/// if let Packet::Literal(_) = pp.packet { +/// // Stream the body. +/// let mut buf = Vec::new(); +/// pp.read_to_end(&mut buf)?; +/// assert_eq!(buf, b"Hello world."); +/// } +/// +/// // Start parsing the next packet, recursing. +/// ppp.recurse()?; +/// } +/// +/// let pile = ppp.finish(); +/// // Process the packet. +/// if let Some(Packet::Literal(literal)) = pile.path_ref(&[0]) { +/// // The body was streamed, not buffered. +/// assert_eq!(literal.body(), b""); +/// } else { +/// unreachable!("We know it is a literal packet."); +/// } +/// # Ok(()) } /// ``` #[derive(Debug)] pub struct PacketPileParser<'a> { @@ -394,6 +511,8 @@ fn test_recurse() -> Result<()> { ppp.recurse().unwrap(); } assert_eq!(count, 61); + let pp = ppp.finish(); + assert_eq!(pp.children().count(), 61); Ok(()) } @@ -407,6 +526,8 @@ fn test_next() -> Result<()> { ppp.next().unwrap(); } assert_eq!(count, 61); + let pp = ppp.finish(); + assert_eq!(pp.children().count(), 61); Ok(()) } -- cgit v1.2.3