use std::fmt;
use crate::Result;
use crate::{HeaderMap, HeaderName, HeaderValue, Method, Uri, Version, body::Body};
use rama_core::extensions::{Extension, Extensions, ExtensionsRef};
use rama_net::ClientIp;
use rama_utils::macros::generate_set_and_with;
#[derive(Clone)]
pub struct Request<T = Body> {
head: Parts,
body: T,
}
#[non_exhaustive]
#[derive(Clone)]
pub struct Parts {
pub method: Method,
pub uri: Uri,
pub version: Version,
pub headers: HeaderMap<HeaderValue>,
pub extensions: Extensions,
}
impl ExtensionsRef for Parts {
fn extensions(&self) -> &Extensions {
&self.extensions
}
}
impl ClientIp for Parts {
fn client_ip(&self) -> Option<std::net::IpAddr> {
rama_net::client_ip::client_ip(self)
}
}
#[derive(Debug)]
#[must_use]
pub struct Builder {
inner: Result<Parts>,
}
impl Request<()> {
#[inline]
pub fn builder() -> Builder {
Builder::new()
}
#[inline]
pub fn builder_with_extensions(ext: Extensions) -> Builder {
Builder::new_with_extensions(ext)
}
pub fn get<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::GET).uri(uri)
}
pub fn put<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::PUT).uri(uri)
}
pub fn post<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::POST).uri(uri)
}
pub fn delete<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::DELETE).uri(uri)
}
pub fn options<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::OPTIONS).uri(uri)
}
pub fn head<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::HEAD).uri(uri)
}
pub fn connect<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::CONNECT).uri(uri)
}
pub fn patch<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::PATCH).uri(uri)
}
pub fn trace<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::TRACE).uri(uri)
}
pub fn query<T>(uri: T) -> Builder
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::QUERY).uri(uri)
}
}
impl<T> Request<T> {
#[inline]
pub fn new(body: T) -> Self {
Self {
head: Parts::new(),
body,
}
}
#[inline]
pub fn from_parts(parts: Parts, body: T) -> Self {
Self { head: parts, body }
}
#[inline]
pub fn method(&self) -> &Method {
&self.head.method
}
#[inline]
pub fn method_mut(&mut self) -> &mut Method {
&mut self.head.method
}
#[inline]
pub fn uri(&self) -> &Uri {
&self.head.uri
}
#[must_use]
pub fn request_uri(&self) -> Uri {
use rama_net::{AuthorityInputExt as _, ProtocolInputExt as _};
let mut uri = self.uri().clone();
if let Some(authority) = self.authority() {
let protocol = self.protocol().unwrap_or(&rama_net::Protocol::HTTP);
let rama_net::address::HostWithOptPort { host, port } =
authority.without_default_port_for(Some(protocol));
uri.set_scheme(protocol.clone())
.set_host(host)
.set_port(port);
}
uri
}
#[inline]
pub fn uri_mut(&mut self) -> &mut Uri {
&mut self.head.uri
}
#[inline]
pub fn version(&self) -> Version {
self.head.version
}
#[inline]
pub fn version_mut(&mut self) -> &mut Version {
&mut self.head.version
}
#[inline]
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
&self.head.headers
}
#[inline]
pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
&mut self.head.headers
}
#[inline]
pub fn clone_parts(&self) -> Parts {
self.head.clone()
}
#[inline]
pub fn body(&self) -> &T {
&self.body
}
#[inline]
pub fn body_mut(&mut self) -> &mut T {
&mut self.body
}
#[inline]
pub fn into_body(self) -> T {
self.body
}
#[inline]
pub fn into_parts(self) -> (Parts, T) {
(self.head, self.body)
}
#[inline]
pub fn map<F, U>(self, f: F) -> Request<U>
where
F: FnOnce(T) -> U,
{
Request {
body: f(self.body),
head: self.head,
}
}
generate_set_and_with! {
pub fn extensions(mut self, extensions: Extensions) -> Self {
self.head.extensions = extensions;
self
}
}
}
impl<T: Default> Default for Request<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: fmt::Debug> fmt::Debug for Request<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Request")
.field("method", self.method())
.field("uri", self.uri())
.field("version", &self.version())
.field("headers", self.headers())
.field("body", self.body())
.finish()
}
}
impl<B> ExtensionsRef for Request<B> {
fn extensions(&self) -> &Extensions {
&self.head.extensions
}
}
impl<B> ClientIp for Request<B> {
fn client_ip(&self) -> Option<std::net::IpAddr> {
rama_net::client_ip::client_ip(self)
}
}
impl Parts {
fn new() -> Self {
Self {
method: Method::default(),
uri: Uri::from_static("/"),
version: Version::default(),
headers: HeaderMap::default(),
extensions: Extensions::default(),
}
}
}
impl fmt::Debug for Parts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Parts")
.field("method", &self.method)
.field("uri", &self.uri)
.field("version", &self.version)
.field("headers", &self.headers)
.finish()
}
}
impl Builder {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn new_with_extensions(ext: Extensions) -> Self {
Self {
inner: Ok(Parts {
method: Method::default(),
uri: Uri::from_static("/"),
version: Version::default(),
headers: HeaderMap::default(),
extensions: ext,
}),
}
}
pub fn method<T>(self, method: T) -> Self
where
T: TryInto<Method>,
<T as TryInto<Method>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
let method = method.try_into().map_err(Into::into)?;
head.method = method;
Ok(head)
})
}
pub fn method_ref(&self) -> Option<&Method> {
self.inner.as_ref().ok().map(|h| &h.method)
}
pub fn uri<T>(self, uri: T) -> Self
where
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
head.uri = uri.try_into().map_err(Into::into)?;
Ok(head)
})
}
pub fn uri_ref(&self) -> Option<&Uri> {
self.inner.as_ref().ok().map(|h| &h.uri)
}
pub fn version(self, version: Version) -> Self {
self.and_then(move |mut head| {
head.version = version;
Ok(head)
})
}
pub fn version_ref(&self) -> Option<&Version> {
self.inner.as_ref().ok().map(|h| &h.version)
}
pub fn header<K, V>(self, key: K, value: V) -> Self
where
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: Into<crate::Error>,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
let name = key.try_into().map_err(Into::into)?;
let value = value.try_into().map_err(Into::into)?;
head.headers.try_append(name, value)?;
Ok(head)
})
}
pub fn headers_ref(&self) -> Option<&HeaderMap<HeaderValue>> {
self.inner.as_ref().ok().map(|h| &h.headers)
}
pub fn headers_mut(&mut self) -> Option<&mut HeaderMap<HeaderValue>> {
self.inner.as_mut().ok().map(|h| &mut h.headers)
}
pub fn extension<T>(self, extension: T) -> Self
where
T: Extension,
{
self.and_then(move |head| {
head.extensions.insert(extension);
Ok(head)
})
}
pub fn extensions(&self) -> Option<&Extensions> {
self.inner.as_ref().ok().map(|h| &h.extensions)
}
pub fn body<T>(self, body: T) -> Result<Request<T>> {
self.inner.map(move |head| Request { head, body })
}
fn and_then<F>(self, func: F) -> Self
where
F: FnOnce(Parts) -> Result<Parts>,
{
Self {
inner: self.inner.and_then(func),
}
}
}
impl Default for Builder {
#[inline]
fn default() -> Self {
Self {
inner: Ok(Parts::new()),
}
}
}
pub trait HttpRequestParts: ExtensionsRef {
fn method(&self) -> &Method;
fn uri(&self) -> &Uri;
fn version(&self) -> Version;
fn headers(&self) -> &HeaderMap<HeaderValue>;
}
impl<T: HttpRequestParts> HttpRequestParts for &T {
#[inline(always)]
fn method(&self) -> &Method {
(*self).method()
}
#[inline(always)]
fn uri(&self) -> &Uri {
(*self).uri()
}
#[inline(always)]
fn version(&self) -> Version {
(*self).version()
}
#[inline(always)]
fn headers(&self) -> &HeaderMap<HeaderValue> {
(*self).headers()
}
}
impl<T: HttpRequestParts> HttpRequestParts for &mut T {
#[inline(always)]
fn method(&self) -> &Method {
(**self).method()
}
fn uri(&self) -> &Uri {
(**self).uri()
}
fn version(&self) -> Version {
(**self).version()
}
fn headers(&self) -> &HeaderMap<HeaderValue> {
(**self).headers()
}
}
impl<Body> HttpRequestParts for Request<Body> {
fn method(&self) -> &Method {
self.method()
}
fn uri(&self) -> &Uri {
self.uri()
}
fn version(&self) -> Version {
self.version()
}
fn headers(&self) -> &HeaderMap<HeaderValue> {
self.headers()
}
}
impl HttpRequestParts for Parts {
fn method(&self) -> &Method {
&self.method
}
fn uri(&self) -> &Uri {
&self.uri
}
fn version(&self) -> Version {
self.version
}
fn headers(&self) -> &HeaderMap<HeaderValue> {
&self.headers
}
}
pub trait HttpRequestPartsMut: HttpRequestParts + ExtensionsRef {
fn method_mut(&mut self) -> &mut Method;
fn uri_mut(&mut self) -> &mut Uri;
fn version_mut(&mut self) -> &mut Version;
fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue>;
}
impl<T: HttpRequestPartsMut> HttpRequestPartsMut for &mut T {
fn method_mut(&mut self) -> &mut Method {
(*self).method_mut()
}
fn uri_mut(&mut self) -> &mut Uri {
(*self).uri_mut()
}
fn version_mut(&mut self) -> &mut Version {
(*self).version_mut()
}
fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
(*self).headers_mut()
}
}
impl<Body> HttpRequestPartsMut for Request<Body> {
fn method_mut(&mut self) -> &mut Method {
self.method_mut()
}
fn uri_mut(&mut self) -> &mut Uri {
self.uri_mut()
}
fn version_mut(&mut self) -> &mut Version {
self.version_mut()
}
fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
self.headers_mut()
}
}
impl HttpRequestPartsMut for Parts {
fn method_mut(&mut self) -> &mut Method {
&mut self.method
}
fn uri_mut(&mut self) -> &mut Uri {
&mut self.uri
}
fn version_mut(&mut self) -> &mut Version {
&mut self.version
}
fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
&mut self.headers
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_can_map_a_body_from_one_type_to_another() {
let request = Request::builder().body("some string").unwrap();
let mapped_request = request.map(|s| {
assert_eq!(s, "some string");
123u32
});
assert_eq!(mapped_request.body(), &123u32);
}
#[test]
fn test_request_uri() {
use crate::header::HOST;
use rama_net::{
address::Domain,
forwarded::{Forwarded, ForwardedElement},
};
for (request, expected_uri_str) in [
(Request::builder().uri("/foo").body(()).unwrap(), "/foo"),
(
Request::builder()
.uri("/foo")
.header(HOST, "example.com")
.body(())
.unwrap(),
"http://example.com/foo",
),
(
Request::builder()
.uri("/foo")
.extension(Forwarded::new(ForwardedElement::new_forwarded_host(
Domain::from_static("example.com"),
)))
.body(())
.unwrap(),
"http://example.com/foo",
),
(
Request::builder()
.uri("http://example.com/foo")
.body(())
.unwrap(),
"http://example.com/foo",
),
(
Request::builder()
.uri("https://example.com/foo")
.body(())
.unwrap(),
"https://example.com/foo",
),
(
Request::builder()
.uri(Uri::parse_authority_form("WwW.ExamplE.COM").unwrap())
.body(())
.unwrap(),
"http://WwW.ExamplE.COM",
),
] {
let s = request.request_uri().to_string();
assert_eq!(s, expected_uri_str, "request: {request:?}");
}
}
}