The great transient state patch. Tests forthcoming.

Signed-off-by: Erik Hollensbe <linux@hollensbe.org>
This commit is contained in:
Erik Hollensbe
2022-01-22 15:45:46 -08:00
parent a7ae4c2c26
commit 692b7e54d8
9 changed files with 237 additions and 121 deletions
+8 -5
View File
@@ -4,8 +4,9 @@ async fn validate_authtoken(
req: Request<Body>,
resp: Option<Response<Body>>,
_params: Params,
app: App<State>,
) -> HTTPResult {
app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let token = req.headers().get("X-AuthToken");
if token.is_none() {
return Err(Error::StatusCode(StatusCode::UNAUTHORIZED));
@@ -24,21 +25,23 @@ async fn validate_authtoken(
return Err(Error::StatusCode(StatusCode::UNAUTHORIZED));
}
return Ok((req, resp));
return Ok((req, resp, NoState {}));
}
async fn hello(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let name = params.get("name").unwrap();
let bytes = Body::from(format!("hello, {}!\n", name));
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
NoState {},
));
}
+8 -5
View File
@@ -7,8 +7,9 @@ async fn validate_authtoken(
req: Request<Body>,
resp: Option<Response<Body>>,
_params: Params,
_app: App<()>,
) -> HTTPResult {
_app: App<(), NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let token = req.headers().get("X-AuthToken");
if token.is_none() {
return Err(Error::StatusCode(StatusCode::UNAUTHORIZED));
@@ -28,21 +29,23 @@ async fn validate_authtoken(
return Err(Error::StatusCode(StatusCode::UNAUTHORIZED));
}
return Ok((req, resp));
return Ok((req, resp, NoState {}));
}
async fn hello(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<()>,
) -> HTTPResult {
_app: App<(), NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let name = params.get("name").unwrap();
let bytes = Body::from(format!("hello, {}!\n", name));
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
NoState {},
));
}
+4 -2
View File
@@ -4,14 +4,16 @@ async fn hello(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<()>,
) -> HTTPResult {
_app: App<(), NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let name = params.get("name").unwrap();
let bytes = Body::from(format!("hello, {}!\n", name));
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
NoState {},
));
}
+8 -5
View File
@@ -5,18 +5,20 @@ async fn log(
req: Request<Body>,
resp: Option<Response<Body>>,
_params: Params,
_app: App<()>,
) -> HTTPResult {
_app: App<(), NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
log::trace!("New request: {}", req.uri().path());
Ok((req, resp))
Ok((req, resp, NoState {}))
}
async fn hello(
req: Request<Body>,
_resp: Option<Response<Body>>,
params: Params,
_app: App<()>,
) -> HTTPResult {
_app: App<(), NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let name = params.get("name").unwrap();
log::info!("Saying hello to {}", name);
let bytes = Body::from(format!("hello, {}!\n", name));
@@ -24,6 +26,7 @@ async fn hello(
return Ok((
req,
Some(Response::builder().status(200).body(bytes).unwrap()),
NoState,
));
}
+13 -13
View File
@@ -4,7 +4,7 @@ use http::{Method, Request, Response, StatusCode};
use hyper::{server::conn::Http, service::service_fn, Body};
use tokio::{net::TcpListener, sync::Mutex};
use crate::{handler::Handler, router::Router, Error, ServerError};
use crate::{handler::Handler, router::Router, Error, ServerError, TransientState};
/// App is used to define application-level functionality and initialize the server. Routes are
/// typically programmed here.
@@ -49,12 +49,12 @@ use crate::{handler::Handler, router::Router, Error, ServerError};
///
/// Requests are routed through paths to [crate::handler::HandlerFunc]s.
#[derive(Clone)]
pub struct App<S: Clone + Send> {
router: Router<S>,
pub struct App<S: Clone + Send, T: TransientState + 'static + Clone + Send> {
router: Router<S, T>,
global_state: Option<Arc<Mutex<S>>>,
}
impl<S: 'static + Clone + Send> App<S> {
impl<S: 'static + Clone + Send, T: TransientState + 'static + Clone + Send> App<S, T> {
/// Construct a new App with no state; it will be passed to handlers as `App<()>`.
pub fn new() -> Self {
Self {
@@ -84,55 +84,55 @@ impl<S: 'static + Clone + Send> App<S> {
/// Create a route for a GET request. See App's docs and [crate::handler::Handler] for
/// more information.
pub fn get(&mut self, path: &str, ch: Handler<S>) {
pub fn get(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::GET, path.to_string(), ch);
}
/// Create a route for a POST request. See App's docs and [crate::handler::Handler] for
/// more information.
pub fn post(&mut self, path: &str, ch: Handler<S>) {
pub fn post(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::POST, path.to_string(), ch);
}
/// Create a route for a DELETE request. See App's docs and [crate::handler::Handler] for
/// more information.
pub fn delete(&mut self, path: &str, ch: Handler<S>) {
pub fn delete(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::DELETE, path.to_string(), ch);
}
/// Create a route for a PUT request. See App's docs and [crate::handler::Handler] for
/// more information.
pub fn put(&mut self, path: &str, ch: Handler<S>) {
pub fn put(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::PUT, path.to_string(), ch);
}
/// Create a route for an OPTIONS request. See App's docs and
/// [crate::handler::Handler] for more information.
pub fn options(&mut self, path: &str, ch: Handler<S>) {
pub fn options(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::OPTIONS, path.to_string(), ch);
}
/// Create a route for a PATCH request. See App's docs and
/// [crate::handler::Handler] for more information.
pub fn patch(&mut self, path: &str, ch: Handler<S>) {
pub fn patch(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::PATCH, path.to_string(), ch);
}
/// Create a route for a HEAD request. See App's docs and
/// [crate::handler::Handler] for more information.
pub fn head(&mut self, path: &str, ch: Handler<S>) {
pub fn head(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::HEAD, path.to_string(), ch);
}
/// Create a route for a CONNECT request. See App's docs and
/// [crate::handler::Handler] for more information.
pub fn connect(&mut self, path: &str, ch: Handler<S>) {
pub fn connect(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::CONNECT, path.to_string(), ch);
}
/// Create a route for a TRACE request. See App's docs and
/// [crate::handler::Handler] for more information.
pub fn trace(&mut self, path: &str, ch: Handler<S>) {
pub fn trace(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::TRACE, path.to_string(), ch);
}
+48 -32
View File
@@ -1,6 +1,6 @@
use std::future::Future;
use crate::{app::App, HTTPResult, PinBox};
use crate::{app::App, HTTPResult, PinBox, TransientState};
use async_recursion::async_recursion;
use http::{Request, Response};
@@ -18,43 +18,46 @@ use hyper::Body;
/// req: Request<Body>,
/// _resp: Option<Response<Body>>,
/// params: Params,
/// _app: App<()>,
/// ) -> HTTPResult {
/// _app: App<(), NoState>,
/// _state: NoState,
/// ) -> HTTPResult<NoState> {
/// let name = params.get("name").unwrap();
/// let bytes = Body::from(format!("hello, {}!\n", name));
///
/// return Ok((
/// req,
/// Some(Response::builder().status(200).body(bytes).unwrap()),
/// NoState{},
/// ));
/// }
/// ```
///
pub type HandlerFunc<S> = fn(
pub type HandlerFunc<S, T> = fn(
req: Request<Body>,
response: Option<Response<Body>>,
params: crate::Params,
app: App<S>,
) -> PinBox<dyn Future<Output = HTTPResult> + Send>;
app: App<S, T>,
state: T,
) -> PinBox<dyn Future<Output = HTTPResult<T>> + Send>;
/// Handler is the structure of the handler. Typically, you will not use this directly, and instead
/// interact with the [crate::compose_handler!] macro. That said, if you wanted to define your own
/// macros or otherwise compose more complicated structures for your handlers, this is available to
/// you.
#[derive(Clone)]
pub struct Handler<S: Clone + Send> {
handler: HandlerFunc<S>,
next: Box<Option<Handler<S>>>,
pub struct Handler<S: Clone + Send, T: TransientState + 'static> {
handler: HandlerFunc<S, T>,
next: Box<Option<Handler<S, T>>>,
}
impl<S> Handler<S>
impl<S: Clone + Send, T: TransientState> Handler<S, T>
where
Self: Send,
S: Clone + Send,
{
/// Construct a new handler composed of a HandlerFunc with state, and an optional next handler
/// in the chain.
pub fn new(handler: HandlerFunc<S>, next: Option<Handler<S>>) -> Self {
pub fn new(handler: HandlerFunc<S, T>, next: Option<Handler<S, T>>) -> Self {
Self {
handler,
next: Box::new(next),
@@ -68,30 +71,35 @@ where
req: Request<hyper::Body>,
response: Option<Response<hyper::Body>>,
params: crate::Params,
app: App<S>,
) -> HTTPResult {
let (req, response) = (self.handler)(req, response, params.clone(), app.clone()).await?;
app: App<S, T>,
state: T,
) -> HTTPResult<T> {
let (req, response, state) =
(self.handler)(req, response, params.clone(), app.clone(), state).await?;
if self.next.is_some() {
return Ok((*self.clone().next)
.unwrap()
.perform(req, response, params, app)
.perform(req, response, params, app, state)
.await?);
}
Ok((req, response))
Ok((req, response, state))
}
}
mod tests {
#[tokio::test]
async fn test_handler_basic() {
use crate::{app::App, Error, HTTPResult, Params};
use crate::{app::App, Error, HTTPResult, NoState, Params};
use http::{HeaderValue, Request, Response, StatusCode};
use hyper::Body;
#[derive(Clone)]
struct State;
#[derive(Clone)]
struct TransientState;
// this method adds a header:
// wakka: wakka wakka
// to the request. that's it!
@@ -99,11 +107,12 @@ mod tests {
mut req: Request<Body>,
_response: Option<Response<Body>>,
_params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let headers = req.headers_mut();
headers.insert("wakka", HeaderValue::from_str("wakka wakka").unwrap());
Ok((req, None))
Ok((req, None, NoState {}))
}
// this method returns an OK status when the wakka header exists.
@@ -111,22 +120,23 @@ mod tests {
req: Request<Body>,
mut response: Option<Response<Body>>,
_params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
if let Some(header) = req.headers().get("wakka") {
if header != "wakka wakka" {
return Err(Error::new("invalid header value"));
}
if response.is_some() {
return Ok((req, response));
return Ok((req, response, NoState {}));
} else {
let resp = Response::builder()
.status(StatusCode::OK)
.body(Body::default())?;
response.replace(resp);
return Ok((req, response));
return Ok((req, response, NoState {}));
}
}
@@ -135,12 +145,12 @@ mod tests {
// single stage handler that never yields a response
let bh = super::Handler::new(
|req, resp, params, app| Box::pin(one(req, resp, params, app)),
|req, resp, params, app, state| Box::pin(one(req, resp, params, app, state)),
None,
);
let req = Request::default();
let (req, response) = bh
.perform(req, None, Params::new(), App::new())
let (req, response, _) = bh
.perform(req, None, Params::new(), App::new(), NoState {})
.await
.unwrap();
@@ -149,22 +159,28 @@ mod tests {
// two-stage handler; yields a response if the first one was good.
let bh_two = super::Handler::new(
|req, resp, params, app| Box::pin(two(req, resp, params, app)),
|req, resp, params, app, state| Box::pin(two(req, resp, params, app, state)),
None,
);
let bh = super::Handler::new(
|req, resp, params, app| Box::pin(one(req, resp, params, app)),
|req, resp, params, app, state| Box::pin(one(req, resp, params, app, state)),
Some(bh_two.clone()),
);
let (_, response) = bh
.perform(req, None, Params::new(), App::new())
let (_, response, _) = bh
.perform(req, None, Params::new(), App::new(), NoState {})
.await
.unwrap();
assert!(response.is_some() && response.unwrap().status() == StatusCode::OK);
assert!(bh_two
.perform(Request::default(), None, Params::new(), App::new())
.perform(
Request::default(),
None,
Params::new(),
App::new(),
NoState {}
)
.await
.is_err());
+31 -2
View File
@@ -74,7 +74,34 @@ where
/// returned. If you wish to return Err(), a [http::StatusCode] or [std::string::String] can be
/// returned, the former is resolved to its status with an empty body, and the latter corresponds
/// to a 500 Internal Server Error with the body set to the string.
pub type HTTPResult = Result<(Request<hyper::Body>, Option<Response<hyper::Body>>), Error>;
pub type HTTPResult<TransientState> = Result<
(
Request<hyper::Body>,
Option<Response<hyper::Body>>,
TransientState,
),
Error,
>;
/// TransientState must be implemented to use state between handlers.
pub trait TransientState
where
Self: Clone + Send,
{
/// initial prescribes an initial state for the trait, allowing it to be constructed at
/// dispatch time.
fn initial() -> Self;
}
/// NoState is an empty [crate::TransientState].
#[derive(Clone)]
pub struct NoState;
impl TransientState for NoState {
fn initial() -> Self {
Self {}
}
}
/// A convenience import to gather all of `ratpack`'s dependencies in one easy place.
/// To use:
@@ -83,7 +110,9 @@ pub type HTTPResult = Result<(Request<hyper::Body>, Option<Response<hyper::Body>
/// use ratpack::prelude::*;
/// ```
pub mod prelude {
pub use crate::{app::App, compose_handler, Error, HTTPResult, Params, ServerError};
pub use crate::{
app::App, compose_handler, Error, HTTPResult, NoState, Params, ServerError, TransientState,
};
pub use http::{Request, Response, StatusCode};
pub use hyper::Body;
}
+36 -16
View File
@@ -10,10 +10,10 @@ macro_rules! compose_handler {
{
use $crate::handler::{HandlerFunc, Handler};
{
let mut funcs: Vec<HandlerFunc<_>> = Vec::new();
let mut funcs: Vec<HandlerFunc<_, _>> = Vec::new();
$(
funcs.push(|req, resp, params, app| Box::pin($x(req, resp, params, app)));
funcs.push(|req, resp, params, app, state| Box::pin($x(req, resp, params, app, state)));
)*
if funcs.len() == 0 {
@@ -22,7 +22,7 @@ macro_rules! compose_handler {
let mut handlers = Vec::new();
let mut last: Option<Handler<_>> = None;
let mut last: Option<Handler<_, _>> = None;
funcs.reverse();
for func in funcs {
@@ -42,7 +42,7 @@ mod tests {
use http::{HeaderValue, Request, Response, StatusCode};
use hyper::Body;
use crate::{app::App, Error, HTTPResult, Params};
use crate::{app::App, Error, HTTPResult, NoState, Params};
#[derive(Clone)]
struct State;
@@ -54,11 +54,12 @@ mod tests {
mut req: Request<Body>,
_response: Option<Response<Body>>,
_params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
let headers = req.headers_mut();
headers.insert("wakka", HeaderValue::from_str("wakka wakka").unwrap());
Ok((req, None))
Ok((req, None, NoState {}))
}
// this method returns an OK status when the wakka header exists.
@@ -66,22 +67,23 @@ mod tests {
req: Request<Body>,
mut response: Option<Response<Body>>,
_params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
if let Some(header) = req.headers().get("wakka") {
if header != "wakka wakka" {
return Err(Error::new("invalid header value"));
}
if response.is_some() {
return Ok((req, response));
return Ok((req, response, NoState {}));
} else {
let resp = Response::builder()
.status(StatusCode::OK)
.body(Body::default())?;
response.replace(resp);
return Ok((req, response));
return Ok((req, response, NoState {}));
}
}
@@ -90,8 +92,14 @@ mod tests {
let handler = compose_handler!(one, two);
let (req, response) = handler
.perform(Request::default(), None, Params::new(), App::new())
let (req, response, _) = handler
.perform(
Request::default(),
None,
Params::new(),
App::new(),
NoState {},
)
.await
.unwrap();
@@ -100,8 +108,14 @@ mod tests {
let handler = compose_handler!(one);
let (req, response) = handler
.perform(Request::default(), None, Params::new(), App::new())
let (req, response, _) = handler
.perform(
Request::default(),
None,
Params::new(),
App::new(),
NoState {},
)
.await
.unwrap();
@@ -111,7 +125,13 @@ mod tests {
let handler = compose_handler!(two);
assert!(handler
.perform(Request::default(), None, Params::new(), App::new())
.perform(
Request::default(),
None,
Params::new(),
App::new(),
NoState {}
)
.await
.is_err());
}
+81 -41
View File
@@ -1,30 +1,30 @@
use http::{Request, Response};
use hyper::Body;
use crate::{app::App, handler::Handler, path::Path, Error, HTTPResult};
use crate::{app::App, handler::Handler, path::Path, Error, HTTPResult, TransientState};
#[derive(Clone)]
pub(crate) struct Route<S: Clone + Send> {
pub(crate) struct Route<S: Clone + Send, T: TransientState + 'static> {
method: http::Method,
path: Path,
handler: Handler<S>,
handler: Handler<S, T>,
}
impl<S: Clone + Send> PartialEq for Route<S> {
impl<S: Clone + Send, T: TransientState> PartialEq for Route<S, T> {
fn eq(&self, other: &Self) -> bool {
self.method.to_string() == other.method.to_string() && self.path.eq(&other.path)
}
}
impl<S: Clone + Send> Eq for Route<S> {}
impl<S: Clone + Send, T: TransientState> Eq for Route<S, T> {}
impl<S: Clone + Send> PartialOrd for Route<S> {
impl<S: Clone + Send, T: TransientState> PartialOrd for Route<S, T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<S: Clone + Send> Ord for Route<S> {
impl<S: Clone + Send, T: TransientState> Ord for Route<S, T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let left = self.method.to_string() + " " + &self.path.to_string();
let right = other.method.to_string() + " " + &other.path.to_string();
@@ -33,8 +33,8 @@ impl<S: Clone + Send> Ord for Route<S> {
}
}
impl<S: Clone + Send> Route<S> {
fn new(method: http::Method, path: String, handler: Handler<S>) -> Self {
impl<S: Clone + Send, T: TransientState> Route<S, T> {
fn new(method: http::Method, path: String, handler: Handler<S, T>) -> Self {
Self {
method,
handler,
@@ -46,27 +46,28 @@ impl<S: Clone + Send> Route<S> {
&self,
provided: String,
req: Request<hyper::Body>,
app: App<S>,
) -> HTTPResult {
app: App<S, T>,
state: T,
) -> HTTPResult<T> {
let params = self.path.extract(provided)?;
if self.method != req.method() {
return Err(Error::StatusCode(http::StatusCode::NOT_FOUND));
}
self.handler.perform(req, None, params, app).await
self.handler.perform(req, None, params, app, state).await
}
}
#[derive(Clone)]
pub(crate) struct Router<S: Clone + Send>(Vec<Route<S>>);
pub(crate) struct Router<S: Clone + Send, T: TransientState + 'static>(Vec<Route<S, T>>);
impl<S: Clone + Send> Router<S> {
impl<S: Clone + Send, T: TransientState + Clone + Send> Router<S, T> {
pub fn new() -> Self {
Self(Vec::new())
}
pub(crate) fn add(&mut self, method: http::Method, path: String, ch: Handler<S>) -> Self {
pub(crate) fn add(&mut self, method: http::Method, path: String, ch: Handler<S, T>) -> Self {
self.0.push(Route::new(method, path, ch));
self.clone()
}
@@ -74,13 +75,15 @@ impl<S: Clone + Send> Router<S> {
pub(crate) async fn dispatch(
&self,
req: Request<Body>,
app: App<S>,
app: App<S, T>,
) -> Result<Response<Body>, Error> {
let path = req.uri().path().to_string();
for route in self.0.clone() {
if route.path.matches(path.to_string()) && route.method.eq(req.method()) {
let (_, response) = route.dispatch(path.to_string(), req, app).await?;
let (_, response, _) = route
.dispatch(path.to_string(), req, app, T::initial())
.await?;
if response.is_none() {
return Err(Error::StatusCode(http::StatusCode::INTERNAL_SERVER_ERROR));
}
@@ -94,13 +97,12 @@ impl<S: Clone + Send> Router<S> {
}
mod tests {
#[tokio::test]
async fn test_route_dynamic() {
use http::{Method, Request, Response};
use hyper::Body;
use crate::{app::App, handler::Handler, HTTPResult, Params};
use crate::{app::App, handler::Handler, HTTPResult, NoState, Params};
use super::Route;
@@ -111,14 +113,16 @@ mod tests {
req: Request<Body>,
_response: Option<Response<Body>>,
params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
return Ok((
req,
Some(Response::builder().status(400).body(Body::from(format!(
"hello, {}",
*params.get("name").unwrap()
)))?),
NoState {},
));
}
@@ -126,13 +130,15 @@ mod tests {
Method::GET,
"/a/:name/c".to_string(),
Handler::new(
|req, resp, params, app| Box::pin(handler_dynamic(req, resp, params, app)),
|req, resp, params, app, state| {
Box::pin(handler_dynamic(req, resp, params, app, state))
},
None,
),
);
assert!(route
.dispatch("/a".to_string(), Request::default(), App::new())
.dispatch("/a".to_string(), Request::default(), App::new(), NoState {})
.await
.is_err());
assert!(route
@@ -143,6 +149,7 @@ mod tests {
.body(Body::from("one=two".as_bytes()))
.unwrap(),
App::new(),
NoState {},
)
.await
.is_err());
@@ -151,7 +158,12 @@ mod tests {
"erik", "adam", "sean", "travis", "joseph", "grant", "joy", "steve", "marc",
] {
assert!(route
.dispatch("/a/:name/c".to_string(), Request::default(), App::new())
.dispatch(
"/a/:name/c".to_string(),
Request::default(),
App::new(),
NoState {}
)
.await
.is_ok());
@@ -159,7 +171,7 @@ mod tests {
let body = hyper::body::to_bytes(
route
.dispatch(path.clone(), Request::default(), App::new())
.dispatch(path.clone(), Request::default(), App::new(), NoState {})
.await
.unwrap()
.1
@@ -172,7 +184,7 @@ mod tests {
assert_eq!(body, format!("hello, {}", name).as_bytes());
let status = route
.dispatch(path, Request::default(), App::new())
.dispatch(path, Request::default(), App::new(), NoState {})
.await
.unwrap()
.1
@@ -188,7 +200,7 @@ mod tests {
use http::{Method, Request, Response};
use hyper::Body;
use crate::{app::App, handler::Handler, HTTPResult, Params};
use crate::{app::App, handler::Handler, HTTPResult, NoState, Params};
use super::Route;
@@ -199,8 +211,9 @@ mod tests {
req: Request<Body>,
_response: Option<Response<Body>>,
_params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
return Ok((
req,
Some(
@@ -208,6 +221,7 @@ mod tests {
.status(400)
.body(Body::from("hello, world".as_bytes()))?,
),
NoState {},
));
}
@@ -215,13 +229,15 @@ mod tests {
Method::GET,
"/a/b/c".to_string(),
Handler::new(
|req, resp, params, app| Box::pin(handler_static(req, resp, params, app)),
|req, resp, params, app, state| {
Box::pin(handler_static(req, resp, params, app, state))
},
None,
),
);
assert!(route
.dispatch("/a".to_string(), Request::default(), App::new())
.dispatch("/a".to_string(), Request::default(), App::new(), NoState {})
.await
.is_err());
assert!(route
@@ -232,18 +248,29 @@ mod tests {
.body(Body::from("one=two".as_bytes()))
.unwrap(),
App::new(),
NoState {},
)
.await
.is_err());
assert!(route
.dispatch("/a/b/c".to_string(), Request::default(), App::new())
.dispatch(
"/a/b/c".to_string(),
Request::default(),
App::new(),
NoState {}
)
.await
.is_ok());
let body = hyper::body::to_bytes(
route
.dispatch("/a/b/c".to_string(), Request::default(), App::new())
.dispatch(
"/a/b/c".to_string(),
Request::default(),
App::new(),
NoState {},
)
.await
.unwrap()
.1
@@ -256,7 +283,12 @@ mod tests {
assert_eq!(body, "hello, world".as_bytes());
let status = route
.dispatch("/a/b/c".to_string(), Request::default(), App::new())
.dispatch(
"/a/b/c".to_string(),
Request::default(),
App::new(),
NoState {},
)
.await
.unwrap()
.1
@@ -269,7 +301,7 @@ mod tests {
#[tokio::test]
async fn test_router() {
use super::Router;
use crate::{app::App, handler::Handler, HTTPResult, Params};
use crate::{app::App, handler::Handler, HTTPResult, NoState, Params};
use http::{Method, Request, Response};
use hyper::Body;
@@ -280,14 +312,16 @@ mod tests {
req: Request<Body>,
_response: Option<Response<Body>>,
params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
return Ok((
req,
Some(Response::builder().status(400).body(Body::from(format!(
"hello, {}",
*params.get("name").unwrap()
)))?),
NoState {},
));
}
@@ -295,8 +329,9 @@ mod tests {
req: Request<Body>,
_response: Option<Response<Body>>,
_params: Params,
_app: App<State>,
) -> HTTPResult {
_app: App<State, NoState>,
_state: NoState,
) -> HTTPResult<NoState> {
return Ok((
req,
Some(
@@ -304,6 +339,7 @@ mod tests {
.status(400)
.body(Body::from("hello, world".as_bytes()))?,
),
NoState {},
));
}
@@ -313,7 +349,9 @@ mod tests {
Method::GET,
"/a/b/c".to_string(),
Handler::new(
|req, resp, params, app| Box::pin(handler_static(req, resp, params, app)),
|req, resp, params, app, state| {
Box::pin(handler_static(req, resp, params, app, state))
},
None,
),
);
@@ -322,7 +360,9 @@ mod tests {
Method::GET,
"/c/b/a/:name".to_string(),
Handler::new(
|req, resp, params, app| Box::pin(handler_dynamic(req, resp, params, app)),
|req, resp, params, app, state| {
Box::pin(handler_dynamic(req, resp, params, app, state))
},
None,
),
);