From 2ed659e3ad5a976464993f92cd489a8d92ef68b0 Mon Sep 17 00:00:00 2001 From: Dorian Pula Date: Tue, 6 Feb 2024 15:51:30 -0500 Subject: [PATCH 1/3] Migrate to using axum over actix for web framework. --- .gitignore | 1 + Cargo.toml | 5 +++-- README.md | 1 + src/main.rs | 27 +++++++++++++++++---------- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 193d30e..8cc11e7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ Cargo.lock # Added by cargo /target +.idea diff --git a/Cargo.toml b/Cargo.toml index 92ac5e2..49d8d59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ section = "web" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-web = "4" -clap = { version = "4.0", features = ["derive"] } +axum = "0.7" +clap = { version = "4.4", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } +tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] } diff --git a/README.md b/README.md index ba07af9..55522d1 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ sudo dpkg -i "mirror-server-${VERSION}_amd64.deb" ## TODO +* [ ] Migrate actix to axum for easier maintainability. * [ ] Add logging to server. * [ ] Add convenience path to access logging for a certain date / records. * [ ] Dockerize mirror-server for easier distribution. diff --git a/src/main.rs b/src/main.rs index 1e241bb..77e5d9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,7 @@ -use actix_web::{web::Json, App, HttpRequest, HttpServer, Responder}; +use axum::{ + extract::{Json, Request}, + Router, +}; use clap::Parser; use serde::Serialize; use std::collections::BTreeMap; @@ -30,7 +33,7 @@ struct EchoResponse { } impl EchoResponse { - fn new(req: &HttpRequest) -> Self { + fn new(req: &Request) -> Self { let req_uri = req.uri(); let mut headers = BTreeMap::new(); for (header_name, header_value) in req.headers().iter() { @@ -48,17 +51,21 @@ impl EchoResponse { } } -async fn echo_request(request: HttpRequest) -> actix_web::Result { - let echo = EchoResponse::new(&request); - Ok(Json(echo)) +async fn echo_request(request: Request) -> Json { + Json(EchoResponse::new(&request)) } -#[actix_web::main] -async fn main() -> std::io::Result<()> { +#[tokio::main] +async fn main() { let cli_args = CliArgs::parse(); let listen_on = (cli_args.ips, cli_args.port); - HttpServer::new(|| App::new().default_service(actix_web::web::route().to(echo_request))) - .bind(listen_on)? - .run() + let listen_on = format!("{ip}:{port}", ip = listen_on.0, port = listen_on.1); + + let app = Router::new().fallback(echo_request); + let listener = tokio::net::TcpListener::bind(&listen_on) .await + .expect(&format!("Attempted binding to {}", listen_on)); + axum::serve(listener, app) + .await + .expect("Server should start"); } From 6a7869a80cd0633ebc512711cda29ab097fc48e1 Mon Sep 17 00:00:00 2001 From: Dorian Pula Date: Wed, 7 Feb 2024 10:12:40 -0500 Subject: [PATCH 2/3] Break out into using axum extractor. Add support for JSON extraction. --- Cargo.toml | 1 + README.md | 4 +++- src/main.rs | 57 ++++++++++++++++++++++++++++++++--------------------- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 49d8d59..f80cc59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ section = "web" axum = "0.7" clap = { version = "4.4", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.113" tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] } diff --git a/README.md b/README.md index 55522d1..8f0c175 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ A simple server for mirroring HTTP requests for testing. * Create a DEB package: * Install cargo-deb: `cargo install cargo-deb` * Create the DEB package: `cargo deb` +* Faster builds using [cargo-watch](https://watchexec.github.io/#cargo-watch): `cargo watch -x run` ## Install @@ -25,7 +26,8 @@ sudo dpkg -i "mirror-server-${VERSION}_amd64.deb" ## TODO -* [ ] Migrate actix to axum for easier maintainability. +* [x] Migrate actix to axum for easier maintainability. +* [x] Add mirroring of JSON request. * [ ] Add logging to server. * [ ] Add convenience path to access logging for a certain date / records. * [ ] Dockerize mirror-server for easier distribution. diff --git a/src/main.rs b/src/main.rs index 77e5d9c..292a60c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ use axum::{ - extract::{Json, Request}, + extract::{Host, OriginalUri, Json}, + http::{header::HeaderMap, Method}, Router, }; use clap::Parser; use serde::Serialize; +use serde_json::{json, Value}; use std::collections::BTreeMap; #[derive(Parser)] @@ -30,29 +32,40 @@ struct EchoResponse { path: String, host: String, headers: BTreeMap, + body: Value, } -impl EchoResponse { - fn new(req: &Request) -> Self { - let req_uri = req.uri(); - let mut headers = BTreeMap::new(); - for (header_name, header_value) in req.headers().iter() { - headers.insert( - header_name.to_string(), - header_value.to_str().unwrap_or("ERROR").to_string(), - ); - } - EchoResponse { - method: req.method().to_string(), - path: req_uri.path().to_string(), - host: req_uri.host().unwrap_or("").to_string(), - headers, - } - } -} +async fn echo_request( + method: Method, + original_uri: OriginalUri, + host: Host, + header_map: HeaderMap, + body: Option> +) -> Json { + let method = method.to_string(); + let host = host.0; + let path = original_uri.path().to_string(); + let headers = header_map + .iter() + .map(|(name, value)| (name.to_string(), value.to_str().unwrap_or("").to_string())) + .collect(); + // TODO: Logging the method and path. -async fn echo_request(request: Request) -> Json { - Json(EchoResponse::new(&request)) + let json_body = match body { + None => { + // TODO: Adding logging. + json!({"error": "Non-JSON request sent."}) + }, + Some(Json(value)) => value, + }; + let response = EchoResponse { + method, + host, + path, + headers, + body: json_body, + }; + Json(response) } #[tokio::main] @@ -64,7 +77,7 @@ async fn main() { let app = Router::new().fallback(echo_request); let listener = tokio::net::TcpListener::bind(&listen_on) .await - .expect(&format!("Attempted binding to {}", listen_on)); + .unwrap_or_else(|_| panic!("Attempted binding to {}", listen_on)); axum::serve(listener, app) .await .expect("Server should start"); From 9ecfb109b431aa27aa6fcde1ea41523123223d6d Mon Sep 17 00:00:00 2001 From: Dorian Pula Date: Wed, 7 Feb 2024 15:01:15 -0500 Subject: [PATCH 3/3] Add logging via tracing and improve managing JSON request bodies. --- Cargo.toml | 10 +++++++--- README.md | 4 ++-- src/main.rs | 34 ++++++++++++++++++++-------------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f80cc59..afabaa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mirror-server" -version = "0.2.0" +version = "0.3.0" edition = "2021" authors = ["Dorian Pula "] description = "A simple server for mirror HTTP requests for testing." @@ -19,5 +19,9 @@ section = "web" axum = "0.7" clap = { version = "4.4", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.113" -tokio = { version = "1.36.0", features = ["macros", "rt-multi-thread"] } +serde_json = "1.0" +tokio = { version = "1.36", features = ["macros", "rt-multi-thread"] } +tower = "0.4" +tower-http = { version = "0.5", features = ["trace"] } +tracing = "0.1" +tracing-subscriber = "0.3" diff --git a/README.md b/README.md index 8f0c175..813c531 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A simple server for mirroring HTTP requests for testing. Download the DEB file, and install it: ```bash -VERSION=0.2.0 +VERSION=0.3.0 REPO_URL=https://code.birch-tree.net/api/packages/dorian/generic/mirror-server/ curl "${REPO_URL}/${VERSION}/mirror-server_${VERSION}_amd64.deb" sudo dpkg -i "mirror-server-${VERSION}_amd64.deb" @@ -28,6 +28,6 @@ sudo dpkg -i "mirror-server-${VERSION}_amd64.deb" * [x] Migrate actix to axum for easier maintainability. * [x] Add mirroring of JSON request. -* [ ] Add logging to server. +* [x] Add logging to server. * [ ] Add convenience path to access logging for a certain date / records. * [ ] Dockerize mirror-server for easier distribution. diff --git a/src/main.rs b/src/main.rs index 292a60c..950bec6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,10 @@ use axum::{ }; use clap::Parser; use serde::Serialize; -use serde_json::{json, Value}; +use serde_json::Value; use std::collections::BTreeMap; +use tower_http::trace; +use tracing::{Level, info, warn}; #[derive(Parser)] #[command(author, version, about, long_about = None)] @@ -20,19 +22,14 @@ struct CliArgs { ips: String, } -#[derive(Serialize)] -struct EchoHeader { - name: String, - value: String, -} - #[derive(Serialize)] struct EchoResponse { method: String, path: String, host: String, headers: BTreeMap, - body: Value, + #[serde(skip_serializing_if = "Option::is_none")] + body: Option, } async fn echo_request( @@ -49,14 +46,13 @@ async fn echo_request( .iter() .map(|(name, value)| (name.to_string(), value.to_str().unwrap_or("").to_string())) .collect(); - // TODO: Logging the method and path. let json_body = match body { None => { - // TODO: Adding logging. - json!({"error": "Non-JSON request sent."}) + warn!("Received a non-JSON body."); + None }, - Some(Json(value)) => value, + Some(Json(value)) => Some(value), }; let response = EchoResponse { method, @@ -74,10 +70,20 @@ async fn main() { let listen_on = (cli_args.ips, cli_args.port); let listen_on = format!("{ip}:{port}", ip = listen_on.0, port = listen_on.1); - let app = Router::new().fallback(echo_request); + // From https://stackoverflow.com/questions/75009289/how-to-enable-logging-tracing-with-axum + tracing_subscriber::fmt() + .with_max_level(Level::INFO) + .init(); + let app = Router::new().fallback(echo_request).layer( + trace::TraceLayer::new_for_http() + .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) + .on_response(trace::DefaultOnResponse::new().level(Level::INFO)), + ); + + info!("Starting the mirror-server to listen to {}", listen_on); let listener = tokio::net::TcpListener::bind(&listen_on) .await - .unwrap_or_else(|_| panic!("Attempted binding to {}", listen_on)); + .unwrap_or_else(|_| panic!("Failed to binding to {}", listen_on)); axum::serve(listener, app) .await .expect("Server should start");