From b965f84d8514a5a5908bc2b05f2f2516a1236c60 Mon Sep 17 00:00:00 2001 From: Alexander Rogovskyy Date: Thu, 14 Mar 2024 17:25:30 +0100 Subject: [PATCH] add option to parse borrowed data --- ipp/src/parser.rs | 101 +++++++++++++++++++++++++++++++++++++++++++--- ipp/src/reader.rs | 27 ++++++++++--- 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/ipp/src/parser.rs b/ipp/src/parser.rs index b0bb063d..efdbb5a6 100644 --- a/ipp/src/parser.rs +++ b/ipp/src/parser.rs @@ -18,7 +18,7 @@ use crate::{ reader::IppReader, request::IppRequestResponse, value::IppValue, - FromPrimitive as _, + FromPrimitive as _, IppHeader, }; /// Parse error enum @@ -149,7 +149,7 @@ pub struct AsyncIppParser { #[cfg(feature = "async")] impl AsyncIppParser where - R: 'static + AsyncRead + Send + Sync + Unpin, + R: AsyncRead + Send + Sync + Unpin, { /// Create IPP parser from AsyncIppReader pub fn new(reader: T) -> AsyncIppParser @@ -170,8 +170,7 @@ where self.state.parse_value(tag, name, value) } - /// Parse IPP stream - pub async fn parse(mut self) -> Result { + async fn parse_header_attributes(&mut self) -> Result { let header = self.reader.read_header().await?; trace!("IPP header: {:?}", header); @@ -189,6 +188,22 @@ where } } + Ok(header) + } + + /// Parse IPP stream without reading beyond the end of the attributes. The payload stays untouched. + pub async fn parse_parts(mut self) -> Result<(IppHeader, IppAttributes, AsyncIppReader), IppParseError> { + let header = self.parse_header_attributes().await?; + Ok((header, self.state.attributes, self.reader)) + } + + /// Parse IPP stream + pub async fn parse(mut self) -> Result + where + R: 'static, + { + let header = self.parse_header_attributes().await?; + Ok(IppRequestResponse { header, attributes: self.state.attributes, @@ -226,8 +241,7 @@ where self.state.parse_value(tag, name, value) } - /// Parse IPP stream - pub fn parse(mut self) -> Result { + fn parse_header_attributes(&mut self) -> Result { let header = self.reader.read_header()?; trace!("IPP header: {:?}", header); @@ -245,6 +259,22 @@ where } } + Ok(header) + } + + /// Parse IPP stream without reading beyond the end of the attributes. The payload stays untouched. + pub fn parse_parts(mut self) -> Result<(IppHeader, IppAttributes, IppReader), IppParseError> { + let header = self.parse_header_attributes()?; + Ok((header, self.state.attributes, self.reader)) + } + + /// Parse IPP stream + pub fn parse(mut self) -> Result + where + R: 'static, + { + let header = self.parse_header_attributes()?; + Ok(IppRequestResponse { header, attributes: self.state.attributes, @@ -255,6 +285,8 @@ where #[cfg(test)] mod tests { + use crate::prelude::IppVersion; + use super::*; #[cfg(feature = "async")] @@ -361,6 +393,7 @@ mod tests { assert!(result.is_ok()); let res = result.ok().unwrap(); + assert_eq!(res.header.version, IppVersion::v1_1()); let attrs = res .attributes .groups_of(DelimiterTag::PrinterAttributes) @@ -375,6 +408,35 @@ mod tests { assert_eq!(cursor.into_inner(), b"foo"); } + #[cfg(feature = "async")] + #[tokio::test] + async fn test_async_parse_parts() { + let data = vec![ + 1, 1, 0, 0, 0, 0, 0, 0, 4, 0x21, 0x00, 0x04, b't', b'e', b's', b't', 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, 3, + b'f', b'o', b'o', + ]; + + let result = AsyncIppParser::new(AsyncIppReader::new(futures_util::io::Cursor::new(data))) + .parse_parts() + .await; + assert!(result.is_ok()); + + let (header, attributes, reader) = result.ok().unwrap(); + assert_eq!(header.version, IppVersion::v1_1()); + let attrs = attributes + .groups_of(DelimiterTag::PrinterAttributes) + .next() + .unwrap() + .attributes(); + let attr = attrs.get("test").unwrap(); + assert_eq!(attr.value().as_integer(), Some(&0x1234_5678)); + + let mut payload = reader.into_payload(); + let mut cursor = io::Cursor::new(Vec::new()); + io::copy(&mut payload, &mut cursor).unwrap(); + assert_eq!(cursor.into_inner(), b"foo"); + } + #[test] fn test_parse_no_attributes() { let data = &[1, 1, 0, 0, 0, 0, 0, 0, 3]; @@ -464,6 +526,7 @@ mod tests { assert!(result.is_ok()); let mut res = result.ok().unwrap(); + assert_eq!(res.header.version, IppVersion::v1_1()); let attrs = res .attributes .groups_of(DelimiterTag::PrinterAttributes) @@ -478,6 +541,32 @@ mod tests { assert_eq!(cursor.into_inner(), b"foo"); } + #[test] + fn test_parse_parts() { + let data = vec![ + 1, 1, 0, 0, 0, 0, 0, 0, 4, 0x21, 0x00, 0x04, b't', b'e', b's', b't', 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, 3, + b'f', b'o', b'o', + ]; + + let result = IppParser::new(IppReader::new(io::Cursor::new(data))).parse_parts(); + assert!(result.is_ok()); + + let (header, attributes, reader) = result.ok().unwrap(); + assert_eq!(header.version, IppVersion::v1_1()); + let attrs = attributes + .groups_of(DelimiterTag::PrinterAttributes) + .next() + .unwrap() + .attributes(); + let attr = attrs.get("test").unwrap(); + assert_eq!(attr.value().as_integer(), Some(&0x1234_5678)); + + let mut payload = reader.into_payload(); + let mut cursor = io::Cursor::new(Vec::new()); + io::copy(&mut payload, &mut cursor).unwrap(); + assert_eq!(cursor.into_inner(), b"foo"); + } + #[test] fn test_parse_groups() { let data = vec![ diff --git a/ipp/src/reader.rs b/ipp/src/reader.rs index 709bac73..85864ca7 100644 --- a/ipp/src/reader.rs +++ b/ipp/src/reader.rs @@ -19,7 +19,7 @@ pub struct AsyncIppReader { #[cfg(feature = "async")] impl AsyncIppReader where - R: 'static + AsyncRead + Send + Sync + Unpin, + R: AsyncRead + Send + Sync + Unpin, { /// Create IppReader from AsyncRead instance pub fn new(inner: R) -> Self { @@ -83,7 +83,10 @@ where } /// Convert the remaining inner stream into IppPayload - pub fn into_payload(self) -> IppPayload { + pub fn into_payload(self) -> IppPayload + where + R: 'static, + { IppPayload::new_async(self.inner) } } @@ -91,7 +94,7 @@ where #[cfg(feature = "async")] impl From for AsyncIppReader where - R: 'static + AsyncRead + Send + Sync + Unpin, + R: AsyncRead + Send + Sync + Unpin, { fn from(r: R) -> Self { AsyncIppReader::new(r) @@ -105,7 +108,7 @@ pub struct IppReader { impl IppReader where - R: 'static + Read + Send + Sync, + R: Read + Send + Sync, { /// Create IppReader from Read instance pub fn new(inner: R) -> Self { @@ -167,14 +170,17 @@ where } /// Convert the remaining inner stream into IppPayload - pub fn into_payload(self) -> IppPayload { + pub fn into_payload(self) -> IppPayload + where + R: 'static, + { IppPayload::new(self.inner) } } impl From for IppReader where - R: 'static + Read + Send + Sync, + R: Read + Send + Sync, { fn from(r: R) -> Self { IppReader::new(r) @@ -202,6 +208,15 @@ mod tests { assert_eq!(value.as_ref(), b"test"); } + #[test] + fn test_read_borrowed_value() { + let data = vec![0x00, 0x04, b't', b'e', b's', b't']; + let data = io::Cursor::new(&data); + let mut reader = IppReader::new(data); + let value = reader.read_value().unwrap(); + assert_eq!(value.as_ref(), b"test"); + } + #[test] fn test_read_header() { let data = io::Cursor::new(vec![0x01, 0x01, 0x04, 0x01, 0x11, 0x22, 0x33, 0x44]);