Skip to content

Commit

Permalink
Closes #593
Browse files Browse the repository at this point in the history
  • Loading branch information
ja573 committed Nov 22, 2024
1 parent de2d0c6 commit 4fc4f79
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 37 deletions.
32 changes: 17 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion thoth-api-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ thoth-api = { version = "=0.13.0", path = "../thoth-api", features = ["backend"]
thoth-errors = { version = "=0.13.0", path = "../thoth-errors" }
actix-web = "4.9"
actix-cors = "0.7.0"
actix-http = "3.9.0"
actix-identity = "0.7.1"
actix-session = { version = "0.9.0", features = ["cookie-session"] }
env_logger = "0.11.5"
juniper = "0.16.1"
futures-util = "0.3.31"
log = "0.4.21"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
42 changes: 21 additions & 21 deletions thoth-api-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
mod graphiql;
mod logger;

use std::time::Duration;
use std::{io, sync::Arc};
use std::{io, sync::Arc, time::Duration};

use actix_cors::Cors;
use actix_identity::{Identity, IdentityMiddleware};
use actix_session::config::PersistentSession;
use actix_session::{storage::CookieSessionStore, SessionMiddleware};
use actix_session::{config::PersistentSession, storage::CookieSessionStore, SessionMiddleware};
use actix_web::{
cookie::time::Duration as CookieDuration, cookie::Key, error, get, http::header,
middleware::Logger, post, web::Data, web::Json, App, Error, HttpMessage, HttpRequest,
HttpResponse, HttpServer, Result,
cookie::{time::Duration as CookieDuration, Key},
error, get,
http::header,
middleware::Compress,
post,
web::{Data, Json},
App, Error, HttpMessage, HttpRequest, HttpResponse, HttpServer, Result,
};
use juniper::http::GraphQLRequest;
use serde::Serialize;
use thoth_api::{
account::model::AccountDetails,
account::model::DecodedToken,
account::model::LoginCredentials,
account::service::get_account,
account::service::get_account_details,
account::service::login,
db::init_pool,
db::PgPool,
graphql::model::Context,
graphql::model::{create_schema, Schema},
account::model::{AccountDetails, DecodedToken, LoginCredentials},
account::service::{get_account, get_account_details, login},
db::{init_pool, PgPool},
graphql::{
model::{create_schema, Context, Schema},
GraphQLRequest,
},
};
use thoth_errors::ThothError;

use crate::graphiql::graphiql_source;

const LOG_FORMAT: &str = r#"%{r}a %a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T"#;
use crate::logger::{BodyLogger, Logger};

#[derive(Serialize)]
struct ApiConfig {
Expand Down Expand Up @@ -201,7 +199,9 @@ pub async fn start_server(

HttpServer::new(move || {
App::new()
.wrap(Logger::new(LOG_FORMAT))
.wrap(Compress::default())
.wrap(Logger::default())
.wrap(BodyLogger)
.wrap(IdentityMiddleware::default())
.wrap(
SessionMiddleware::builder(
Expand Down
93 changes: 93 additions & 0 deletions thoth-api-server/src/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::{
future::{ready, Ready},
rc::Rc,
};

use actix_http::h1;
use actix_web::{
dev::{self, Payload, Service, ServiceRequest, ServiceResponse, Transform},
middleware, web, Error, HttpMessage,
};
use futures_util::future::LocalBoxFuture;

const LOG_FORMAT: &str = r#"%{r}a %a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T %{QUERY}xi"#;
pub(crate) struct Logger;
pub(crate) struct BodyLogger;

impl Logger {
pub(crate) fn default() -> middleware::Logger {
middleware::Logger::new(LOG_FORMAT).custom_request_replace("QUERY", Self::get_request_body)
}

fn format_request_body(body: &web::Bytes) -> String {
// Pretty print request body when logging level is Debug
if log::log_enabled!(log::Level::Debug) {
return format!("\n{}", String::from_utf8_lossy(body).replace("\\n", "\n"));
}
format!("\n{}", String::from_utf8_lossy(body))
}

fn get_request_body(req: &ServiceRequest) -> String {
if let Some(body) = req.extensions().get::<web::Bytes>() {
return Self::format_request_body(body);
}
"".to_string()
}
}

impl<S, B> Transform<S, ServiceRequest> for BodyLogger
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Transform = BodyLoggerMiddleware<S>;
type InitError = ();
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(BodyLoggerMiddleware {
service: Rc::new(service),
}))
}
}

pub(crate) struct BodyLoggerMiddleware<S> {
service: Rc<S>,
}

impl<S, B> Service<ServiceRequest> for BodyLoggerMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
S::Future: 'static,
{
type Response = ServiceResponse<B>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

dev::forward_ready!(service);

fn call(&self, mut req: ServiceRequest) -> Self::Future {
let svc = self.service.clone();

Box::pin(async move {
// store request body in the request extensions container for retrieval by logger later
// only store GraphQL queries to avoid logging credentials
if req.path().eq("/graphql") {
let body = req.extract::<web::Bytes>().await.unwrap();
req.extensions_mut().insert(body.clone());
req.set_payload(bytes_to_payload(body));
}

let res = svc.call(req).await?;
Ok(res)
})
}
}

fn bytes_to_payload(buf: web::Bytes) -> Payload {
let (_, mut pl) = h1::Payload::create(true);
pl.unread_data(buf);
dev::Payload::from(pl)
}

0 comments on commit 4fc4f79

Please sign in to comment.