use headers::authorization::{Authorization, Bearer};
use highnoon::filter::session;
use highnoon::filter::session::{HasSession, Session};
use highnoon::filter::Next;
use highnoon::{App, Error, Json, Message, Request, Response, Result};
use hyper::StatusCode;
use serde_derive::Serialize;
use tokio;
use tracing::info;
#[derive(Debug)]
struct Db;
impl Default for Db {
fn default() -> Self {
Db
}
}
trait HasDb {
fn get_db(&self) -> &Db;
}
#[derive(Default)]
struct State {
db: Db,
}
#[derive(Default)]
struct Context {
session: session::Session,
}
impl highnoon::State for State {
type Context = Context;
fn new_context(&self) -> Context {
Context::default()
}
}
impl session::HasSession for Context {
fn session(&mut self) -> &mut Session {
&mut self.session
}
}
impl HasDb for State {
fn get_db(&self) -> &Db {
&self.db
}
}
impl<S> HasDb for Request<S>
where
S: highnoon::State + HasDb,
{
fn get_db(&self) -> &Db {
self.state().get_db()
}
}
#[derive(Serialize)]
struct Sample {
data: String,
value: i32,
}
#[derive(Default)]
struct ApiState;
#[derive(Default)]
struct ApiContext {
token: Option<String>,
}
impl From<Context> for ApiContext {
fn from(_: Context) -> Self {
ApiContext::default()
}
}
impl highnoon::State for ApiState {
type Context = ApiContext;
fn new_context(&self) -> ApiContext {
ApiContext::default()
}
}
struct AuthCheck;
#[async_trait::async_trait]
impl highnoon::filter::Filter<ApiState> for AuthCheck {
async fn apply(
&self,
mut req: Request<ApiState>,
next: Next<'_, ApiState>,
) -> Result<Response> {
let auth = req.header::<Authorization<Bearer>>();
match auth {
None => return Ok(Response::status(StatusCode::UNAUTHORIZED)),
Some(bearer) => {
info!("got bearer token: {}", bearer.0.token());
req.context_mut().token = Some(bearer.0.token().to_owned());
next.next(req).await
}
}
}
}
fn error_example(req: &Request<State>) -> Result<()> {
let fail = req.param("fail")?.parse::<bool>()?;
if fail {
Err(Error::bad_request("you asked for it"))
} else {
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt().compact().init();
let mut app = App::new(State::default());
app.with(highnoon::filter::Log);
let memstore = highnoon::filter::session::MemorySessionStore::new();
app.with(
highnoon::filter::session::SessionFilter::new(memstore)
.with_cookie_name("simple_sid")
.with_expiry(time::Duration::minutes(5))
.with_callback(|cookie| {
cookie.set_secure(false);
}),
);
app.at("/hello")
.get(|_req| async { "Hello world!\n\n" })
.post(|mut req: Request<State>| async move {
let bytes = req.body_bytes().await?;
Ok(bytes)
});
app.at("/echo/:name")
.get(|mut req: Request<State>| async move {
let seen = match req.session().get("seen") {
None => 0,
Some(s) => s.parse()?,
};
let greeting = if seen > 1 {
"You again!"
} else if seen == 1 {
"Welcome back"
} else {
"Hello"
};
req.session().set("seen".to_owned(), (seen + 1).to_string());
let p = req.param("name");
Ok(match p {
Err(_) => format!("{} anonymous\n\n", greeting),
Ok(name) => format!("{} {}\n\n", greeting, name),
})
});
app.at("/db").get(|req: Request<State>| async move {
let db = req.get_db();
format!("database is {:?}", db)
});
app.at("/json").get(|_req| async {
Json(Sample {
data: "hello".to_owned(),
value: 1234,
})
});
app.at("/error/:fail").get(|req| async move {
error_example(&req)?;
Ok("")
});
app.at("/query").get(echo_stuff);
app.at("/ws/:name").ws(|req, mut tx, mut rx| async move {
println!("running the websocket");
let name = req.param("name")?;
while let Some(msg) = rx.recv().await? {
println!("message: {}", msg);
let reply = Message::text(format!("Hello {}, from Highnoon!", name));
tx.send(reply).await?;
}
println!("websocket closed");
Ok(())
});
let mut api = App::new(ApiState::default());
api.with(AuthCheck);
api.at("check").get(|req: Request<ApiState>| async move {
println!("URI: {}", req.uri());
println!("Bearer: {:?}", req.context().token);
StatusCode::OK
});
api.at("user/:name").get(|req: Request<_>| async move {
println!("URI: {}", req.uri());
println!("params: {:?}", req.params());
StatusCode::OK
});
app.at("/api/:version").mount(api);
app.at("/static/*").static_files("examples/resources/");
app.listen("0.0.0.0:8888").await?;
Ok(())
}
async fn echo_stuff(mut req: Request<State>) -> Result<StatusCode> {
let uri = req.uri();
println!("URI: {}", uri);
let method = req.method();
println!("method: {}", method);
let headers = req.headers();
println!("header: {:#?}", headers);
let body = req.body_bytes().await?;
println!("body: {}", String::from_utf8_lossy(&body));
println!("remote addr: {}", req.remote_addr());
Ok(StatusCode::OK)
}