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)
}
}