138 lines
3.2 KiB
Rust
138 lines
3.2 KiB
Rust
use winnow::combinator::seq;
|
|
use winnow::{
|
|
ascii::line_ending, combinator::repeat, prelude::*, stream::Partial, token::take_while,
|
|
};
|
|
|
|
pub(crate) type Stream<'i> = Partial<&'i [u8]>;
|
|
|
|
#[rustfmt::skip]
|
|
#[derive(Debug)]
|
|
#[allow(dead_code)]
|
|
pub(crate) struct Request<'a> {
|
|
method: &'a [u8],
|
|
uri: &'a [u8],
|
|
version: &'a [u8],
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
#[allow(dead_code)]
|
|
pub(crate) struct Header<'a> {
|
|
name: &'a [u8],
|
|
value: Vec<&'a [u8]>,
|
|
}
|
|
|
|
pub(crate) fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
|
|
let mut buf = Partial::new(data);
|
|
let mut v = Vec::new();
|
|
loop {
|
|
match request(&mut buf) {
|
|
Ok(r) => {
|
|
v.push(r);
|
|
|
|
if buf.is_empty() {
|
|
//println!("{}", i);
|
|
break;
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("error: {e:?}");
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
|
|
Some(v)
|
|
}
|
|
|
|
fn request<'s>(input: &mut Stream<'s>) -> ModalResult<(Request<'s>, Vec<Header<'s>>)> {
|
|
let req = request_line(input)?;
|
|
let h = repeat(1.., message_header).parse_next(input)?;
|
|
let _ = line_ending.parse_next(input)?;
|
|
|
|
Ok((req, h))
|
|
}
|
|
|
|
fn request_line<'s>(input: &mut Stream<'s>) -> ModalResult<Request<'s>> {
|
|
seq!( Request {
|
|
method: take_while(1.., is_token),
|
|
_: take_while(1.., is_space),
|
|
uri: take_while(1.., is_not_space),
|
|
_: take_while(1.., is_space),
|
|
version: http_version,
|
|
_: line_ending,
|
|
})
|
|
.parse_next(input)
|
|
}
|
|
|
|
fn http_version<'s>(input: &mut Stream<'s>) -> ModalResult<&'s [u8]> {
|
|
let _ = "HTTP/".parse_next(input)?;
|
|
let version = take_while(1.., is_version).parse_next(input)?;
|
|
|
|
Ok(version)
|
|
}
|
|
|
|
fn message_header_value<'s>(input: &mut Stream<'s>) -> ModalResult<&'s [u8]> {
|
|
let _ = take_while(1.., is_horizontal_space).parse_next(input)?;
|
|
let data = take_while(1.., till_line_ending).parse_next(input)?;
|
|
let _ = line_ending.parse_next(input)?;
|
|
|
|
Ok(data)
|
|
}
|
|
|
|
fn message_header<'s>(input: &mut Stream<'s>) -> ModalResult<Header<'s>> {
|
|
seq!(Header {
|
|
name: take_while(1.., is_token),
|
|
_: ':',
|
|
value: repeat(1.., message_header_value),
|
|
})
|
|
.parse_next(input)
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
#[allow(clippy::match_same_arms)]
|
|
#[allow(clippy::match_like_matches_macro)]
|
|
fn is_token(c: u8) -> bool {
|
|
match c {
|
|
128..=255 => false,
|
|
0..=31 => false,
|
|
b'(' => false,
|
|
b')' => false,
|
|
b'<' => false,
|
|
b'>' => false,
|
|
b'@' => false,
|
|
b',' => false,
|
|
b';' => false,
|
|
b':' => false,
|
|
b'\\' => false,
|
|
b'"' => false,
|
|
b'/' => false,
|
|
b'[' => false,
|
|
b']' => false,
|
|
b'?' => false,
|
|
b'=' => false,
|
|
b'{' => false,
|
|
b'}' => false,
|
|
b' ' => false,
|
|
_ => true,
|
|
}
|
|
}
|
|
|
|
fn is_version(c: u8) -> bool {
|
|
c.is_ascii_digit() || c == b'.'
|
|
}
|
|
|
|
fn till_line_ending(c: u8) -> bool {
|
|
c != b'\r' && c != b'\n'
|
|
}
|
|
|
|
fn is_space(c: u8) -> bool {
|
|
c == b' '
|
|
}
|
|
|
|
fn is_not_space(c: u8) -> bool {
|
|
c != b' '
|
|
}
|
|
|
|
fn is_horizontal_space(c: u8) -> bool {
|
|
c == b' ' || c == b'\t'
|
|
}
|