pulse_http 0.2.0

Async HTTP/1.1 framework on Tokio — raw TCP, routing, middleware, no Hyper
Documentation
use crate::{
    errors::{FormError, MultipartError},
    headers::Headers,
    helpers::{
        parse_multipart::{Multipart, parse_multipart},
        parse_urlencoded::parse_urlencoded,
        split_path_and_query::split_path_and_query,
    },
    state::State,
};
use serde::de::DeserializeOwned;
use std::{collections::HashMap, net::IpAddr, str::from_utf8};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Method {
    Get,
    Post,
    Put,
    Delete,
    Head,
    Options,
}

impl Method {
    pub fn parse(raw: &str) -> Option<Self> {
        match raw {
            "GET" => Some(Self::Get),
            "POST" => Some(Self::Post),
            "PUT" => Some(Self::Put),
            "DELETE" => Some(Self::Delete),
            "HEAD" => Some(Self::Head),
            "OPTIONS" => Some(Self::Options),
            _ => None,
        }
    }

    pub fn as_str(self) -> &'static str {
        match self {
            Self::Get => "GET",
            Self::Post => "POST",
            Self::Put => "PUT",
            Self::Delete => "DELETE",
            Self::Head => "HEAD",
            Self::Options => "OPTIONS",
        }
    }
}

#[derive(Debug)]
pub struct Request {
    pub method: Method,
    pub path: String,
    pub query: HashMap<String, String>,
    pub headers: HashMap<String, String>,
    pub params: HashMap<String, String>,
    pub body: Vec<u8>,
    pub http_version: String,
    pub state: State,
    pub ip_addr: IpAddr,
}

impl Request {
    pub fn from_headers(
        headers: Headers,
        body: Vec<u8>,
        state: State,
        ip_addr: IpAddr,
    ) -> Option<Self> {
        let method = Method::parse(&headers.method)?;
        let (path, query) = split_path_and_query(&headers.path);

        Some(Self {
            method,
            path,
            query,
            headers: headers.fields,
            params: HashMap::new(),
            body,
            http_version: headers.http_version,
            state,
            ip_addr,
        })
    }

    pub fn json<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
        serde_json::from_slice(&self.body)
    }

    pub fn form(&self) -> Result<HashMap<String, String>, FormError> {
        let body = from_utf8(&self.body).map_err(|_| FormError::InvalidUtf8)?;
        parse_urlencoded(body)
    }

    pub fn multipart(&self) -> Result<Multipart, MultipartError> {
        let content_type = self
            .headers
            .get("content-type")
            .ok_or(MultipartError::MissingContentType)?;
        parse_multipart(&self.body, content_type)
    }
}