1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#![warn(rust_2018_idioms)]

use std::error::Error;
use std::fs::File;
use std::io::Read;
use std::net::SocketAddr;
use std::time::{Duration, Instant};

pub use http::{header, HeaderMap, Method, Request, Response, StatusCode, Version};

pub type ResponseResult<Error> = Result<Response<Body>, Error>;
pub type HttpResult = ResponseResult<http::Error>;

pub type BoxError = Box<dyn Error + Send>;
pub type HandlerResult = Result<Response<Body>, BoxError>;

/// A type representing the instant a request was received
///
/// Servers must add this to the request's extensions, capturing the moment
/// request headers were received.
pub struct StartInstant(Instant);

impl StartInstant {
    pub fn now() -> Self {
        Self(Instant::now())
    }
}

/// A type representing a `Response` body.
///
/// This type is intended exclusively for use as part of a `Response<Body>`.
/// Each conduit server provides its own request type that implements
/// `RequestExt` which provides the request body as a `&'a mut dyn Read`.
pub enum Body {
    Static(&'static [u8]),
    Owned(Vec<u8>),
    File(File),
}

impl Body {
    /// Create a new `Body` from an empty static slice.
    pub fn empty() -> Self {
        Self::from_static(b"")
    }

    /// Create a new `Body` from the provided static byte slice.
    pub fn from_static(bytes: &'static [u8]) -> Self {
        Self::Static(bytes)
    }

    /// Create a new `Body` by taking ownership of the provided bytes.
    pub fn from_vec(bytes: Vec<u8>) -> Self {
        Self::Owned(bytes)
    }
}

/// A helper to convert a concrete error type into a `Box<dyn Error + Send>`
///
/// # Example
///
/// ```
/// # use std::error::Error;
/// # use conduit::{box_error, Body, Response};
/// # let _: Result<Response<Body>, Box<dyn Error + Send>> =
/// Response::builder().body(Body::empty()).map_err(box_error);
/// ```
pub fn box_error<E: Error + Send + 'static>(error: E) -> BoxError {
    Box::new(error)
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Scheme {
    Http,
    Https,
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Host<'a> {
    Name(&'a str),
    Socket(SocketAddr),
}

/// A Dictionary for extensions provided by the server or middleware
pub type Extensions = http::Extensions;

pub trait RequestExt {
    /// The elapsed time since the start of the request (headers received)
    ///
    /// # Panics
    ///
    /// This method may panic if the server does not add `StartInstant` to the
    /// request extensions, or if it has been removed by the application.
    fn elapsed(&self) -> Duration {
        self.extensions().get::<StartInstant>().unwrap().0.elapsed()
    }

    /// The version of HTTP being used
    fn http_version(&self) -> Version;

    /// The request method, such as GET, POST, PUT, DELETE or PATCH
    fn method(&self) -> &Method;

    /// The scheme part of the request URL
    fn scheme(&self) -> Scheme;

    /// The host part of the requested URL
    fn host(&self) -> Host<'_>;

    /// The initial part of the request URL's path that corresponds
    /// to a virtual root. This allows an application to have a
    /// virtual location that consumes part of the path.
    fn virtual_root(&self) -> Option<&str>;

    /// The remainder of the path.
    fn path(&self) -> &str;

    /// Obtain the request path for mutation/rewrite
    fn path_mut(&mut self) -> &mut String;

    /// The portion of the request URL that follows the "?"
    fn query_string(&self) -> Option<&str>;

    /// The remote IP address of the client or the last proxy that
    /// sent the request.
    fn remote_addr(&self) -> SocketAddr;

    /// The byte-size of the body, if any
    fn content_length(&self) -> Option<u64>;

    /// The request's headers, as conduit::Headers.
    fn headers(&self) -> &HeaderMap;

    /// A Reader for the body of the request
    ///
    /// # Blocking
    ///
    /// The returned value implements the blocking `Read` API and should only
    /// be read from while in a blocking context.
    fn body(&mut self) -> &mut dyn Read;

    /// A readable map of extensions
    fn extensions(&self) -> &Extensions;

    /// A mutable map of extensions
    fn mut_extensions(&mut self) -> &mut Extensions;
}

/// A Handler takes a request and returns a response or an error.
/// By default, a bare function implements `Handler`.
pub trait Handler: Sync + Send + 'static {
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult;
}

impl<F, E> Handler for F
where
    F: Fn(&mut dyn RequestExt) -> ResponseResult<E> + Sync + Send + 'static,
    E: Error + Send + 'static,
{
    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
        (*self)(request).map_err(box_error)
    }
}