use std::path::PathBuf;
use std::time::SystemTime;
use std::fs::metadata;
-use std::sync::RwLock;
+use std::sync::{Mutex, RwLock};
use anyhow::{bail, Error, format_err};
use hyper::Method;
use serde::Serialize;
use proxmox::api::{ApiMethod, Router, RpcEnvironmentType};
+use proxmox::tools::fs::{create_path, CreateOptions};
+
+use crate::tools::{FileLogger, FileLogOptions};
pub struct ApiConfig {
basedir: PathBuf,
env_type: RpcEnvironmentType,
templates: RwLock<Handlebars<'static>>,
template_files: RwLock<HashMap<String, (SystemTime, PathBuf)>>,
+ request_log: Option<Mutex<FileLogger>>,
}
impl ApiConfig {
env_type,
templates: RwLock::new(Handlebars::new()),
template_files: RwLock::new(HashMap::new()),
+ request_log: None,
})
}
templates.render(name, data).map_err(|err| format_err!("{}", err))
}
}
+
+ pub fn enable_file_log<P>(&mut self, path: P) -> Result<(), Error>
+ where
+ P: Into<PathBuf>
+ {
+ let path: PathBuf = path.into();
+ if let Some(base) = path.parent() {
+ if !base.exists() {
+ let backup_user = crate::backup::backup_user()?;
+ let opts = CreateOptions::new().owner(backup_user.uid).group(backup_user.gid);
+ create_path(base, None, Some(opts)).map_err(|err| format_err!("{}", err))?;
+ }
+ }
+
+ let logger_options = FileLogOptions {
+ append: true,
+ ..Default::default()
+ };
+ self.request_log = Some(Mutex::new(FileLogger::new(&path, logger_options)?));
+
+ Ok(())
+ }
+ pub fn get_file_log(&self) -> Option<&Mutex<FileLogger>> {
+ self.request_log.as_ref()
+ }
}
use std::hash::BuildHasher;
use std::path::{Path, PathBuf};
use std::pin::Pin;
-use std::sync::Arc;
+use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use anyhow::{bail, format_err, Error};
use futures::future::{self, FutureExt, TryFutureExt};
use futures::stream::TryStreamExt;
use hyper::header::{self, HeaderMap};
+use hyper::body::HttpBody;
use hyper::http::request::Parts;
use hyper::{Body, Request, Response, StatusCode};
use lazy_static::lazy_static;
use crate::auth_helpers::*;
use crate::api2::types::Userid;
use crate::tools;
+use crate::tools::FileLogger;
use crate::tools::ticket::Ticket;
use crate::config::cached_user_info::CachedUserInfo;
}
fn log_response(
+ logfile: Option<&Mutex<FileLogger>>,
peer: &std::net::SocketAddr,
method: hyper::Method,
path_query: &str,
log::error!("{} {}: {} {}: [client {}] {}", method.as_str(), path, status.as_str(), reason, peer, message);
}
+ if let Some(logfile) = logfile {
+ let user = match resp.extensions().get::<Userid>() {
+ Some(userid) => userid.as_str(),
+ None => "-",
+ };
+ let now = proxmox::tools::time::epoch_i64();
+ // time format which apache/nginx use (by default), copied from pve-http-server
+ let datetime = proxmox::tools::time::strftime_local("%d/%m/%Y:%H:%M:%S %z", now)
+ .unwrap_or("-".into());
+
+ logfile
+ .lock()
+ .unwrap()
+ .log(format!(
+ "{} - {} [{}] \"{} {}\" {} {}",
+ peer.ip(),
+ user,
+ datetime,
+ method.as_str(),
+ path,
+ status.as_str(),
+ resp.body().size_hint().lower(),
+ ));
+ }
}
fn get_proxied_peer(headers: &HeaderMap) -> Option<std::net::SocketAddr> {
None => self.peer,
};
async move {
- let response = match handle_request(config, req, &peer).await {
+ let response = match handle_request(Arc::clone(&config), req, &peer).await {
Ok(response) => response,
Err(err) => {
let (err, code) = match err.downcast_ref::<HttpError>() {
Response::builder().status(code).body(err.into())?
}
};
- log_response(&peer, method, &path, &response);
+ let logger = config.get_file_log();
+ log_response(logger, &peer, method, &path, &response);
Ok(response)
}
.boxed()
}
};
- Response::builder()
+ let mut resp = Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, ct)
.body(index.into())
- .unwrap()
+ .unwrap();
+
+ if let Some(userid) = userid {
+ resp.extensions_mut().insert(userid);
+ }
+
+ resp
}
fn extension_to_content_type(filename: &Path) -> (&'static str, bool) {
handle_api_request(rpcenv, api_method, formatter, parts, body, uri_param).await
};
- if let Err(err) = result {
- return Ok((formatter.format_error)(err));
+ let mut response = match result {
+ Ok(resp) => resp,
+ Err(err) => (formatter.format_error)(err),
+ };
+
+ if let Some(user) = user {
+ let userid: Userid = user.parse()?;
+ response.extensions_mut().insert(userid);
}
- return result;
+
+ return Ok(response);
}
}