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}