Compare commits
4 Commits
2aff7bb416
...
c60dcce63c
Author | SHA1 | Date |
---|---|---|
meeg_leeto | c60dcce63c | |
meeg_leeto | 364e5e8038 | |
meeg_leeto | d47a38e143 | |
meeg_leeto | b86ead74a4 |
|
@ -1 +1,2 @@
|
|||
/target
|
||||
data/log/*.log
|
108
src/main.rs
108
src/main.rs
|
@ -1,5 +1,4 @@
|
|||
use argh::FromArgs;
|
||||
use core::panic;
|
||||
use validators::traits::ValidateString;
|
||||
use warp::{http::Response, hyper::StatusCode, Filter};
|
||||
|
||||
|
@ -182,8 +181,6 @@ mod conf {
|
|||
ServeFileNotExists(PathBuf),
|
||||
ServeDirNotDir(PathBuf),
|
||||
ServeDirNotExists(PathBuf),
|
||||
BadAccessLogPath,
|
||||
BadErrorLogPath,
|
||||
AccessLogDirectoryNotExists(PathBuf),
|
||||
ErrorLogDirectoryNotExists(PathBuf),
|
||||
}
|
||||
|
@ -227,12 +224,8 @@ mod conf {
|
|||
|
||||
// Check access and error log parent directories
|
||||
// - Access log file
|
||||
let canonical = self
|
||||
.log_rules
|
||||
.access_log_file
|
||||
.canonicalize()
|
||||
.map_err(|_| ConfigParseError::BadAccessLogPath)?;
|
||||
if let Some(parent) = canonical.parent() {
|
||||
let weak_canonical = normalize_path(&self.log_rules.access_log_file);
|
||||
if let Some(parent) = weak_canonical.parent() {
|
||||
if !parent.exists() {
|
||||
return Err(ConfigParseError::AccessLogDirectoryNotExists(
|
||||
parent.to_path_buf(),
|
||||
|
@ -240,12 +233,8 @@ mod conf {
|
|||
}
|
||||
}
|
||||
// - Error log file
|
||||
let canonical = self
|
||||
.log_rules
|
||||
.error_log_file
|
||||
.canonicalize()
|
||||
.map_err(|_| ConfigParseError::BadErrorLogPath)?;
|
||||
if let Some(parent) = canonical.parent() {
|
||||
let weak_canonical = normalize_path(&self.log_rules.error_log_file);
|
||||
if let Some(parent) = weak_canonical.parent() {
|
||||
if !parent.exists() {
|
||||
return Err(ConfigParseError::ErrorLogDirectoryNotExists(
|
||||
parent.to_path_buf(),
|
||||
|
@ -257,6 +246,37 @@ mod conf {
|
|||
}
|
||||
}
|
||||
|
||||
/// Yanked from the source of cargo. Weaker than canonicalize, because it
|
||||
/// doesn't require the target file to exist.
|
||||
fn normalize_path(path: &std::path::Path) -> PathBuf {
|
||||
use std::path::*;
|
||||
|
||||
let mut components = path.components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
impl ConfigParseError {
|
||||
pub fn panic_with_message(self, config_file_name: &str) -> ! {
|
||||
match self {
|
||||
|
@ -316,12 +336,6 @@ mod conf {
|
|||
file.to_string_lossy()
|
||||
)
|
||||
}
|
||||
ConfigParseError::BadAccessLogPath => {
|
||||
eprintln!("Access log path could not be parsed as a canonicalizable path.")
|
||||
}
|
||||
ConfigParseError::BadErrorLogPath => {
|
||||
eprintln!("Error log path could not be parsed as a canonicalizable path.")
|
||||
}
|
||||
ConfigParseError::AccessLogDirectoryNotExists(dir) => {
|
||||
eprintln!("Access log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy())
|
||||
}
|
||||
|
@ -903,11 +917,7 @@ mod service {
|
|||
pub mod log {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use tokio::{
|
||||
fs::OpenOptions,
|
||||
io::{AsyncWriteExt, BufWriter},
|
||||
sync,
|
||||
};
|
||||
use tokio::{fs::OpenOptions, io::AsyncWriteExt, sync};
|
||||
|
||||
/// A struct responsible for logging events, per messages received from
|
||||
/// other processes.
|
||||
|
@ -943,7 +953,7 @@ mod service {
|
|||
/// should decide either to stop logging, ignore these errors, or
|
||||
/// halt the program.
|
||||
pub fn access(&self, msg: String) -> Result<(), ()> {
|
||||
self.access_tx.send(msg).map_err(|e| ())
|
||||
self.access_tx.send(msg).map_err(|_| ())
|
||||
}
|
||||
|
||||
/// Log a message into the error log file.
|
||||
|
@ -953,7 +963,7 @@ mod service {
|
|||
/// should decide either to stop logging, ignore these errors, or
|
||||
/// halt the program.
|
||||
pub fn error(&self, msg: String) -> Result<(), ()> {
|
||||
self.error_tx.send(msg).map_err(|e| ())
|
||||
self.error_tx.send(msg).map_err(|_| ())
|
||||
}
|
||||
|
||||
/// The task responsible for receiving the log messages and actually
|
||||
|
@ -961,7 +971,11 @@ mod service {
|
|||
/// for each target file.
|
||||
async fn logging_task(mut rx: sync::mpsc::UnboundedReceiver<String>, into: PathBuf) {
|
||||
// Open the log file in append mode
|
||||
let file = OpenOptions::new().append(true).open(into.clone()).await;
|
||||
let file = OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(into.clone())
|
||||
.await;
|
||||
if let Err(e) = file {
|
||||
eprintln!(
|
||||
concat!(
|
||||
|
@ -974,11 +988,23 @@ mod service {
|
|||
);
|
||||
return;
|
||||
}
|
||||
let mut file = BufWriter::new(file.unwrap());
|
||||
let mut file = file.unwrap();
|
||||
|
||||
// Listen to the logging message channel
|
||||
while let Some(log) = rx.recv().await {
|
||||
let write_result = file.write(log.as_bytes()).await;
|
||||
let write_result = file
|
||||
.write_buf(
|
||||
&mut format!(
|
||||
"{} ",
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.expect("Bad system time")
|
||||
.as_secs()
|
||||
)
|
||||
.as_bytes(),
|
||||
)
|
||||
.await
|
||||
.and(file.write_buf(&mut log.as_bytes()).await);
|
||||
if let Err(e) = write_result {
|
||||
eprintln!(
|
||||
concat!(
|
||||
|
@ -1005,6 +1031,7 @@ async fn shorten(
|
|||
slug_factory: &slug::SlugFactory,
|
||||
db: &db::SlugDatabase,
|
||||
b64str: &str,
|
||||
logger: &log::Logger,
|
||||
) -> Result<slug::Slug, (StatusCode, String)> {
|
||||
// Parse the URL given by the user. It should arrive as a Base64 string,
|
||||
// and anything other than this should cleanly result in an HTTP rejection.
|
||||
|
@ -1036,10 +1063,15 @@ async fn shorten(
|
|||
|
||||
// ...and attempt to insert it into the database.
|
||||
// Failure to do so is reported to the user.
|
||||
let insert_result = db.insert_slug(new_slug, url).await;
|
||||
let insert_result = db.insert_slug(new_slug, url.clone()).await;
|
||||
match insert_result {
|
||||
Ok(result) => match result {
|
||||
service::db::AddResult::Success(slug) => Ok(slug),
|
||||
service::db::AddResult::Success(slug) => {
|
||||
logger
|
||||
.access(format!("{} -> {}\n", slug.inner_str(), url))
|
||||
.ok();
|
||||
Ok(slug)
|
||||
}
|
||||
service::db::AddResult::Fail => Err((
|
||||
warp::http::StatusCode::INTERNAL_SERVER_ERROR,
|
||||
debuginfo!("Got insertion response, but it was error.").into(),
|
||||
|
@ -1150,12 +1182,14 @@ async fn serve() {
|
|||
// Warp logging compatibility layer
|
||||
let log = warp::log::custom(move |info| {
|
||||
let log_msg = format!(
|
||||
"{} requested {}/{}, replied with status {}",
|
||||
"{} ({}) {} {}, replied with status {}\n",
|
||||
info.remote_addr()
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or_else(|| "".to_string()),
|
||||
info.path(),
|
||||
.unwrap_or_else(|| "<Unknown remote address>".to_string()),
|
||||
info.user_agent()
|
||||
.unwrap_or_else(|| "<No user agent provided>"),
|
||||
info.method(),
|
||||
info.path(),
|
||||
info.status().as_u16(),
|
||||
);
|
||||
if info.status().is_client_error() || info.status().is_server_error() {
|
||||
|
@ -1178,7 +1212,7 @@ async fn serve() {
|
|||
.body(String::new())
|
||||
.unwrap();
|
||||
}
|
||||
match shorten(&slug_factory, &db, b64str.unwrap()).await {
|
||||
match shorten(&slug_factory, &db, b64str.unwrap(), logger).await {
|
||||
Ok(slug) => Response::builder()
|
||||
.body(slug.inner_str().to_string())
|
||||
.unwrap(),
|
||||
|
|
Loading…
Reference in New Issue