[go: up one dir, main page]

conduit/
lib.rs

1#![warn(rust_2018_idioms)]
2
3use std::error::Error;
4use std::fs::File;
5use std::io::Read;
6use std::net::SocketAddr;
7use std::time::{Duration, Instant};
8
9pub use http::{header, HeaderMap, Method, Request, Response, StatusCode, Version};
10
11pub type ResponseResult<Error> = Result<Response<Body>, Error>;
12pub type HttpResult = ResponseResult<http::Error>;
13
14pub type BoxError = Box<dyn Error + Send>;
15pub type HandlerResult = Result<Response<Body>, BoxError>;
16
17/// A type representing the instant a request was received
18///
19/// Servers must add this to the request's extensions, capturing the moment
20/// request headers were received.
21pub struct StartInstant(Instant);
22
23impl StartInstant {
24    pub fn now() -> Self {
25        Self(Instant::now())
26    }
27}
28
29/// A type representing a `Response` body.
30///
31/// This type is intended exclusively for use as part of a `Response<Body>`.
32/// Each conduit server provides its own request type that implements
33/// `RequestExt` which provides the request body as a `&'a mut dyn Read`.
34pub enum Body {
35    Static(&'static [u8]),
36    Owned(Vec<u8>),
37    File(File),
38}
39
40impl Body {
41    /// Create a new `Body` from an empty static slice.
42    pub fn empty() -> Self {
43        Self::from_static(b"")
44    }
45
46    /// Create a new `Body` from the provided static byte slice.
47    pub fn from_static(bytes: &'static [u8]) -> Self {
48        Self::Static(bytes)
49    }
50
51    /// Create a new `Body` by taking ownership of the provided bytes.
52    pub fn from_vec(bytes: Vec<u8>) -> Self {
53        Self::Owned(bytes)
54    }
55}
56
57/// A helper to convert a concrete error type into a `Box<dyn Error + Send>`
58///
59/// # Example
60///
61/// ```
62/// # use std::error::Error;
63/// # use conduit::{box_error, Body, Response};
64/// # let _: Result<Response<Body>, Box<dyn Error + Send>> =
65/// Response::builder().body(Body::empty()).map_err(box_error);
66/// ```
67pub fn box_error<E: Error + Send + 'static>(error: E) -> BoxError {
68    Box::new(error)
69}
70
71#[derive(PartialEq, Debug, Clone, Copy)]
72pub enum Scheme {
73    Http,
74    Https,
75}
76
77#[derive(PartialEq, Debug, Clone, Copy)]
78pub enum Host<'a> {
79    Name(&'a str),
80    Socket(SocketAddr),
81}
82
83/// A Dictionary for extensions provided by the server or middleware
84pub type Extensions = http::Extensions;
85
86pub trait RequestExt {
87    /// The elapsed time since the start of the request (headers received)
88    ///
89    /// # Panics
90    ///
91    /// This method may panic if the server does not add `StartInstant` to the
92    /// request extensions, or if it has been removed by the application.
93    fn elapsed(&self) -> Duration {
94        self.extensions().get::<StartInstant>().unwrap().0.elapsed()
95    }
96
97    /// The version of HTTP being used
98    fn http_version(&self) -> Version;
99
100    /// The request method, such as GET, POST, PUT, DELETE or PATCH
101    fn method(&self) -> &Method;
102
103    /// The scheme part of the request URL
104    fn scheme(&self) -> Scheme;
105
106    /// The host part of the requested URL
107    fn host(&self) -> Host<'_>;
108
109    /// The initial part of the request URL's path that corresponds
110    /// to a virtual root. This allows an application to have a
111    /// virtual location that consumes part of the path.
112    fn virtual_root(&self) -> Option<&str>;
113
114    /// The remainder of the path.
115    fn path(&self) -> &str;
116
117    /// Obtain the request path for mutation/rewrite
118    fn path_mut(&mut self) -> &mut String;
119
120    /// The portion of the request URL that follows the "?"
121    fn query_string(&self) -> Option<&str>;
122
123    /// The remote IP address of the client or the last proxy that
124    /// sent the request.
125    fn remote_addr(&self) -> SocketAddr;
126
127    /// The byte-size of the body, if any
128    fn content_length(&self) -> Option<u64>;
129
130    /// The request's headers, as conduit::Headers.
131    fn headers(&self) -> &HeaderMap;
132
133    /// A Reader for the body of the request
134    ///
135    /// # Blocking
136    ///
137    /// The returned value implements the blocking `Read` API and should only
138    /// be read from while in a blocking context.
139    fn body(&mut self) -> &mut dyn Read;
140
141    /// A readable map of extensions
142    fn extensions(&self) -> &Extensions;
143
144    /// A mutable map of extensions
145    fn mut_extensions(&mut self) -> &mut Extensions;
146}
147
148/// A Handler takes a request and returns a response or an error.
149/// By default, a bare function implements `Handler`.
150pub trait Handler: Sync + Send + 'static {
151    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult;
152}
153
154impl<F, E> Handler for F
155where
156    F: Fn(&mut dyn RequestExt) -> ResponseResult<E> + Sync + Send + 'static,
157    E: Error + Send + 'static,
158{
159    fn call(&self, request: &mut dyn RequestExt) -> HandlerResult {
160        (*self)(request).map_err(box_error)
161    }
162}