use std::marker::PhantomData;
use std::str::FromStr;
use anyhow::Error;
use bytes::Bytes;
use futures::StreamExt;
use http_body_util::{Empty, Full, StreamBody};
use http_body_util::{BodyExt, combinators::BoxBody};
use hyper::{HeaderMap, Uri};
use hyper::body::Frame;
use hyper::header::{HeaderName, HeaderValue};
use hyper::{Request, body::Incoming};
use crate::common::stream::ByteStream;
pub struct Final;
pub struct SetMethod;
#[derive(Debug)]
pub struct HttpRequest {
body: BoxBody<Bytes, Error>,
parts: hyper::http::request::Parts,
params: Vec<(String, String)>,
}
impl HttpRequest {
pub fn builder() -> HttpRequestBuilder<SetMethod> {
HttpRequestBuilder {
builder: Request::builder(),
uri_string: None,
_state: PhantomData
}
}
pub fn from_parts(body: BoxBody<Bytes, Error>, parts: hyper::http::request::Parts) -> HttpRequest {
HttpRequest {
body,
parts,
params: Vec::new()
}
}
pub fn from_parts_with_params(body: BoxBody<Bytes, Error>, parts: hyper::http::request::Parts, params: Vec<(String, String)>) -> HttpRequest {
HttpRequest {
body,
parts,
params
}
}
pub fn body(self) -> ByteStream {
let stream = self.body.into_data_stream();
ByteStream::new(stream)
}
pub fn method(&self) -> &str {
self.parts.method.as_str()
}
pub fn host(&self) -> Option<&str> {
self.parts.uri.host()
}
pub fn path(&self) -> &str {
self.parts.uri.path()
}
pub fn port(&self) -> Option<u16> {
self.parts.uri.port_u16()
}
pub fn scheme(&self) -> Option<&str> {
self.parts.uri.scheme_str()
}
pub fn add_header(&mut self, key: impl AsRef<str>, value: impl AsRef<str>) -> anyhow::Result<()> {
let key = HeaderName::from_str(key.as_ref())?;
let value = value.as_ref().parse()?;
self.parts.headers.insert(key, value);
Ok(())
}
pub fn remove_header(&mut self, key: impl AsRef<str>) {
self.parts.headers.remove(key.as_ref());
}
pub fn header(&self, key: impl AsRef<str>) -> Option<&HeaderValue> {
self.parts.headers.get(key.as_ref())
}
pub fn headers(&self) -> &HeaderMap {
&self.parts.headers
}
pub fn param(&self, key: impl AsRef<str>) -> Option<&str> {
self.params.iter().find(|(k, _)| k == key.as_ref()).map(|(_, v)| v.as_str())
}
pub fn params(&self) -> impl Iterator<Item = (&str, &str)> {
self.params.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
}
pub struct HttpRequestBuilder<State> {
builder: hyper::http::request::Builder,
uri_string: Option<String>,
_state: PhantomData<State>
}
impl HttpRequestBuilder<SetMethod> {
pub fn get(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("GET");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn post(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("POST");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn put(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("PUT");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn patch(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("PATCH");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn delete(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("DELETE");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn options(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("OPTIONS");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn head(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("OPTIONS");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn connect(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("CONNECT");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
pub fn trace(mut self, uri: impl Into<String>) -> HttpRequestBuilder<Final> {
self.builder = self.builder.method("TRACE");
HttpRequestBuilder {
builder: self.builder,
uri_string: Some(uri.into()),
_state: PhantomData
}
}
}
impl HttpRequestBuilder<Final> {
pub fn body_empty(self) -> anyhow::Result<HttpRequest> {
let uri: Uri = self.uri_string.unwrap().parse()?;
let body = Empty::new().map_err(|e| match e {}).boxed();
let request = self.builder.uri(&uri).header("Host", uri.host().unwrap()).body(body)?;
Ok(HttpRequest::from(request))
}
pub fn body_bytes(self, body: impl Into<Bytes>) -> anyhow::Result<HttpRequest> {
let uri: Uri = self.uri_string.unwrap().parse()?;
let body = Full::from(body.into()).map_err(|e| match e {}).boxed();
let request = self.builder.uri(&uri).header("Host", uri.host().unwrap()).body(body)?;
Ok(HttpRequest::from(request))
}
pub fn body_stream(self, stream: ByteStream) -> anyhow::Result<HttpRequest> {
let uri: Uri = self.uri_string.unwrap().parse()?;
let mapped_stream = stream.inner_stream().map(|res| { res.map(Frame::data) });
let body = StreamBody::new(mapped_stream);
let boxed_body: BoxBody<Bytes, anyhow::Error> = BodyExt::boxed(body);
let request: Request<BoxBody<Bytes, Error>> = self.builder.uri(&uri).header("Host", uri.host().unwrap()).body(boxed_body)?;
Ok(HttpRequest::from(request))
}
pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.builder = self.builder.header(key.into(), value.into());
self
}
pub fn headers(mut self, headers: &HeaderMap) -> Self {
for (key, value) in headers {
self.builder = self.builder.header(key, value);
}
self
}
}
impl From<HttpRequest> for Request<BoxBody<Bytes, Error>> {
fn from(req: HttpRequest) -> Self {
Request::from_parts(req.parts, req.body.boxed())
}
}
impl From<Request<BoxBody<Bytes, Error>>> for HttpRequest {
fn from(req: Request<BoxBody<Bytes, Error>>) -> Self {
let (parts, body) = req.into_parts();
HttpRequest::from_parts(body, parts)
}
}
impl From<Request<Incoming>> for HttpRequest {
fn from(req: Request<Incoming>) -> Self {
let (parts, body) = req.into_parts();
let body = body.map_err(anyhow::Error::from);
HttpRequest::from_parts(body.boxed(), parts)
}
}