use crate::http_chunk::HttpChunk;
use crate::http_header::HttpHeader;
use crate::http_method::HttpMethod;
use crate::http_request::HttpRequest;
use crate::http_response::HttpResponse;
use crate::http_response_status::HttpResponseStatus;
use crate::http_version::HttpVersion;
use nom::{
bytes::streaming::{tag, take, take_until, take_while},
character::is_alphanumeric,
combinator::{fail, iterator, map_res},
error::Error,
sequence::terminated,
IResult,
};
use std::str::FromStr;
fn parse_method(i: &[u8]) -> IResult<&[u8], HttpMethod> {
let (i, method_au8) = take_while(is_alphanumeric)(i)?;
let (_, method_s) = map_res(take(method_au8.len()), std::str::from_utf8)(method_au8)?;
let method = HttpMethod::from_str(method_s).unwrap();
let (i, _) = take(1u8)(i)?;
Ok((i, method))
}
fn parse_path(i: &[u8]) -> IResult<&[u8], String> {
let (i, path_au8) = take_until(" ")(i)?;
let path = path_au8.iter().map(|&b| b as char).collect();
let (i, _) = take(1u8)(i)?;
Ok((i, path))
}
fn parse_version(i: &[u8]) -> IResult<&[u8], HttpVersion> {
let (i, version_au8) = take_until("\r\n")(i)?;
let (_, version_s) = map_res(take(version_au8.len()), std::str::from_utf8)(version_au8)?;
let version = HttpVersion::from_str(version_s).unwrap();
let (i, _) = take(2u8)(i)?;
Ok((i, version))
}
fn parse_header(i: &[u8]) -> IResult<&[u8], HttpHeader> {
let (value_au8, name_au8) = take_until(": ")(i)?;
let name = name_au8.iter().map(|&b| b as char).collect();
let (value_au8, _) = take(2u8)(value_au8)?;
let value: String = value_au8.iter().map(|&b| b as char).collect();
let header = HttpHeader::new(name, value);
Ok((value_au8, header))
}
fn parse_headers(i: &[u8]) -> IResult<&[u8], Vec<HttpHeader>> {
let (rest, headers_au8) = terminated(take_until("\r\n\r\n"), tag("\r\n"))(i)?;
let headers_au8 = [headers_au8, rest].concat();
let mut headers_iter = iterator(
&headers_au8[..],
terminated(take_until("\r\n"), tag::<_, _, Error<_>>("\r\n")),
);
let headers: Vec<HttpHeader> = headers_iter
.map(parse_header)
.filter_map(|r| r.ok())
.map(|v| v.1)
.collect();
Ok((&rest[2..], headers)) }
pub fn parse_request(i: &[u8], ssl: bool) -> IResult<&[u8], HttpRequest> {
let copy = i;
let (i, method) = parse_method(i)?;
let (i, path) = parse_path(i)?;
let (i, version) = parse_version(i)?;
let (body_au8, headers) = parse_headers(i)?;
let mut body = Vec::with_capacity(body_au8.len());
body.extend_from_slice(body_au8);
let head_len = copy.len() - body_au8.len();
let mut raw = Vec::with_capacity(head_len);
raw.extend_from_slice(©[..head_len]);
let r = HttpRequest::new(method, path, version, headers, body, raw, ssl);
Ok((body_au8, r))
}
fn parse_status_version(i: &[u8]) -> IResult<&[u8], HttpVersion> {
let (i, version_au8) = take_until(" ")(i)?;
let (_, version_s) = map_res(take(version_au8.len()), std::str::from_utf8)(version_au8)?;
let version = HttpVersion::from_str(version_s).unwrap();
let (i, _) = take(1u8)(i)?; Ok((i, version))
}
fn parse_status_code(i: &[u8]) -> IResult<&[u8], usize> {
let (i, code_au8) = take_until(" ")(i)?;
let (_, code_s) = map_res(take(code_au8.len()), std::str::from_utf8)(code_au8)?;
let code = code_s.parse::<usize>().unwrap();
let (i, _) = take(1u8)(i)?; Ok((i, code))
}
fn parse_status_text(i: &[u8]) -> IResult<&[u8], Vec<u8>> {
let (i, msg_au8) = take_until("\r\n")(i)?;
let msg: Vec<u8> = msg_au8.to_vec();
let (i, _) = take(2u8)(i)?;
Ok((i, msg))
}
fn parse_status_line(i: &[u8]) -> IResult<&[u8], HttpResponseStatus> {
let (i, version) = parse_status_version(i)?;
let (i, code) = parse_status_code(i)?;
let (i, msg) = parse_status_text(i)?;
let rstatus = HttpResponseStatus::new(version, code, msg);
Ok((i, rstatus))
}
pub fn parse_response(i: &[u8]) -> IResult<&[u8], HttpResponse> {
let copy = i;
let (i, status_line) = parse_status_line(i)?;
let (body_au8, headers) = parse_headers(i)?;
let body: Vec<u8> = body_au8.to_vec();
let head_len = copy.len() - body_au8.len();
let mut raw = Vec::with_capacity(head_len);
raw.extend_from_slice(©[..head_len]);
let r = HttpResponse::new(status_line, headers, body, raw);
Ok((body_au8, r))
}
pub fn parse_chunk_header(i: &[u8]) -> IResult<&[u8], usize> {
let (i, header_au8) = take_until("\r\n")(i)?;
let (_, size_s) = map_res(take(header_au8.len()), std::str::from_utf8)(header_au8)?;
let size = usize::from_str_radix(size_s, 16).unwrap();
let (i, _) = take(2u8)(i)?;
Ok((i, size))
}
pub fn take_chunk_body(i: &[u8], size: usize) -> IResult<&[u8], Vec<u8>> {
let (i, chunkbody) = take(size)(i)?;
let (i, _) = take(2u8)(i)?;
let mut body = Vec::with_capacity(chunkbody.len());
body.extend_from_slice(chunkbody);
body.extend_from_slice(&[0x0d, 0x0a]);
Ok((i, body))
}
pub fn parse_chunk(i: &[u8]) -> IResult<&[u8], HttpChunk> {
let (rest, chunksize) = parse_chunk_header(i)?;
let (rest, chunkbody) = take_chunk_body(rest, chunksize)?;
let chunk_raw = &i[..i.len() - rest.len()];
let chunk = HttpChunk::new(chunk_raw.to_vec(), chunkbody, chunksize);
Ok((rest, chunk))
}
pub fn take_all_chunks(mut i: &[u8]) -> IResult<&[u8], Vec<HttpChunk>> {
if i.is_empty() {
fail::<_, &[u8], _>(i)?;
}
let mut chunks: Vec<HttpChunk> = vec![];
let mut chunk;
while !i.is_empty() {
(i, chunk) = parse_chunk(i)?;
chunks.push(chunk);
}
Ok((i, chunks))
}