pub struct MultipartParser<B: AsMut<[u8]> + AsRef<[u8]>> { /* private fields */ }Expand description
The sans-IO multipart/form-data parser.
Feed it bytes as they arrive, in chunks of any size split at any byte;
it returns one MultipartEvent per call and how many bytes it consumed. The
events are identical for every split of a body that parses; for a body
that fails, the error is identical, though a split body may emit data
before it that a whole one does not. It never allocates after
construction. The delimiter search skips 32 bytes at a time to a line
break followed by - and compares the delimiter there a word at a time;
bytes that may start a delimiter at a chunk’s end are carried and
scanned again with the next chunk (at most 73 per boundary), so the
search stays linear. The streaming automaton (a Knuth-Morris-Pratt
table) only carries that state between chunks.
The buffer holds the header block, the decoded field name, the decoded
filename* (under AcceptAlone), the token, and the _charset_ value:
Limits::multipart_buffer_len bytes. It is wiped on reset and on
drop.
§Examples
use form_net::{MultipartEvent, FormKind, Limits, MultipartParser, Policy};
let content_type = b"multipart/form-data; boundary=X";
let body = b"--X\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\nHoliday\r\n--X--\r\n";
let FormKind::Multipart(boundary) = FormKind::detect(content_type)? else { unreachable!() };
let mut parser = MultipartParser::new(boundary, &Limits::DEFAULT, &Policy::DEFAULT)?;
let mut events = Vec::new();
let mut chunks = body.chunks(7).peekable(); // any split gives the same events
while let Some(chunk) = chunks.next() {
let last = chunks.peek().is_none();
let mut rest = chunk;
loop {
let step = parser.parse(rest, last)?;
rest = &rest[step.consumed..];
match step.event {
MultipartEvent::PartStart(head) => events.push(format!("start {}", head.name())),
MultipartEvent::Data(bytes) => events.push(format!("data {}", bytes.len())),
MultipartEvent::PartEnd(summary) => events.push(format!("end {}", summary.bytes)),
MultipartEvent::End => break,
MultipartEvent::NeedInput => break,
_ => {}
}
}
}
assert!(parser.is_done());
assert_eq!(events.first().map(String::as_str), Some("start title"));
assert_eq!(events.last().map(String::as_str), Some("end 7"));Implementations§
Source§impl MultipartParser<Vec<u8>>
impl MultipartParser<Vec<u8>>
Sourcepub fn new(
boundary: Boundary,
limits: &Limits,
policy: &Policy,
) -> Result<Self, FormError>
Available on crate feature alloc only.
pub fn new( boundary: Boundary, limits: &Limits, policy: &Policy, ) -> Result<Self, FormError>
alloc only.A parser with its own buffer of
Limits::multipart_buffer_len bytes, allocated once.
§Errors
InvalidLimits from validating the limits with the policy.
§Examples
use form_net::{Boundary, Limits, MultipartParser, Policy};
let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &Policy::DEFAULT)?;
assert_eq!(parser.bytes_seen(), 0);Source§impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B>
impl<B: AsMut<[u8]> + AsRef<[u8]>> MultipartParser<B>
Sourcepub fn with_buffer(
boundary: Boundary,
limits: &Limits,
policy: &Policy,
buffer: B,
) -> Result<Self, FormError>
pub fn with_buffer( boundary: Boundary, limits: &Limits, policy: &Policy, buffer: B, ) -> Result<Self, FormError>
A parser over a buffer the caller provides; it never allocates.
§Errors
InvalidLimits; BufferTooSmall when buffer is shorter than
Limits::multipart_buffer_len.
§Examples
use form_net::{Boundary, Limits, MultipartParser, Policy};
const LIMITS: Limits = Limits::DEFAULT.with_max_part_header_bytes(1024);
const LEN: usize = LIMITS.multipart_buffer_len(&Policy::DEFAULT);
let mut buffer = [0u8; LEN];
let parser = MultipartParser::with_buffer(Boundary::new(b"X")?, &LIMITS, &Policy::DEFAULT, &mut buffer[..])?;Sourcepub fn parse<'p>(
&'p mut self,
input: &'p [u8],
eof: bool,
) -> Result<MultipartStep<'p>, FormError>
pub fn parse<'p>( &'p mut self, input: &'p [u8], eof: bool, ) -> Result<MultipartStep<'p>, FormError>
Consumes a prefix of input and returns one event. eof = true says
input ends the body.
§Errors
Every multipart error (see the crate’s docs/API.md); Poisoned
after any error until reset.
§Examples
let body = b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--";
// The first 10 bytes are not a whole header block: give more.
let step = parser.parse(&body[..10], false)?;
assert!(matches!(step.event, MultipartEvent::NeedInput));
let rest = &body[step.consumed..];
let step = parser.parse(rest, true)?;
assert!(matches!(step.event, MultipartEvent::PartStart(head) if head.name() == "a"));Sourcepub fn skip_part(&mut self)
pub fn skip_part(&mut self)
Discards the rest of the current part: its bytes are consumed and
counted against the budgets but produce no Data events, and its
summary has skipped = true. Outside a part it does nothing.
§Examples
let step = parser.parse(body, true)?;
let mut rest = &body[step.consumed..];
parser.skip_part();
let summary = loop {
let step = parser.parse(rest, true)?;
rest = &rest[step.consumed..];
assert!(!matches!(step.event, MultipartEvent::Data(_)));
if let MultipartEvent::PartEnd(summary) = step.event {
break summary;
}
};
assert!(summary.skipped);Sourcepub fn reset(&mut self, boundary: Boundary)
pub fn reset(&mut self, boundary: Boundary)
Returns the parser to its initial state for a new body, wipes its buffer, and clears the poisoned state. It never allocates.
§Examples
assert!(parser.parse(b"no delimiter here", true).is_err());
// Poisoned until reset; reset also takes the next body's boundary.
assert!(parser.parse(b"", true).is_err());
parser.reset(Boundary::new(b"Y")?);
let body = b"--Y\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--Y--";
assert!(matches!(parser.parse(body, true)?.event, MultipartEvent::PartStart(_)));Sourcepub const fn bytes_seen(&self) -> u64
pub const fn bytes_seen(&self) -> u64
Body bytes consumed so far.
§Examples
let consumed = parser.parse(body, true)?.consumed;
assert_eq!(parser.bytes_seen(), consumed as u64);Sourcepub const fn parts_seen(&self) -> u32
pub const fn parts_seen(&self) -> u32
Sourcepub const fn files_seen(&self) -> u32
pub const fn files_seen(&self) -> u32
Sourcepub fn is_done(&self) -> bool
pub fn is_done(&self) -> bool
Whether the close delimiter was read.
§Examples
let mut rest = &b"--X\r\nContent-Disposition: form-data; name=a\r\n\r\n1\r\n--X--"[..];
assert!(!parser.is_done());
while !parser.is_done() {
let step = parser.parse(rest, true)?;
rest = &rest[step.consumed..];
}
// The close delimiter was read.
assert!(rest.is_empty());Sourcepub const fn limits(&self) -> &Limits
pub const fn limits(&self) -> &Limits
The limits the parser was built with.
§Examples
use form_net::{Boundary, Limits, MultipartParser, Policy};
let limits = Limits::DEFAULT.with_max_part_header_bytes(1024);
let parser = MultipartParser::new(Boundary::new(b"X")?, &limits, &Policy::DEFAULT)?;
assert_eq!(parser.limits(), &limits);Sourcepub const fn policy(&self) -> &Policy
pub const fn policy(&self) -> &Policy
The policy the parser was built with.
§Examples
use form_net::{Boundary, Limits, MultipartParser, Policy, TokenField};
let policy = Policy::DEFAULT.with_token_field(TokenField::new("csrf"));
let parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &policy)?;
assert_eq!(parser.policy(), &policy);Sourcepub fn captured_token(&self) -> Option<&str>
pub fn captured_token(&self) -> Option<&str>
The token field’s value, once its part has ended.
§Examples
use form_net::{Boundary, Limits, MultipartParser, Policy, TokenField};
let policy = Policy::DEFAULT.with_token_field(TokenField::new("csrf"));
let mut parser = MultipartParser::new(Boundary::new(b"X")?, &Limits::DEFAULT, &policy)?;
let mut rest = &b"--X\r\nContent-Disposition: form-data; name=csrf\r\n\r\nabc123\r\n--X--"[..];
while !parser.is_done() {
let step = parser.parse(rest, true)?;
rest = &rest[step.consumed..];
}
assert_eq!(parser.captured_token(), Some("abc123"));Sourcepub fn declared_charset(&self) -> Option<&[u8]>
pub fn declared_charset(&self) -> Option<&[u8]>
The value of the last complete _charset_ field.
§Examples
let mut rest = &b"--X\r\nContent-Disposition: form-data; name=_charset_\r\n\r\nutf-8\r\n--X--"[..];
while !parser.is_done() {
let step = parser.parse(rest, true)?;
rest = &rest[step.consumed..];
}
assert_eq!(parser.declared_charset(), Some(&b"utf-8"[..]));