Compare commits

...

4 Commits

Author SHA1 Message Date
Dorian e627581f66 Merge pull request 'axum-migration' (#1) from axum-migration into main
Reviewed-on: #1
2024-02-07 15:03:14 -05:00
Dorian Pula 9ecfb109b4 Add logging via tracing and improve managing JSON request bodies. 2024-02-07 15:01:15 -05:00
Dorian Pula 6a7869a80c Break out into using axum extractor.
Add support for JSON extraction.
2024-02-07 10:12:40 -05:00
Dorian Pula 2ed659e3ad Migrate to using axum over actix for web framework. 2024-02-06 15:51:30 -05:00
4 changed files with 75 additions and 39 deletions

1
.gitignore vendored
View File

@ -19,3 +19,4 @@ Cargo.lock
# Added by cargo # Added by cargo
/target /target
.idea

View File

@ -1,6 +1,6 @@
[package] [package]
name = "mirror-server" name = "mirror-server"
version = "0.2.0" version = "0.3.0"
edition = "2021" edition = "2021"
authors = ["Dorian Pula <dorian.pula@amber-penguin-software.ca>"] authors = ["Dorian Pula <dorian.pula@amber-penguin-software.ca>"]
description = "A simple server for mirror HTTP requests for testing." description = "A simple server for mirror HTTP requests for testing."
@ -16,6 +16,12 @@ section = "web"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
actix-web = "4" axum = "0.7"
clap = { version = "4.0", features = ["derive"] } clap = { version = "4.4", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
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"

View File

@ -11,13 +11,14 @@ A simple server for mirroring HTTP requests for testing.
* Create a DEB package: * Create a DEB package:
* Install cargo-deb: `cargo install cargo-deb` * Install cargo-deb: `cargo install cargo-deb`
* Create the DEB package: `cargo deb` * Create the DEB package: `cargo deb`
* Faster builds using [cargo-watch](https://watchexec.github.io/#cargo-watch): `cargo watch -x run`
## Install ## Install
Download the DEB file, and install it: Download the DEB file, and install it:
```bash ```bash
VERSION=0.2.0 VERSION=0.3.0
REPO_URL=https://code.birch-tree.net/api/packages/dorian/generic/mirror-server/ REPO_URL=https://code.birch-tree.net/api/packages/dorian/generic/mirror-server/
curl "${REPO_URL}/${VERSION}/mirror-server_${VERSION}_amd64.deb" curl "${REPO_URL}/${VERSION}/mirror-server_${VERSION}_amd64.deb"
sudo dpkg -i "mirror-server-${VERSION}_amd64.deb" sudo dpkg -i "mirror-server-${VERSION}_amd64.deb"
@ -25,6 +26,8 @@ sudo dpkg -i "mirror-server-${VERSION}_amd64.deb"
## TODO ## TODO
* [ ] Add logging to server. * [x] Migrate actix to axum for easier maintainability.
* [x] Add mirroring of JSON request.
* [x] Add logging to server.
* [ ] Add convenience path to access logging for a certain date / records. * [ ] Add convenience path to access logging for a certain date / records.
* [ ] Dockerize mirror-server for easier distribution. * [ ] Dockerize mirror-server for easier distribution.

View File

@ -1,7 +1,14 @@
use actix_web::{web::Json, App, HttpRequest, HttpServer, Responder}; use axum::{
extract::{Host, OriginalUri, Json},
http::{header::HeaderMap, Method},
Router,
};
use clap::Parser; use clap::Parser;
use serde::Serialize; use serde::Serialize;
use serde_json::Value;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use tower_http::trace;
use tracing::{Level, info, warn};
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -15,50 +22,69 @@ struct CliArgs {
ips: String, ips: String,
} }
#[derive(Serialize)]
struct EchoHeader {
name: String,
value: String,
}
#[derive(Serialize)] #[derive(Serialize)]
struct EchoResponse { struct EchoResponse {
method: String, method: String,
path: String, path: String,
host: String, host: String,
headers: BTreeMap<String, String>, headers: BTreeMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
body: Option<Value>,
} }
impl EchoResponse { async fn echo_request(
fn new(req: &HttpRequest) -> Self { method: Method,
let req_uri = req.uri(); original_uri: OriginalUri,
let mut headers = BTreeMap::new(); host: Host,
for (header_name, header_value) in req.headers().iter() { header_map: HeaderMap,
headers.insert( body: Option<Json<Value>>
header_name.to_string(), ) -> Json<EchoResponse> {
header_value.to_str().unwrap_or("ERROR").to_string(), let method = method.to_string();
); let host = host.0;
} let path = original_uri.path().to_string();
EchoResponse { let headers = header_map
method: req.method().to_string(), .iter()
path: req_uri.path().to_string(), .map(|(name, value)| (name.to_string(), value.to_str().unwrap_or("").to_string()))
host: req_uri.host().unwrap_or("").to_string(), .collect();
headers,
} let json_body = match body {
} None => {
warn!("Received a non-JSON body.");
None
},
Some(Json(value)) => Some(value),
};
let response = EchoResponse {
method,
host,
path,
headers,
body: json_body,
};
Json(response)
} }
async fn echo_request(request: HttpRequest) -> actix_web::Result<impl Responder> { #[tokio::main]
let echo = EchoResponse::new(&request); async fn main() {
Ok(Json(echo))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let cli_args = CliArgs::parse(); let cli_args = CliArgs::parse();
let listen_on = (cli_args.ips, cli_args.port); let listen_on = (cli_args.ips, cli_args.port);
HttpServer::new(|| App::new().default_service(actix_web::web::route().to(echo_request))) let listen_on = format!("{ip}:{port}", ip = listen_on.0, port = listen_on.1);
.bind(listen_on)?
.run() // 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 .await
.unwrap_or_else(|_| panic!("Failed to binding to {}", listen_on));
axum::serve(listener, app)
.await
.expect("Server should start");
} }