diff options
author | Joris | 2023-11-25 08:59:39 +0100 |
---|---|---|
committer | Joris | 2023-11-25 08:59:39 +0100 |
commit | bb906d8ecc796f6b71dda1851d6bd0aa91c6bce5 (patch) | |
tree | cf98c0d9466e0338992c94060d09ac90862178c5 /src | |
parent | 936871e6ba92a23b1956b30272af8c96951c7c2d (diff) |
Upgrade dependencies
Diffstat (limited to 'src')
-rw-r--r-- | src/assets.rs | 12 | ||||
-rw-r--r-- | src/controller/balance.rs | 6 | ||||
-rw-r--r-- | src/controller/categories.rs | 20 | ||||
-rw-r--r-- | src/controller/error.rs | 10 | ||||
-rw-r--r-- | src/controller/incomes.rs | 23 | ||||
-rw-r--r-- | src/controller/login.rs | 14 | ||||
-rw-r--r-- | src/controller/payments.rs | 22 | ||||
-rw-r--r-- | src/controller/statistics.rs | 6 | ||||
-rw-r--r-- | src/controller/utils.rs | 30 | ||||
-rw-r--r-- | src/main.rs | 51 | ||||
-rw-r--r-- | src/routes.rs | 25 |
11 files changed, 128 insertions, 91 deletions
diff --git a/src/assets.rs b/src/assets.rs index 80f9630..36fab55 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -2,6 +2,7 @@ use sha2::{Digest, Sha256}; use std::collections::HashMap; use std::fs; use std::iter::FromIterator; +use std::fmt::Write; pub fn get() -> HashMap<String, String> { let paths = fs::read_dir("assets").unwrap().map(|e| { @@ -19,9 +20,10 @@ pub fn get() -> HashMap<String, String> { fn sha256(input: Vec<u8>) -> String { let mut hasher = Sha256::new(); hasher.update(input); - hasher - .finalize() - .iter() - .map(|b| format!("{:x}", b)) - .collect() + + let mut res = String::new(); + for byte in hasher.finalize().iter() { + write!(&mut res, "{:x}", byte).unwrap(); + } + res } diff --git a/src/controller/balance.rs b/src/controller/balance.rs index adde8a9..c5d9d4a 100644 --- a/src/controller/balance.rs +++ b/src/controller/balance.rs @@ -1,4 +1,6 @@ -use hyper::{Body, Response}; +use http_body_util::Full; +use hyper::body::Bytes; +use hyper::Response; use std::collections::HashMap; use tera::Context; @@ -9,7 +11,7 @@ use crate::model::user::User; use crate::payer; use crate::templates; -pub async fn get(wallet: &Wallet) -> Response<Body> { +pub async fn get(wallet: &Wallet) -> Response<Full<Bytes>> { let users = db::users::list(&wallet.pool).await; let incomes_from = db::incomes::defined_for_all(&wallet.pool).await; diff --git a/src/controller/categories.rs b/src/controller/categories.rs index b1a3664..ff2d8e7 100644 --- a/src/controller/categories.rs +++ b/src/controller/categories.rs @@ -1,4 +1,6 @@ -use hyper::{Body, Response}; +use http_body_util::Full; +use hyper::body::Bytes; +use hyper::Response; use std::collections::HashMap; use tera::Context; @@ -12,7 +14,7 @@ use crate::validation; pub async fn table( wallet: &Wallet, query: queries::Categories, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let categories = db::categories::list(&wallet.pool).await; let mut context = Context::new(); @@ -29,7 +31,7 @@ pub async fn table( ) } -pub async fn create_form(wallet: &Wallet) -> Response<Body> { +pub async fn create_form(wallet: &Wallet) -> Response<Full<Bytes>> { create_form_feedback(wallet, HashMap::new(), None).await } @@ -37,7 +39,7 @@ async fn create_form_feedback( wallet: &Wallet, form: HashMap<String, String>, error: Option<String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let mut context = Context::new(); context.insert("header", &templates::Header::Categories); context.insert("connected_user", &wallet.user.clone()); @@ -55,7 +57,7 @@ async fn create_form_feedback( pub async fn create( wallet: &Wallet, form: HashMap<String, String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let error = |e: &str| { create_form_feedback(wallet, form.clone(), Some(e.to_string())) }; @@ -73,7 +75,7 @@ pub async fn create( } } -pub async fn update_form(id: i64, wallet: &Wallet) -> Response<Body> { +pub async fn update_form(id: i64, wallet: &Wallet) -> Response<Full<Bytes>> { update_form_feedback(id, wallet, HashMap::new(), None).await } @@ -82,7 +84,7 @@ async fn update_form_feedback( wallet: &Wallet, form: HashMap<String, String>, error: Option<String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let category = db::categories::get(&wallet.pool, id).await; let is_category_used = db::payments::is_category_used(&wallet.pool, id).await; @@ -108,7 +110,7 @@ pub async fn update( id: i64, wallet: &Wallet, form: HashMap<String, String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let error = |e: &str| { update_form_feedback(id, wallet, form.clone(), Some(e.to_string())) }; @@ -126,7 +128,7 @@ pub async fn update( } } -pub async fn delete(id: i64, wallet: &Wallet) -> Response<Body> { +pub async fn delete(id: i64, wallet: &Wallet) -> Response<Full<Bytes>> { if db::categories::delete(&wallet.pool, id).await { utils::redirect("/categories") } else { diff --git a/src/controller/error.rs b/src/controller/error.rs index 2a84255..0f6dcc1 100644 --- a/src/controller/error.rs +++ b/src/controller/error.rs @@ -1,5 +1,7 @@ +use http_body_util::Full; +use hyper::body::Bytes; use hyper::header::CACHE_CONTROL; -use hyper::{Body, Response}; +use hyper::Response; use std::collections::HashMap; use tera::{Context, Tera}; @@ -7,7 +9,11 @@ use crate::controller::utils; use crate::controller::wallet::Wallet; // TODO error code -pub fn error(wallet: &Wallet, title: &str, message: &str) -> Response<Body> { +pub fn error( + wallet: &Wallet, + title: &str, + message: &str, +) -> Response<Full<Bytes>> { utils::with_headers( Response::new( template(&wallet.assets, &wallet.templates, title, message).into(), diff --git a/src/controller/incomes.rs b/src/controller/incomes.rs index cc66ed6..f22098b 100644 --- a/src/controller/incomes.rs +++ b/src/controller/incomes.rs @@ -1,6 +1,8 @@ use chrono::Datelike; use chrono::Utc; -use hyper::{Body, Response}; +use http_body_util::Full; +use hyper::body::Bytes; +use hyper::Response; use std::collections::HashMap; use tera::Context; @@ -13,7 +15,10 @@ use crate::validation; static PER_PAGE: i64 = 10; -pub async fn table(wallet: &Wallet, query: queries::Incomes) -> Response<Body> { +pub async fn table( + wallet: &Wallet, + query: queries::Incomes, +) -> Response<Full<Bytes>> { let page = query.page.unwrap_or(1); let count = db::incomes::count(&wallet.pool).await; let incomes = db::incomes::list(&wallet.pool, page, PER_PAGE).await; @@ -53,7 +58,7 @@ static MONTHS: [&str; 12] = [ pub async fn create_form( wallet: &Wallet, query: queries::Incomes, -) -> Response<Body> { +) -> Response<Full<Bytes>> { create_form_feedback(wallet, query, HashMap::new(), None).await } @@ -62,7 +67,7 @@ async fn create_form_feedback( query: queries::Incomes, form: HashMap<String, String>, error: Option<String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let users = db::users::list(&wallet.pool).await; let mut context = Context::new(); @@ -87,7 +92,7 @@ pub async fn create( wallet: &Wallet, query: queries::Incomes, form: HashMap<String, String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let error = |e: &str| { create_form_feedback(wallet, query, form.clone(), Some(e.to_string())) }; @@ -125,7 +130,7 @@ pub async fn update_form( id: i64, wallet: &Wallet, query: queries::Incomes, -) -> Response<Body> { +) -> Response<Full<Bytes>> { update_form_feedback(id, wallet, query, HashMap::new(), None).await } @@ -135,7 +140,7 @@ async fn update_form_feedback( query: queries::Incomes, form: HashMap<String, String>, error: Option<String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let users = db::users::list(&wallet.pool).await; let income = db::incomes::get(&wallet.pool, id).await; @@ -163,7 +168,7 @@ pub async fn update( wallet: &Wallet, query: queries::Incomes, form: HashMap<String, String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let error = |e: &str| { update_form_feedback( id, @@ -203,7 +208,7 @@ pub async fn delete( id: i64, wallet: &Wallet, query: queries::Incomes, -) -> Response<Body> { +) -> Response<Full<Bytes>> { if db::incomes::delete(&wallet.pool, id).await { utils::redirect(&format!("/incomes?page={}", query.page.unwrap_or(1))) } else { diff --git a/src/controller/login.rs b/src/controller/login.rs index 036e6fc..dd50a3a 100644 --- a/src/controller/login.rs +++ b/src/controller/login.rs @@ -1,6 +1,8 @@ use bcrypt; +use http_body_util::Full; +use hyper::body::Bytes; use hyper::header::SET_COOKIE; -use hyper::{Body, Response}; +use hyper::Response; use sqlx::sqlite::SqlitePool; use std::collections::HashMap; use tera::{Context, Tera}; @@ -18,7 +20,7 @@ pub async fn page( assets: &HashMap<String, String>, templates: &Tera, error: Option<&str>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let connected_user: Option<User> = None; let mut context = Context::new(); @@ -34,7 +36,7 @@ pub async fn login( templates: &Tera, form: HashMap<String, String>, pool: SqlitePool, -) -> Response<Body> { +) -> Response<Full<Bytes>> { match validation::login::login(&form) { Some(login) => { match db::users::get_password_hash(&pool, login.email.clone()).await @@ -88,14 +90,14 @@ async fn server_error( assets: &HashMap<String, String>, templates: &Tera, msg: &str, -) -> Response<Body> { +) -> Response<Full<Bytes>> { page(assets, templates, Some(msg)).await } async fn not_authorized( assets: &HashMap<String, String>, templates: &Tera, -) -> Response<Body> { +) -> Response<Full<Bytes>> { page( assets, templates, @@ -104,7 +106,7 @@ async fn not_authorized( .await } -pub async fn logout(config: &Config, wallet: &Wallet) -> Response<Body> { +pub async fn logout(config: &Config, wallet: &Wallet) -> Response<Full<Bytes>> { if db::users::remove_login_token(&wallet.pool, wallet.user.id).await { with_headers( utils::redirect("/"), diff --git a/src/controller/payments.rs b/src/controller/payments.rs index 2663fa7..8184015 100644 --- a/src/controller/payments.rs +++ b/src/controller/payments.rs @@ -1,5 +1,7 @@ +use http_body_util::Full; +use hyper::body::Bytes; use hyper::header::CONTENT_TYPE; -use hyper::{Body, Response}; +use hyper::Response; use std::collections::HashMap; use tera::Context; @@ -16,7 +18,7 @@ static PER_PAGE: i64 = 10; pub async fn table( wallet: &Wallet, query: queries::Payments, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let page = query.page.unwrap_or(1); let count = db::payments::count(&wallet.pool, &query).await; let payments = @@ -48,7 +50,7 @@ pub async fn table( pub async fn create_form( wallet: &Wallet, query: queries::Payments, -) -> Response<Body> { +) -> Response<Full<Bytes>> { create_form_feedback(wallet, query, HashMap::new(), None).await } @@ -57,7 +59,7 @@ async fn create_form_feedback( query: queries::Payments, form: HashMap<String, String>, error: Option<String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let users = db::users::list(&wallet.pool).await; let categories = db::categories::list(&wallet.pool).await; @@ -82,7 +84,7 @@ pub async fn create( wallet: &Wallet, query: queries::Payments, form: HashMap<String, String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let error = |e: &str| { create_form_feedback(wallet, query, form.clone(), Some(e.to_string())) }; @@ -125,7 +127,7 @@ pub async fn update_form( id: i64, wallet: &Wallet, query: queries::Payments, -) -> Response<Body> { +) -> Response<Full<Bytes>> { update_form_feedback(id, wallet, query, HashMap::new(), None).await } @@ -135,7 +137,7 @@ async fn update_form_feedback( query: queries::Payments, form: HashMap<String, String>, error: Option<String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let payment = db::payments::get_for_form(&wallet.pool, id).await; let users = db::users::list(&wallet.pool).await; let categories = db::categories::list(&wallet.pool).await; @@ -164,7 +166,7 @@ pub async fn update( wallet: &Wallet, query: queries::Payments, form: HashMap<String, String>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let error = |e: &str| { update_form_feedback( id, @@ -207,7 +209,7 @@ pub async fn delete( id: i64, wallet: &Wallet, query: queries::Payments, -) -> Response<Body> { +) -> Response<Full<Bytes>> { if db::payments::delete(&wallet.pool, id).await { let query = queries::Payments { highlight: None, @@ -229,7 +231,7 @@ pub async fn delete( pub async fn search_category( wallet: &Wallet, query: queries::PaymentCategory, -) -> Response<Body> { +) -> Response<Full<Bytes>> { match db::payments::search_category(&wallet.pool, query.payment_name).await { Some(category_id) => utils::with_headers( diff --git a/src/controller/statistics.rs b/src/controller/statistics.rs index 38a5787..eb1e704 100644 --- a/src/controller/statistics.rs +++ b/src/controller/statistics.rs @@ -1,4 +1,6 @@ -use hyper::{Body, Response}; +use http_body_util::Full; +use hyper::body::Bytes; +use hyper::Response; use tera::Context; use crate::controller::utils; @@ -6,7 +8,7 @@ use crate::controller::wallet::Wallet; use crate::db; use crate::templates; -pub async fn get(wallet: &Wallet) -> Response<Body> { +pub async fn get(wallet: &Wallet) -> Response<Full<Bytes>> { let categories = db::categories::list(&wallet.pool).await; let payments = db::payments::list_for_stats(&wallet.pool).await; let incomes = db::incomes::total_each_month(&wallet.pool).await; diff --git a/src/controller/utils.rs b/src/controller/utils.rs index fb1d9ad..1b58c68 100644 --- a/src/controller/utils.rs +++ b/src/controller/utils.rs @@ -1,18 +1,18 @@ +use http_body_util::Full; +use hyper::body::Bytes; use hyper::header::{ HeaderName, HeaderValue, CACHE_CONTROL, CONTENT_TYPE, LOCATION, }; -use hyper::{Body, Response, StatusCode}; +use hyper::{Response, StatusCode}; use std::collections::HashMap; use tera::{Context, Tera}; -use tokio::fs::File; -use tokio_util::codec::{BytesCodec, FramedRead}; use crate::controller::error; pub fn with_headers( - response: Response<Body>, + response: Response<Full<Bytes>>, headers: Vec<(HeaderName, &str)>, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let mut response = response; let response_headers = response.headers_mut(); for (name, value) in headers { @@ -26,7 +26,7 @@ pub fn template( templates: &Tera, path: &str, context: Context, -) -> Response<Body> { +) -> Response<Full<Bytes>> { let mut context = context; context.insert("assets", assets); @@ -47,7 +47,7 @@ fn server_error( assets: &HashMap<String, String>, templates: &Tera, msg: &str, -) -> Response<Body> { +) -> Response<Full<Bytes>> { with_headers( Response::new( error::template(assets, templates, "Erreur serveur", msg).into(), @@ -56,36 +56,34 @@ fn server_error( ) } -pub fn text(str: String) -> Response<Body> { +pub fn text(str: String) -> Response<Full<Bytes>> { let mut response = Response::new(str.into()); *response.status_mut() = StatusCode::OK; response } -pub fn ok() -> Response<Body> { +pub fn ok() -> Response<Full<Bytes>> { let mut response = Response::default(); *response.status_mut() = StatusCode::OK; response } -pub fn redirect(uri: &str) -> Response<Body> { +pub fn redirect(uri: &str) -> Response<Full<Bytes>> { let mut response = Response::default(); *response.status_mut() = StatusCode::MOVED_PERMANENTLY; with_headers(response, vec![(LOCATION, uri), (CACHE_CONTROL, "no-cache")]) } -pub fn not_found() -> Response<Body> { +pub fn not_found() -> Response<Full<Bytes>> { let mut response = Response::default(); *response.status_mut() = StatusCode::NOT_FOUND; response } -pub async fn file(filename: &str, content_type: &str) -> Response<Body> { - if let Ok(file) = File::open(filename).await { - let stream = FramedRead::new(file, BytesCodec::new()); - let body = Body::wrap_stream(stream); +pub async fn file(filename: &str, content_type: &str) -> Response<Full<Bytes>> { + if let Ok(contents) = tokio::fs::read(filename).await { with_headers( - Response::new(body), + Response::new(Full::new(contents.into())), vec![ (CACHE_CONTROL, "max-age=3153600000"), (CONTENT_TYPE, content_type), diff --git a/src/main.rs b/src/main.rs index 65d52df..5f3b8c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,8 @@ -use hyper::service::{make_service_fn, service_fn}; -use hyper::Server; +use hyper::server::conn::http1; +use hyper::service::service_fn; +use hyper_util::rt::TokioIo; use sqlx::sqlite::SqlitePool; -use std::convert::Infallible; +use tokio::net::TcpListener; #[macro_use] extern crate log; @@ -23,7 +24,7 @@ mod validation; use model::config; #[tokio::main] -async fn main() { +async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { env_logger::Builder::from_env( env_logger::Env::default().default_filter_or("warn"), ) @@ -42,26 +43,36 @@ async fn main() { tokio::spawn(jobs::start(config.clone(), pool.clone(), templates.clone())); - let make_svc = make_service_fn(|_conn| { + let listener = TcpListener::bind(config.socket_address).await?; + info!("Starting server at {}", config.socket_address); + + loop { let config = config.clone(); let pool = pool.clone(); let assets = assets.clone(); let templates = templates.clone(); - async move { - Ok::<_, Infallible>(service_fn(move |req| { - routes::routes( - config.clone(), - pool.clone(), - assets.clone(), - templates.clone(), - req, - ) - })) - } - }); + let (stream, _) = listener.accept().await?; - info!("Starting server at {}", config.socket_address); - if let Err(e) = Server::bind(&config.socket_address).serve(make_svc).await { - error!("server error: {}", e); + let io = TokioIo::new(stream); + + tokio::task::spawn(async move { + if let Err(err) = http1::Builder::new() + .serve_connection( + io, + service_fn(move |req| { + routes::routes( + config.clone(), + pool.clone(), + assets.clone(), + templates.clone(), + req, + ) + }), + ) + .await + { + println!("Error serving connection: {:?}", err); + } + }); } } diff --git a/src/routes.rs b/src/routes.rs index ef63c8e..ae87d39 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,4 +1,6 @@ -use hyper::{Body, Method, Request, Response}; +use http_body_util::{BodyExt, Full}; +use hyper::body::{Bytes, Incoming}; +use hyper::{Method, Request, Response}; use serde::Deserialize; use sqlx::sqlite::SqlitePool; use std::collections::HashMap; @@ -19,8 +21,8 @@ pub async fn routes( pool: SqlitePool, assets: HashMap<String, String>, templates: Tera, - request: Request<Body>, -) -> Result<Response<Body>, Infallible> { + request: Request<Incoming>, +) -> Result<Response<Full<Bytes>>, Infallible> { let method = request.method(); let uri = request.uri(); let path = &uri.path().split('/').collect::<Vec<&str>>()[1..]; @@ -68,7 +70,7 @@ pub async fn routes( async fn connected_user( config: &Config, pool: &SqlitePool, - request: &Request<Body>, + request: &Request<Incoming>, ) -> Option<User> { let cookie = request.headers().get("COOKIE")?.to_str().ok()?; let login_token = cookie::extract_token(config, cookie).ok()?; @@ -78,8 +80,8 @@ async fn connected_user( async fn authenticated_routes( config: &Config, wallet: Wallet, - request: Request<Body>, -) -> Response<Body> { + request: Request<Incoming>, +) -> Response<Full<Bytes>> { let method = request.method(); let uri = request.uri(); let path = &uri.path().split('/').collect::<Vec<&str>>()[1..]; @@ -202,7 +204,7 @@ async fn authenticated_routes( _ => controller::error::error( &wallet, "Page introuvable", - "La page que recherchez n’existe pas.", + "La page que vous recherchez n’existe pas.", ), } } @@ -211,9 +213,12 @@ fn parse_query<'a, T: Deserialize<'a>>(query: Option<&'a str>) -> T { serde_urlencoded::from_str(query.unwrap_or("")).unwrap() } -async fn body_form(request: Request<Body>) -> HashMap<String, String> { - match hyper::body::to_bytes(request).await { - Ok(bytes) => form_urlencoded::parse(bytes.as_ref()) +async fn body_form(request: Request<Incoming>) -> HashMap<String, String> { + match request.collect().await { + // Warning: this is a simplified use case. In principle names can appear multiple times in + // a form, and the values should be rolled up into a HashMap<String, Vec<String>>. However + // in this example the simpler approach is sufficient. + Ok(content) => form_urlencoded::parse(content.to_bytes().as_ref()) .into_owned() .collect::<HashMap<String, String>>(), Err(_) => HashMap::new(), |