Migrate to actix-web #8

Manually merged
tlater merged 14 commits from tlater/actix-web into master 2022-09-16 17:44:53 +01:00
4 changed files with 263 additions and 95 deletions
Showing only changes of commit 627a7525f3 - Show all commits

157
Cargo.lock generated
View file

@ -204,12 +204,63 @@ dependencies = [
"syn",
]
[[package]]
name = "actix-web-flash-messages"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05c89d051c33e7044e8a6e811ed06dda6ce65dfb51a1e379146f723116ca362a"
dependencies = [
"actix-web",
"anyhow",
"percent-encoding",
"serde",
"serde_json",
"thiserror",
"time",
"tokio",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array",
]
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "ahash"
version = "0.7.6"
@ -245,6 +296,12 @@ dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "anyhow"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
[[package]]
name = "askama_escape"
version = "0.10.3"
@ -340,6 +397,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
]
[[package]]
name = "clap"
version = "3.2.17"
@ -391,7 +457,14 @@ version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05"
dependencies = [
"aes-gcm",
"base64",
"hkdf",
"hmac",
"percent-encoding",
"rand",
"sha2",
"subtle",
"time",
"version_check",
]
@ -424,6 +497,15 @@ dependencies = [
"typenum",
]
[[package]]
name = "ctr"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
dependencies = [
"cipher",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@ -445,6 +527,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -552,6 +635,16 @@ dependencies = [
"wasi",
]
[[package]]
name = "ghash"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "h2"
version = "0.3.13"
@ -607,6 +700,24 @@ dependencies = [
"libc",
]
[[package]]
name = "hkdf"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "http"
version = "0.2.8"
@ -801,6 +912,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
version = "6.3.0"
@ -898,6 +1015,18 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "polyval"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
@ -1103,6 +1232,17 @@ dependencies = [
"digest",
]
[[package]]
name = "sha2"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9db03534dff993187064c4e0c05a5708d2a9728ace9a8959b77bedf415dac5"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -1143,6 +1283,12 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.99"
@ -1228,6 +1374,7 @@ version = "0.1.0"
dependencies = [
"actix-files",
"actix-web",
"actix-web-flash-messages",
"clap",
"derive_more",
"env_logger",
@ -1333,6 +1480,16 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "url"
version = "2.2.2"

View file

@ -6,6 +6,7 @@ edition = "2021"
[dependencies]
actix-files = "0.6.2"
actix-web = "4.1.0"
actix-web-flash-messages = {version = "0.4", features = ["cookies"]}
clap = { version = "3.2.17", features = ["derive"] }
derive_more = "0.99.17"
env_logger = "0.9.0"

View file

@ -2,18 +2,19 @@
use std::net::SocketAddr;
use std::path::PathBuf;
use actix_files::NamedFile;
use actix_web::http::{Method, StatusCode};
use actix_web::middleware::{self, ErrorHandlers};
use actix_web::{get, post, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
use actix_web::{
http::{Method, StatusCode},
middleware::{self, ErrorHandlers},
web, App, HttpServer,
};
use clap::Parser;
use handlebars::Handlebars;
use log::info;
use serde::Deserialize;
mod errors;
mod main_pages;
use errors::{generic_error, UserError};
use errors::generic_error;
use main_pages::{mail_post, static_file, template, template_index};
#[derive(Parser, Debug, Clone)]
struct Config {
@ -36,94 +37,6 @@ struct SharedData<'a> {
config: Config,
}
#[get(r"/{filename:.*\.html}")]
async fn template(
shared: web::Data<SharedData<'_>>,
req: HttpRequest,
) -> actix_web::Result<impl Responder> {
let path = req
.match_info()
.query("filename")
.strip_suffix(".html")
.expect("only paths with this suffix should get here");
if shared.handlebars.has_template(path) {
let body = shared
.handlebars
.render(path, &())
.map_err(|_| UserError::InternalError)?;
Ok(HttpResponse::Ok().body(body))
} else {
Err(UserError::NotFound)?
}
}
#[get(r"/")]
async fn template_index(shared: web::Data<SharedData<'_>>) -> actix_web::Result<impl Responder> {
if shared.handlebars.has_template("index") {
let body = shared
.handlebars
.render("index", &())
.map_err(|_| UserError::InternalError)?;
Ok(HttpResponse::Ok().body(body))
} else {
Err(UserError::NotFound)?
}
}
#[get("/{filename:.*[^/]+}")]
async fn static_file(
shared: web::Data<SharedData<'_>>,
req: HttpRequest,
) -> actix_web::Result<impl Responder> {
let requested = req.match_info().query("filename");
match shared
.config
.template_directory
.join(requested)
.canonicalize()
{
// We only want to serve paths that are both valid *and* in
// the template directory.
//
// i.e., don't serve up /etc/passwd
Ok(path) if path.starts_with(&shared.config.template_directory) => {
let file = NamedFile::open_async(path)
.await
.map_err(|_| UserError::NotFound)?;
Ok(file.use_last_modified(false).respond_to(&req))
}
// Any other cases should 404
_ => Err(UserError::NotFound)?,
}
}
#[derive(Clone, Debug, Deserialize)]
struct Mail {
mail: String,
subject: String,
message: String,
}
#[post("/mail.html")]
async fn mail_post(
shared: web::Data<SharedData<'_>>,
form: web::Form<Mail>,
) -> actix_web::Result<impl Responder> {
info!("{:?}", form);
if shared.handlebars.has_template("mail") {
let body = shared
.handlebars
.render("mail", &())
.map_err(|_| UserError::InternalError)?;
Ok(HttpResponse::Ok().body(body))
} else {
Err(UserError::InternalError)?
}
}
#[actix_web::main]
async fn main() -> Result<(), std::io::Error> {
let mut config = Config::parse();

97
src/main_pages.rs Normal file
View file

@ -0,0 +1,97 @@
use actix_files::NamedFile;
use actix_web::{get, post, web, HttpRequest, HttpResponse, Responder};
use log::info;
use serde::Deserialize;
use crate::errors::UserError;
use crate::SharedData;
#[derive(Clone, Debug, Deserialize)]
pub(crate) struct Mail {
mail: String,
subject: String,
message: String,
}
#[get(r"/")]
pub(crate) async fn template_index(
shared: web::Data<SharedData<'_>>,
) -> actix_web::Result<impl Responder> {
if shared.handlebars.has_template("index") {
let body = shared
.handlebars
.render("index", &())
.map_err(|_| UserError::InternalError)?;
Ok(HttpResponse::Ok().body(body))
} else {
Err(UserError::NotFound)?
}
}
#[get(r"/{filename:.*\.html}")]
pub(crate) async fn template(
shared: web::Data<SharedData<'_>>,
req: HttpRequest,
) -> actix_web::Result<impl Responder> {
let path = req
.match_info()
.query("filename")
.strip_suffix(".html")
.expect("only paths with this suffix should get here");
if shared.handlebars.has_template(path) {
let body = shared
.handlebars
.render(path, &())
.map_err(|_| UserError::InternalError)?;
Ok(HttpResponse::Ok().body(body))
} else {
Err(UserError::NotFound)?
}
}
#[get("/{filename:.*[^/]+}")]
pub(crate) async fn static_file(
shared: web::Data<SharedData<'_>>,
req: HttpRequest,
) -> actix_web::Result<impl Responder> {
let requested = req.match_info().query("filename");
match shared
.config
.template_directory
.join(requested)
.canonicalize()
{
// We only want to serve paths that are both valid *and* in
// the template directory.
//
// i.e., don't serve up /etc/passwd
Ok(path) if path.starts_with(&shared.config.template_directory) => {
let file = NamedFile::open_async(path)
.await
.map_err(|_| UserError::NotFound)?;
Ok(file.use_last_modified(false).respond_to(&req))
}
// Any other cases should 404
_ => Err(UserError::NotFound)?,
}
}
#[post("/mail.html")]
pub(crate) async fn mail_post(
shared: web::Data<SharedData<'_>>,
form: web::Form<Mail>,
) -> actix_web::Result<impl Responder> {
info!("{:?}", form);
if shared.handlebars.has_template("mail") {
let body = shared
.handlebars
.render("mail", &())
.map_err(|_| UserError::InternalError)?;
Ok(HttpResponse::Ok().body(body))
} else {
Err(UserError::InternalError)?
}
}