From f807e9d1b118c754a1c006095549042d04df3c65 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 24 Mar 2022 08:21:12 -0700 Subject: [PATCH] chore: Run cargo fmt --- src/bindle_util.rs | 98 +++++--- src/dispatcher.rs | 131 +++++++--- src/dynamic_route.rs | 18 +- src/handler_loader/emplacer.rs | 267 +++++++++++++++----- src/handler_loader/loader.rs | 55 +++-- src/handler_loader/mod.rs | 21 +- src/handler_loader/module_loader.rs | 83 ++++--- src/handlers.rs | 29 ++- src/http_util.rs | 8 +- src/lib.rs | 366 +++++++++++++++++++--------- src/main.rs | 8 +- src/wagi_app.rs | 36 +-- src/wagi_config.rs | 3 +- src/wagi_server.rs | 22 +- src/wasm_module.rs | 6 +- src/wasm_runner.rs | 11 +- 16 files changed, 800 insertions(+), 362 deletions(-) diff --git a/src/bindle_util.rs b/src/bindle_util.rs index 241cb1f..73a6a9e 100644 --- a/src/bindle_util.rs +++ b/src/bindle_util.rs @@ -1,4 +1,7 @@ -use std::{collections::{HashMap, HashSet}, iter::FromIterator}; +use std::{ + collections::{HashMap, HashSet}, + iter::FromIterator, +}; use bindle::{Invoice, Parcel}; @@ -45,8 +48,9 @@ impl InvoiceUnderstander { pub fn classify_parcel(&self, parcel: &Parcel) -> Option { // Currently only handlers but we have talked of scheduled tasks etc. parcel.label.feature.as_ref().and_then(|features| { - features.get("wagi").and_then(|wagi_features| { - match wagi_features.get("route") { + features + .get("wagi") + .and_then(|wagi_features| match wagi_features.get("route") { Some(route) => { let handler_info = WagiHandlerInfo { invoice_id: self.id(), @@ -55,21 +59,24 @@ impl InvoiceUnderstander { entrypoint: wagi_features.get("entrypoint").map(|s| s.to_owned()), allowed_hosts: wagi_features.get("allowed_hosts").map(|h| parse_csv(h)), argv: wagi_features.get("argv").map(|s| s.to_owned()), - required_parcels: parcels_required_for(parcel, &self.group_dependency_map), + required_parcels: parcels_required_for( + parcel, + &self.group_dependency_map, + ), }; Some(InterestingParcel::WagiHandler(handler_info)) - }, + } None => None, - } - }) + }) }) } pub fn parse_wagi_handlers(&self) -> Vec { - self - .top_modules().iter() + self.top_modules() + .iter() .filter_map(|parcel| self.classify_parcel(parcel)) - .map(|parcel| match parcel { // If there are other cases of InterestingParcel this may need to become a filter_map, but right now that makes Clippy mad + .map(|parcel| match parcel { + // If there are other cases of InterestingParcel this may need to become a filter_map, but right now that makes Clippy mad InterestingParcel::WagiHandler(h) => h, }) .collect() @@ -93,27 +100,45 @@ pub struct WagiHandlerInfo { impl WagiHandlerInfo { pub fn asset_parcels(&self) -> Vec { - self.required_parcels.iter().filter(|p| is_file(p)).cloned().collect() + self.required_parcels + .iter() + .filter(|p| is_file(p)) + .cloned() + .collect() } } const NO_PARCELS: Vec = vec![]; pub fn is_file(parcel: &Parcel) -> bool { - parcel.label.feature.as_ref().and_then(|features| { - features.get("wagi").map(|wagi_features| { - match wagi_features.get("file") { - Some(s) => s == "true", - _ => false, - } + parcel + .label + .feature + .as_ref() + .and_then(|features| { + features + .get("wagi") + .map(|wagi_features| match wagi_features.get("file") { + Some(s) => s == "true", + _ => false, + }) }) - }).unwrap_or(false) + .unwrap_or(false) } -pub fn parcels_required_for(parcel: &Parcel, full_dep_map: &HashMap>) -> Vec { +pub fn parcels_required_for( + parcel: &Parcel, + full_dep_map: &HashMap>, +) -> Vec { let mut required = HashSet::new(); for group in parcel.directly_requires() { - required.extend(full_dep_map.get(&group).unwrap_or(&NO_PARCELS).iter().cloned()); + required.extend( + full_dep_map + .get(&group) + .unwrap_or(&NO_PARCELS) + .iter() + .cloned(), + ); } Vec::from_iter(required) } @@ -144,7 +169,13 @@ pub fn build_full_memberships(invoice: &Invoice) -> HashMap> for group in direct_memberships.keys() { let mut all_members = HashSet::new(); for dep_group in gg_deps.get(group).unwrap() { - all_members.extend(direct_memberships.get(dep_group).unwrap_or(&NO_PARCELS).iter().cloned()); + all_members.extend( + direct_memberships + .get(dep_group) + .unwrap_or(&NO_PARCELS) + .iter() + .cloned(), + ); } full_memberships.insert(group.to_owned(), Vec::from_iter(all_members)); } @@ -152,17 +183,25 @@ pub fn build_full_memberships(invoice: &Invoice) -> HashMap> full_memberships } -fn group_to_group_direct_dependencies(direct_memberships: &HashMap>) -> HashMap> { +fn group_to_group_direct_dependencies( + direct_memberships: &HashMap>, +) -> HashMap> { let mut ggd = HashMap::new(); for (group, members) in direct_memberships { - let mut directs: Vec<_> = members.iter().flat_map(|parcel| parcel.directly_requires()).collect(); + let mut directs: Vec<_> = members + .iter() + .flat_map(|parcel| parcel.directly_requires()) + .collect(); directs.push(group.to_owned()); ggd.insert(group.to_owned(), directs); } ggd } -fn direct_deps_not_already_in_list(list: &[String], direct_dep_map: &HashMap>) -> Vec { +fn direct_deps_not_already_in_list( + list: &[String], + direct_dep_map: &HashMap>, +) -> Vec { let mut new_dds = vec![]; for group in list { if let Some(child_groups) = direct_dep_map.get(group) { @@ -174,7 +213,9 @@ fn direct_deps_not_already_in_list(list: &[String], direct_dep_map: &HashMap::from_iter(new_dds).into_iter().collect() } -fn group_to_group_full_dependencies(direct_memberships: &HashMap>) -> HashMap> { +fn group_to_group_full_dependencies( + direct_memberships: &HashMap>, +) -> HashMap> { let mut ggd = HashMap::new(); let direct_deps = group_to_group_direct_dependencies(direct_memberships); for (group, directs) in &direct_deps { @@ -210,7 +251,7 @@ impl ParcelUtils for Parcel { } fn parse_csv(text: &str) -> Vec { - text.split(',').map(|v| v.to_owned()).collect() // TODO: trim etc.? + text.split(',').map(|v| v.to_owned()).collect() // TODO: trim etc.? } // Bindle client/auth utils, derived from github.com/deislabs/hippo-cli @@ -273,7 +314,6 @@ impl TokenManager for AnyAuth { } } - #[cfg(test)] mod test { use super::*; @@ -457,7 +497,9 @@ mod test { }; let membership_map = build_full_memberships(&inv); - let members = membership_map.get("coffee").expect("there should have been a group called 'coffee'"); + let members = membership_map + .get("coffee") + .expect("there should have been a group called 'coffee'"); assert_eq!(2, members.len()); } } diff --git a/src/dispatcher.rs b/src/dispatcher.rs index c25578a..448b7ac 100644 --- a/src/dispatcher.rs +++ b/src/dispatcher.rs @@ -1,19 +1,19 @@ use std::net::SocketAddr; -use hyper::{ - http::request::Parts, - Body, Request, Response, StatusCode, -}; +use hyper::{http::request::Parts, Body, Request, Response, StatusCode}; use sha2::{Digest, Sha256}; -use tracing::{instrument}; +use tracing::instrument; -use crate::dynamic_route::{DynamicRoutes, interpret_routes}; +use crate::dynamic_route::{interpret_routes, DynamicRoutes}; use crate::handlers::{RouteHandler, WasmRouteHandler}; -use crate::http_util::{not_found}; +use crate::http_util::not_found; use crate::request::{RequestContext, RequestGlobalContext}; -use crate::handler_loader::{WasmHandlerConfigurationEntry, WasmHandlerConfiguration}; -use crate::wasm_runner::{RunWasmResult, prepare_stdio_streams, prepare_wasm_instance, run_prepared_wasm_instance_if_present, WasmLinkOptions}; +use crate::handler_loader::{WasmHandlerConfiguration, WasmHandlerConfigurationEntry}; +use crate::wasm_runner::{ + prepare_stdio_streams, prepare_wasm_instance, run_prepared_wasm_instance_if_present, + RunWasmResult, WasmLinkOptions, +}; #[derive(Clone, Debug)] pub struct RoutingTable { @@ -51,15 +51,13 @@ impl RoutingTable { match self.route_for(&uri_path) { Ok(rte) => { - let request_context = RequestContext { - client_addr, - }; - let response = rte.handle_request(&parts, data, &request_context, &self.global_context); + let request_context = RequestContext { client_addr }; + let response = + rte.handle_request(&parts, data, &request_context, &self.global_context); Ok(response) - }, + } Err(_) => Ok(not_found()), } - } #[instrument(level = "trace", skip(self))] @@ -139,7 +137,14 @@ impl RoutingTableEntry { match &self.handler_info { RouteHandler::HealthCheck => Response::new(Body::from("OK")), RouteHandler::Wasm(w) => { - let response = w.handle_request(&self.route_pattern, req, body, request_context, global_context, self.unique_key()); + let response = w.handle_request( + &self.route_pattern, + req, + body, + request_context, + global_context, + self.unique_key(), + ); match response { Ok(res) => res, Err(e) => { @@ -150,7 +155,6 @@ impl RoutingTableEntry { srv_err } } - } } } @@ -160,7 +164,7 @@ impl RoutePattern { pub fn parse(path_text: &str) -> Self { match path_text.strip_suffix("/...") { Some(prefix) => Self::Prefix(prefix.to_owned()), - None => Self::Exact(path_text.to_owned()) + None => Self::Exact(path_text.to_owned()), } } @@ -171,19 +175,22 @@ impl RoutePattern { pub fn is_match(&self, uri_fragment: &str) -> bool { match self { Self::Exact(path) => path == uri_fragment, - Self::Prefix(prefix) => prefix == uri_fragment || uri_fragment.starts_with(&format!("{}/", prefix)), + Self::Prefix(prefix) => { + prefix == uri_fragment || uri_fragment.starts_with(&format!("{}/", prefix)) + } } } pub fn script_name(&self) -> String { match self { Self::Exact(path) => path.clone(), - Self::Prefix(prefix) => + Self::Prefix(prefix) => { if prefix.starts_with('/') { prefix.to_owned() } else { format!("/{}", prefix) } + } } } @@ -240,20 +247,28 @@ fn concat_no_duplicate_slash(prefix: &str, suffix: &str) -> String { } impl RoutingTable { - pub fn build(source: &WasmHandlerConfiguration, global_context: RequestGlobalContext) -> anyhow::Result { + pub fn build( + source: &WasmHandlerConfiguration, + global_context: RequestGlobalContext, + ) -> anyhow::Result { let user_entries = Self::build_from_handler_config_entries(&source.entries)?; let full_user_entries = augment_dynamic_routes(user_entries, &global_context)?; let built_in_entries = Self::inbuilt_patterns(); - let entries = built_in_entries.into_iter().chain(full_user_entries).collect(); + let entries = built_in_entries + .into_iter() + .chain(full_user_entries) + .collect(); Ok(Self { entries, global_context, }) } - fn build_from_handler_config_entries(entries: &[WasmHandlerConfigurationEntry]) -> anyhow::Result> { + fn build_from_handler_config_entries( + entries: &[WasmHandlerConfigurationEntry], + ) -> anyhow::Result> { entries .iter() .filter_map(|e| RoutingTableEntry::build_from_handler_config_entry(e)) @@ -261,31 +276,52 @@ impl RoutingTable { } fn inbuilt_patterns() -> Vec { - vec![ - RoutingTableEntry::inbuilt("/healthz", RouteHandler::HealthCheck), - ] + vec![RoutingTableEntry::inbuilt( + "/healthz", + RouteHandler::HealthCheck, + )] } } -fn augment_dynamic_routes(base_entries: Vec, global_context: &RequestGlobalContext) -> anyhow::Result> { - let results: anyhow::Result> = base_entries.into_iter().map(|e| augment_one_with_dynamic_routes(e, global_context)).collect(); +fn augment_dynamic_routes( + base_entries: Vec, + global_context: &RequestGlobalContext, +) -> anyhow::Result> { + let results: anyhow::Result> = base_entries + .into_iter() + .map(|e| augment_one_with_dynamic_routes(e, global_context)) + .collect(); let augmented = results?.into_iter().flatten().collect(); Ok(augmented) } -fn augment_one_with_dynamic_routes(routing_table_entry: RoutingTableEntry, global_context: &RequestGlobalContext) -> anyhow::Result> { +fn augment_one_with_dynamic_routes( + routing_table_entry: RoutingTableEntry, + global_context: &RequestGlobalContext, +) -> anyhow::Result> { match &routing_table_entry.handler_info { - RouteHandler::Wasm(w) => augment_one_wasm_with_dynamic_routes(&routing_table_entry, w, global_context), + RouteHandler::Wasm(w) => { + augment_one_wasm_with_dynamic_routes(&routing_table_entry, w, global_context) + } RouteHandler::HealthCheck => Ok(vec![routing_table_entry]), } } -fn augment_one_wasm_with_dynamic_routes(routing_table_entry: &RoutingTableEntry, wasm_route_handler: &WasmRouteHandler, global_context: &RequestGlobalContext) -> anyhow::Result> { - let redirects = prepare_stdio_streams(vec![] /* TODO: eww */, global_context, routing_table_entry.unique_key())?; +fn augment_one_wasm_with_dynamic_routes( + routing_table_entry: &RoutingTableEntry, + wasm_route_handler: &WasmRouteHandler, + global_context: &RequestGlobalContext, +) -> anyhow::Result> { + let redirects = prepare_stdio_streams( + vec![], /* TODO: eww */ + global_context, + routing_table_entry.unique_key(), + )?; let ctx = build_wasi_context_for_dynamic_route_query(redirects.streams); let link_options = WasmLinkOptions::none(); - let (store, instance) = prepare_wasm_instance(ctx, &wasm_route_handler.wasm_module_source, link_options)?; + let (store, instance) = + prepare_wasm_instance(ctx, &wasm_route_handler.wasm_module_source, link_options)?; match run_prepared_wasm_instance_if_present(instance, store, "_routes") { RunWasmResult::WasmError(e) => Err(e), @@ -294,8 +330,9 @@ fn augment_one_wasm_with_dynamic_routes(routing_table_entry: &RoutingTableEntry, let out = redirects.stdout_mutex.read().unwrap(); let dynamic_routes_text = std::str::from_utf8(&*out)?; let dynamic_routes = interpret_routes(dynamic_routes_text)?; - - let mut dynamic_route_entries = append_all_dynamic_routes(routing_table_entry, wasm_route_handler, dynamic_routes); + + let mut dynamic_route_entries = + append_all_dynamic_routes(routing_table_entry, wasm_route_handler, dynamic_routes); dynamic_route_entries.reverse(); dynamic_route_entries.push(routing_table_entry.clone()); Ok(dynamic_route_entries) @@ -303,23 +340,37 @@ fn augment_one_wasm_with_dynamic_routes(routing_table_entry: &RoutingTableEntry, } } -fn append_all_dynamic_routes(routing_table_entry: &RoutingTableEntry, wasm_route_handler: &WasmRouteHandler, dynamic_routes: DynamicRoutes) -> Vec { +fn append_all_dynamic_routes( + routing_table_entry: &RoutingTableEntry, + wasm_route_handler: &WasmRouteHandler, + dynamic_routes: DynamicRoutes, +) -> Vec { dynamic_routes - .subpath_entrypoints.iter() + .subpath_entrypoints + .iter() .map(|dr| append_one_dynamic_route(routing_table_entry, wasm_route_handler, &dr.0, &dr.1)) .collect() } -fn append_one_dynamic_route(routing_table_entry: &RoutingTableEntry, wasm_route_handler: &WasmRouteHandler, dynamic_route_pattern: &RoutePattern, entrypoint: &str) -> RoutingTableEntry { +fn append_one_dynamic_route( + routing_table_entry: &RoutingTableEntry, + wasm_route_handler: &WasmRouteHandler, + dynamic_route_pattern: &RoutePattern, + entrypoint: &str, +) -> RoutingTableEntry { let mut subpath_handler = wasm_route_handler.clone(); subpath_handler.entrypoint = entrypoint.to_owned(); RoutingTableEntry { - route_pattern: routing_table_entry.route_pattern.append(dynamic_route_pattern), + route_pattern: routing_table_entry + .route_pattern + .append(dynamic_route_pattern), handler_info: RouteHandler::Wasm(subpath_handler), } } -fn build_wasi_context_for_dynamic_route_query(redirects: crate::wasm_module::IOStreamRedirects) -> wasi_common::WasiCtx { +fn build_wasi_context_for_dynamic_route_query( + redirects: crate::wasm_module::IOStreamRedirects, +) -> wasi_common::WasiCtx { let builder = wasi_cap_std_sync::WasiCtxBuilder::new() .stderr(Box::new(redirects.stderr)) .stdout(Box::new(redirects.stdout)); diff --git a/src/dynamic_route.rs b/src/dynamic_route.rs index de62e98..aa2cc36 100644 --- a/src/dynamic_route.rs +++ b/src/dynamic_route.rs @@ -3,7 +3,7 @@ use crate::dispatcher::RoutePattern; pub struct DynamicRoutes { // Using a Vec rather than a HashMap because order matters // (and direct lookup doesn't because some routes may be prefixes) - pub subpath_entrypoints: Vec<(RoutePattern, String)>, // TODO: private + pub subpath_entrypoints: Vec<(RoutePattern, String)>, // TODO: private } pub fn interpret_routes(route_text: impl Into) -> anyhow::Result { @@ -17,7 +17,9 @@ pub fn interpret_routes(route_text: impl Into) -> anyhow::Result, _>>()?; - Ok(DynamicRoutes { subpath_entrypoints: routes }) + Ok(DynamicRoutes { + subpath_entrypoints: routes, + }) } fn parse_dynamic_route(line: &str) -> anyhow::Result<(RoutePattern, String)> { @@ -27,7 +29,10 @@ fn parse_dynamic_route(line: &str) -> anyhow::Result<(RoutePattern, String)> { return Err(anyhow::anyhow!("Dynamic routes contained empty line")); } if parts.len() != 2 { - return Err(anyhow::anyhow!("Dynamic routes contained invalid line {}", line)); + return Err(anyhow::anyhow!( + "Dynamic routes contained invalid line {}", + line + )); } let path_text = parts.get(0).unwrap_or(&"/"); @@ -87,7 +92,10 @@ mod test { assert_eq!(RoutePattern::Prefix("/hello".to_owned()), entrypoints[0].0); assert_eq!("hello", entrypoints[0].1); - assert_eq!(RoutePattern::Prefix("/goodbye".to_owned()), entrypoints[1].0); + assert_eq!( + RoutePattern::Prefix("/goodbye".to_owned()), + entrypoints[1].0 + ); assert_eq!("au_revoir", entrypoints[1].1); } -} \ No newline at end of file +} diff --git a/src/handler_loader/emplacer.rs b/src/handler_loader/emplacer.rs index 8569a08..6068d8b 100644 --- a/src/handler_loader/emplacer.rs +++ b/src/handler_loader/emplacer.rs @@ -1,4 +1,8 @@ -use std::{collections::HashMap, path::{Path, PathBuf}, sync::Arc}; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::Arc, +}; use anyhow::Context; use bindle::Invoice; @@ -39,16 +43,22 @@ pub struct Bits { impl Emplacer { async fn new(configuration: &WagiConfiguration) -> anyhow::Result { - Self::new_from_settings( - &configuration.asset_cache_dir, - &configuration.handlers - ).await + Self::new_from_settings(&configuration.asset_cache_dir, &configuration.handlers).await } - async fn new_from_settings(asset_cache_dir: &Path, handlers: &HandlerConfigurationSource) -> anyhow::Result { + async fn new_from_settings( + asset_cache_dir: &Path, + handlers: &HandlerConfigurationSource, + ) -> anyhow::Result { let cache_path = asset_cache_dir.to_owned(); - tokio::fs::create_dir_all(&cache_path).await - .with_context(|| format!("Can't create asset cache directory {}", cache_path.display()))?; + tokio::fs::create_dir_all(&cache_path) + .await + .with_context(|| { + format!( + "Can't create asset cache directory {}", + cache_path.display() + ) + })?; Ok(Self { cache_path, source: handlers.clone(), @@ -57,20 +67,32 @@ impl Emplacer { pub async fn emplace_all(self) -> anyhow::Result { match self.source.clone() { - HandlerConfigurationSource::ModuleConfigFile(path) => - Ok(EmplacedHandlerConfiguration::ModuleMapFile(path.clone())), - HandlerConfigurationSource::StandaloneBindle(bindle_base_dir, id) => - self.emplace_standalone_bindle(&bindle_base_dir, &id).await, - HandlerConfigurationSource::RemoteBindle(bindle_connection_info, id) => - self.emplace_remote_bindle(bindle_connection_info, &id).await, - }.with_context(|| "Error caching assets from bindle") + HandlerConfigurationSource::ModuleConfigFile(path) => { + Ok(EmplacedHandlerConfiguration::ModuleMapFile(path.clone())) + } + HandlerConfigurationSource::StandaloneBindle(bindle_base_dir, id) => { + self.emplace_standalone_bindle(&bindle_base_dir, &id).await + } + HandlerConfigurationSource::RemoteBindle(bindle_connection_info, id) => { + self.emplace_remote_bindle(bindle_connection_info, &id) + .await + } + } + .with_context(|| "Error caching assets from bindle") } // TODO: NO! NO! NO! pub async fn get_bits_for(&self, handler: &WagiHandlerInfo) -> anyhow::Result { let module_parcel_path = self.module_parcel_path(&handler.parcel); - let wasm_module = tokio::fs::read(&module_parcel_path).await - .with_context(|| format!("Error reading module {} from cache path {}", handler.parcel.label.name, module_parcel_path.display()))?; + let wasm_module = tokio::fs::read(&module_parcel_path) + .await + .with_context(|| { + format!( + "Error reading module {} from cache path {}", + handler.parcel.label.name, + module_parcel_path.display() + ) + })?; let volume_mounts = if handler.asset_parcels().is_empty() { HashMap::new() @@ -83,74 +105,143 @@ impl Emplacer { }) } - async fn emplace_standalone_bindle(self, bindle_base_dir: &Path, id: &bindle::Id) -> anyhow::Result { - let reader = bindle::standalone::StandaloneRead::new(bindle_base_dir, id).await - .with_context(|| format!("Error constructing bindle reader for {} in {}", id, bindle_base_dir.display()))?; + async fn emplace_standalone_bindle( + self, + bindle_base_dir: &Path, + id: &bindle::Id, + ) -> anyhow::Result { + let reader = bindle::standalone::StandaloneRead::new(bindle_base_dir, id) + .await + .with_context(|| { + format!( + "Error constructing bindle reader for {} in {}", + id, + bindle_base_dir.display() + ) + })?; self.emplace_bindle(&reader, id).await } - async fn emplace_remote_bindle(self, bindle_connection_info: crate::bindle_util::BindleConnectionInfo, id: &bindle::Id) -> anyhow::Result { - self.emplace_bindle(&bindle_connection_info.client()?, id).await + async fn emplace_remote_bindle( + self, + bindle_connection_info: crate::bindle_util::BindleConnectionInfo, + id: &bindle::Id, + ) -> anyhow::Result { + self.emplace_bindle(&bindle_connection_info.client()?, id) + .await } - async fn emplace_bindle(self, reader: &impl BindleReader, id: &bindle::Id) -> anyhow::Result { + async fn emplace_bindle( + self, + reader: &impl BindleReader, + id: &bindle::Id, + ) -> anyhow::Result { let invoice_path = self.invoice_path(id); if !invoice_path.is_file() { let invoice_text = reader.get_invoice_bytes(id).await?; - safely_write(&invoice_path, invoice_text).await + safely_write(&invoice_path, invoice_text) + .await .with_context(|| format!("Error writing invoice {} to cache", &id))?; } - let invoice_text = tokio::fs::read(&invoice_path).await - .with_context(|| format!("Error reading cached invoice file {}", invoice_path.display()))?; - let invoice_raw = toml::from_slice(&invoice_text) - .with_context(|| format!("Error parsing cached invoice file {}", invoice_path.display()))?; + let invoice_text = tokio::fs::read(&invoice_path).await.with_context(|| { + format!( + "Error reading cached invoice file {}", + invoice_path.display() + ) + })?; + let invoice_raw = toml::from_slice(&invoice_text).with_context(|| { + format!( + "Error parsing cached invoice file {}", + invoice_path.display() + ) + })?; let invoice = InvoiceUnderstander::new(&invoice_raw); let module_parcels = invoice.parse_wagi_handlers(); - let module_placements = module_parcels.iter().map(|h| self.emplace_module_and_assets(reader, id, h)); + let module_placements = module_parcels + .iter() + .map(|h| self.emplace_module_and_assets(reader, id, h)); let all_module_placements = futures::future::join_all(module_placements).await; match all_module_placements.into_iter().find_map(|e| e.err()) { Some(e) => Err(e), - None => Ok(EmplacedHandlerConfiguration::Bindle(self, invoice_raw)) + None => Ok(EmplacedHandlerConfiguration::Bindle(self, invoice_raw)), } } - async fn emplace_module_and_assets(&self, reader: &impl BindleReader, invoice_id: &bindle::Id, handler: &WagiHandlerInfo) -> anyhow::Result<()> { - self.emplace_module(reader, invoice_id, &handler.parcel).await?; - self.emplace_as_assets(reader, invoice_id, &handler.asset_parcels()).await?; + async fn emplace_module_and_assets( + &self, + reader: &impl BindleReader, + invoice_id: &bindle::Id, + handler: &WagiHandlerInfo, + ) -> anyhow::Result<()> { + self.emplace_module(reader, invoice_id, &handler.parcel) + .await?; + self.emplace_as_assets(reader, invoice_id, &handler.asset_parcels()) + .await?; Ok(()) } - async fn emplace_module(&self, reader: &impl BindleReader, invoice_id: &bindle::Id, parcel: &bindle::Parcel) -> anyhow::Result<()> { + async fn emplace_module( + &self, + reader: &impl BindleReader, + invoice_id: &bindle::Id, + parcel: &bindle::Parcel, + ) -> anyhow::Result<()> { let parcel_path = self.cache_path.join(&parcel.label.sha256); if parcel_path.is_file() { return Ok(()); } let parcel_data = reader.get_parcel(invoice_id, parcel).await?; - safely_write(&parcel_path, parcel_data).await - .with_context(|| format!("Error caching parcel {} at {}", parcel.label.name, parcel_path.display())) + safely_write(&parcel_path, parcel_data) + .await + .with_context(|| { + format!( + "Error caching parcel {} at {}", + parcel.label.name, + parcel_path.display() + ) + }) } - async fn emplace_as_asset(&self, reader: &impl BindleReader, invoice_id: &bindle::Id, parcel: &bindle::Parcel) -> anyhow::Result<()> { + async fn emplace_as_asset( + &self, + reader: &impl BindleReader, + invoice_id: &bindle::Id, + parcel: &bindle::Parcel, + ) -> anyhow::Result<()> { let parcel_path = self.asset_parcel_path(invoice_id, parcel); if parcel_path.is_file() { return Ok(()); } let parcel_data = reader.get_parcel(invoice_id, parcel).await?; - safely_write(&parcel_path, parcel_data).await - .with_context(|| format!("Error caching parcel {} at {}", parcel.label.name, parcel_path.display()))?; + safely_write(&parcel_path, parcel_data) + .await + .with_context(|| { + format!( + "Error caching parcel {} at {}", + parcel.label.name, + parcel_path.display() + ) + })?; Ok(()) } - async fn emplace_as_assets(&self, reader: &impl BindleReader, invoice_id: &bindle::Id, parcels: &[bindle::Parcel]) -> anyhow::Result<()> { - let placement_futures = parcels.iter().map(|parcel| self.emplace_as_asset(reader, invoice_id, parcel)); + async fn emplace_as_assets( + &self, + reader: &impl BindleReader, + invoice_id: &bindle::Id, + parcels: &[bindle::Parcel], + ) -> anyhow::Result<()> { + let placement_futures = parcels + .iter() + .map(|parcel| self.emplace_as_asset(reader, invoice_id, parcel)); let all_placements = futures::future::join_all(placement_futures).await; let first_error = all_placements.into_iter().find(|p| p.is_err()); first_error.unwrap_or(Ok(())) @@ -187,10 +278,12 @@ impl Emplacer { fn asset_dir_volume_mount(&self, invoice_id: &bindle::Id) -> HashMap { let mut volumes = HashMap::new(); - volumes.insert("/".to_owned(), self.asset_path_for(invoice_id).display().to_string()); // TODO: maybe volumes should map PathBufs // or struct of host and guest + volumes.insert( + "/".to_owned(), + self.asset_path_for(invoice_id).display().to_string(), + ); // TODO: maybe volumes should map PathBufs // or struct of host and guest volumes } - } fn invoice_cache_key(id: &bindle::Id) -> String { @@ -203,9 +296,12 @@ fn invoice_cache_key(id: &bindle::Id) -> String { async fn safely_write(path: impl AsRef, content: Vec) -> std::io::Result<()> { let path = path.as_ref(); - let dir = path.parent().ok_or_else(|| - std::io::Error::new(std::io::ErrorKind::Other, format!("cache location {} has no parent directory", path.display())) - )?; + let dir = path.parent().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("cache location {} has no parent directory", path.display()), + ) + })?; tokio::fs::create_dir_all(dir).await?; tokio::fs::write(path, content).await } @@ -215,36 +311,59 @@ trait BindleReader { // We have to flatten the error type at this point because standalone and remote // have different error types. async fn get_invoice_bytes(&self, id: &bindle::Id) -> anyhow::Result>; - async fn get_parcel(&self, id: &bindle::Id, parcel: &bindle::Parcel) -> anyhow::Result>; + async fn get_parcel(&self, id: &bindle::Id, parcel: &bindle::Parcel) + -> anyhow::Result>; } #[async_trait::async_trait] -impl BindleReader for bindle::client::Client { +impl BindleReader + for bindle::client::Client +{ async fn get_invoice_bytes(&self, id: &bindle::Id) -> anyhow::Result> { - let invoice = self.get_invoice(id).await + let invoice = self + .get_invoice(id) + .await .with_context(|| format!("Error fetching remote invoice {}", &id))?; let invoice_bytes = toml::to_vec(&invoice) .with_context(|| format!("Error reserialising remote invoice {} to cache", &id))?; Ok(invoice_bytes) } - async fn get_parcel(&self, id: &bindle::Id, parcel: &bindle::Parcel) -> anyhow::Result> { - self.get_parcel(id, &parcel.label.sha256).await + async fn get_parcel( + &self, + id: &bindle::Id, + parcel: &bindle::Parcel, + ) -> anyhow::Result> { + self.get_parcel(id, &parcel.label.sha256) + .await .with_context(|| format!("Error fetching remote parcel {}", parcel.label.name)) } } - #[async_trait::async_trait] impl BindleReader for bindle::standalone::StandaloneRead { async fn get_invoice_bytes(&self, id: &bindle::Id) -> anyhow::Result> { - let invoice_bytes = tokio::fs::read(&self.invoice_file).await - .with_context(|| format!("Error reading bindle invoice {} from {}", id, self.invoice_file.display()))?; + let invoice_bytes = tokio::fs::read(&self.invoice_file).await.with_context(|| { + format!( + "Error reading bindle invoice {} from {}", + id, + self.invoice_file.display() + ) + })?; Ok(invoice_bytes) } - async fn get_parcel(&self, _id: &bindle::Id, parcel: &bindle::Parcel) -> anyhow::Result> { + async fn get_parcel( + &self, + _id: &bindle::Id, + parcel: &bindle::Parcel, + ) -> anyhow::Result> { let path = self.parcel_dir.join(format!("{}.dat", parcel.label.sha256)); - tokio::fs::read(&path).await - .with_context(|| format!("Error reading standalone parcel {} from {}", parcel.label.name, path.display())) + tokio::fs::read(&path).await.with_context(|| { + format!( + "Error reading standalone parcel {} from {}", + parcel.label.name, + path.display() + ) + }) } } @@ -273,20 +392,35 @@ mod test { .expect("Test bindle ID should have been valid"); let asset_cache_dir = pick_test_dir(); let handlers = HandlerConfigurationSource::StandaloneBindle(test_data_dir(), test_id); - let emplacer = Emplacer::new_from_settings(&asset_cache_dir, &handlers).await + let emplacer = Emplacer::new_from_settings(&asset_cache_dir, &handlers) + .await .expect("Should have created emplacer"); - emplacer.emplace_all().await + emplacer + .emplace_all() + .await .expect("Should have emplaced files"); // Module parcels should be emplaced at top level with filename=SHA - assert!(asset_cache_dir.join("d7cc2648c55b8b1896472b1f87da9d80c26c8e9bd71602ba981123639140bf77").is_file(), - "Expected module parcel in asset directory but not found"); - assert!(asset_cache_dir.join("9ab62770d7e69fa16243e6b0d199fcfd1c733f1d710297b505c98938a36a9be4").is_file(), - "Expected module parcel in asset directory but not found"); + assert!( + asset_cache_dir + .join("d7cc2648c55b8b1896472b1f87da9d80c26c8e9bd71602ba981123639140bf77") + .is_file(), + "Expected module parcel in asset directory but not found" + ); + assert!( + asset_cache_dir + .join("9ab62770d7e69fa16243e6b0d199fcfd1c733f1d710297b505c98938a36a9be4") + .is_file(), + "Expected module parcel in asset directory but not found" + ); // There should be an asset directory with the SHA of the invoice ID - assert!(asset_cache_dir.join("_ASSETS/28e62d239a12d50b11db734eb4a37bf9e746fd487f2a375d17db3a82d6869d54").is_dir(), - "Expected invoice asset dir in asset directory but not found"); + assert!( + asset_cache_dir + .join("_ASSETS/28e62d239a12d50b11db734eb4a37bf9e746fd487f2a375d17db3a82d6869d54") + .is_dir(), + "Expected invoice asset dir in asset directory but not found" + ); // There should be assets in the asset directory assert!(asset_cache_dir.join("_ASSETS/28e62d239a12d50b11db734eb4a37bf9e746fd487f2a375d17db3a82d6869d54/images/raw-toast.jpeg").is_file(), @@ -294,7 +428,8 @@ mod test { assert!(asset_cache_dir.join("_ASSETS/28e62d239a12d50b11db734eb4a37bf9e746fd487f2a375d17db3a82d6869d54/images/derrida.png").is_file(), "Where in the world in Jacques Derrida?"); - tokio::fs::remove_dir_all(&asset_cache_dir).await + tokio::fs::remove_dir_all(&asset_cache_dir) + .await .expect("(note: test body passed, but cleanup failed"); } } diff --git a/src/handler_loader/loader.rs b/src/handler_loader/loader.rs index 01d4a8c..2641283 100644 --- a/src/handler_loader/loader.rs +++ b/src/handler_loader/loader.rs @@ -34,7 +34,7 @@ pub struct ModuleMapConfigurationEntry { // The route to wire up pub route: String, // The Wasm to wire it up to - pub module: String, // file path, file://foo URL, bindle:foo/bar/1.2.3 or oci:foo/bar:1.2.3 (bindle: is deprecated which is good because it's not clear which parcel you'd use) + pub module: String, // file path, file://foo URL, bindle:foo/bar/1.2.3 or oci:foo/bar:1.2.3 (bindle: is deprecated which is good because it's not clear which parcel you'd use) pub entrypoint: Option, pub bindle_server: Option, // The environment in which to run it @@ -51,14 +51,18 @@ pub async fn load( load_handler_configuration(emplaced_handlers, configuration).await } -pub async fn load_handler_configuration(pre_handler_config: EmplacedHandlerConfiguration, configuration: &WagiConfiguration) -> anyhow::Result { +pub async fn load_handler_configuration( + pre_handler_config: EmplacedHandlerConfiguration, + configuration: &WagiConfiguration, +) -> anyhow::Result { match pre_handler_config { EmplacedHandlerConfiguration::ModuleMapFile(path) => { let module_map_configuration = read_module_map_configuration(&path).await?; handlers_for_module_map(&module_map_configuration, configuration).await - }, - EmplacedHandlerConfiguration::Bindle(emplacer, invoice) => - handlers_for_bindle(&invoice, &emplacer).await, + } + EmplacedHandlerConfiguration::Bindle(emplacer, invoice) => { + handlers_for_bindle(&invoice, &emplacer).await + } } } @@ -77,21 +81,30 @@ async fn read_module_map_configuration(path: &Path) -> anyhow::Result anyhow::Result { +async fn handlers_for_module_map( + module_map: &ModuleMapConfiguration, + configuration: &WagiConfiguration, +) -> anyhow::Result { let loaders = module_map .entries .iter() .map(|e| handler_for_module_map_entry(e, configuration)); - let loadeds: anyhow::Result> = futures::future::join_all(loaders).await.into_iter().collect(); - - let entries = - loadeds? + let loadeds: anyhow::Result> = futures::future::join_all(loaders) + .await + .into_iter() + .collect(); + + let entries = loadeds? .into_iter() .map(LoadedHandlerConfigurationEntry::from_loaded_module_map_entry) .collect(); @@ -99,16 +112,21 @@ async fn handlers_for_module_map(module_map: &ModuleMapConfiguration, configurat Ok(LoadedHandlerConfiguration { entries }) } -async fn handlers_for_bindle(invoice: &bindle::Invoice, emplacer: &Emplacer) -> anyhow::Result { +async fn handlers_for_bindle( + invoice: &bindle::Invoice, + emplacer: &Emplacer, +) -> anyhow::Result { let invoice = InvoiceUnderstander::new(invoice); let wagi_handlers = invoice.parse_wagi_handlers(); let loaders = wagi_handlers.iter().map(|h| emplacer.get_bits_for(h)); - let loadeds: anyhow::Result> = futures::future::join_all(loaders).await.into_iter().collect(); + let loadeds: anyhow::Result> = futures::future::join_all(loaders) + .await + .into_iter() + .collect(); - let entries = - wagi_handlers + let entries = wagi_handlers .into_iter() .zip(loadeds?.into_iter()) .map(LoadedHandlerConfigurationEntry::from_loaded_bindle_handler) @@ -117,7 +135,10 @@ async fn handlers_for_bindle(invoice: &bindle::Invoice, emplacer: &Emplacer) -> Ok(LoadedHandlerConfiguration { entries }) } -async fn handler_for_module_map_entry(module_map_entry: &ModuleMapConfigurationEntry, configuration: &WagiConfiguration) -> anyhow::Result> { +async fn handler_for_module_map_entry( + module_map_entry: &ModuleMapConfigurationEntry, + configuration: &WagiConfiguration, +) -> anyhow::Result> { module_loader::load_from_module_map_entry(module_map_entry, configuration) .await .map(|v| Loaded::new(module_map_entry, v)) diff --git a/src/handler_loader/mod.rs b/src/handler_loader/mod.rs index 46129ab..b1b0f6d 100644 --- a/src/handler_loader/mod.rs +++ b/src/handler_loader/mod.rs @@ -11,11 +11,20 @@ mod module_loader; pub use compiler::WasmCompilationSettings; -pub async fn load_handlers(configuration: &WagiConfiguration) -> anyhow::Result { - let emplaced_handlers = emplacer::emplace(&configuration /* configuration.handlers, configuration.placement_settings() */).await - .with_context(|| "Failed to copy modules and assets to local cache")?; - let loaded_handlers = loader::load(emplaced_handlers, &configuration /* .loader_settings() */).await - .with_context(|| "Failed to load one or more Wasm modules from source")?; +pub async fn load_handlers( + configuration: &WagiConfiguration, +) -> anyhow::Result { + let emplaced_handlers = emplacer::emplace( + &configuration, /* configuration.handlers, configuration.placement_settings() */ + ) + .await + .with_context(|| "Failed to copy modules and assets to local cache")?; + let loaded_handlers = loader::load( + emplaced_handlers, + &configuration, /* .loader_settings() */ + ) + .await + .with_context(|| "Failed to load one or more Wasm modules from source")?; let handlers = compiler::compile(loaded_handlers, configuration.wasm_compilation_settings()) .with_context(|| "Failed to compile one or more Wasm modules")?; Ok(handlers) @@ -28,7 +37,7 @@ pub struct HandlerInfo { pub allowed_hosts: Option>, pub http_max_concurrency: Option, pub volume_mounts: HashMap, - pub argv: Option + pub argv: Option, } pub struct WasmHandlerConfiguration { diff --git a/src/handler_loader/module_loader.rs b/src/handler_loader/module_loader.rs index b44a89b..97523f7 100644 --- a/src/handler_loader/module_loader.rs +++ b/src/handler_loader/module_loader.rs @@ -2,10 +2,10 @@ use std::{path::Path, sync::Arc}; use anyhow::Context; // TODO: move OCI-specific stuff out to a helper file +use docker_credential::DockerCredential; use oci_distribution::client::{Client, ClientConfig}; use oci_distribution::secrets::RegistryAuth; use oci_distribution::Reference; -use docker_credential::DockerCredential; use sha2::{Digest, Sha256}; use url::Url; @@ -13,7 +13,10 @@ use crate::wagi_config::WagiConfiguration; use super::loader::ModuleMapConfigurationEntry; -pub async fn load_from_module_map_entry(module_map_entry: &ModuleMapConfigurationEntry, configuration: &WagiConfiguration) -> anyhow::Result> { +pub async fn load_from_module_map_entry( + module_map_entry: &ModuleMapConfigurationEntry, + configuration: &WagiConfiguration, +) -> anyhow::Result> { let module_ref = module_map_entry.module.clone(); match url::Url::parse(&module_ref) { Err(e) => { @@ -21,35 +24,50 @@ pub async fn load_from_module_map_entry(module_map_entry: &ModuleMapConfiguratio error = %e, "Error parsing module URI. Assuming this is a local file" ); - let bytes = tokio::fs::read(&module_ref).await - .with_context(|| format!("Error reading file '{}' referenced by module config", module_ref))?; + let bytes = tokio::fs::read(&module_ref).await.with_context(|| { + format!( + "Error reading file '{}' referenced by module config", + module_ref + ) + })?; Ok(bytes) - }, + } Ok(uri) => match uri.scheme() { "file" => match uri.to_file_path() { - Ok(p) => Ok(tokio::fs::read(&p).await - .with_context(|| format!("Error reading file '{}' referenced by module file: URI", p.display()))?), - Err(e) => Err(anyhow::anyhow!("Cannot get path to file {}: {:#?}", module_ref, e)), - } + Ok(p) => Ok(tokio::fs::read(&p).await.with_context(|| { + format!( + "Error reading file '{}' referenced by module file: URI", + p.display() + ) + })?), + Err(e) => Err(anyhow::anyhow!( + "Cannot get path to file {}: {:#?}", + module_ref, + e + )), + }, "bindle" => { // TODO: should we allow --bindle-server so modules.toml can resolve? This is deprecated so not keen - let bindle_server = module_map_entry.bindle_server.as_ref().ok_or_else(|| anyhow::anyhow!("No Bindle server specified for module {}", module_ref))?; + let bindle_server = module_map_entry.bindle_server.as_ref().ok_or_else(|| { + anyhow::anyhow!("No Bindle server specified for module {}", module_ref) + })?; load_bindle(bindle_server, &uri, &configuration.asset_cache_dir).await - }, + } // "parcel" => self.load_parcel(&uri, store.engine(), cache).await, // TODO: this is not mentioned in the spec...? "oci" => load_from_oci(&uri, &configuration.asset_cache_dir).await, - s => Err(anyhow::anyhow!("Unknown scheme {} in module reference {}", s, module_ref)), - } + s => Err(anyhow::anyhow!( + "Unknown scheme {} in module reference {}", + s, + module_ref + )), + }, } } const WASM_LAYER_CONTENT_TYPE: &str = "application/vnd.wasm.content.layer.v1+wasm"; #[tracing::instrument(level = "info", skip(cache))] -async fn load_from_oci( - uri: &url::Url, - cache: impl AsRef, -) -> anyhow::Result> { +async fn load_from_oci(uri: &url::Url, cache: impl AsRef) -> anyhow::Result> { let cache_file_name = hash_name(uri); let cache_file_path = cache.as_ref().join(cache_file_name); @@ -69,17 +87,20 @@ async fn load_from_oci( let mut auth = RegistryAuth::Anonymous; - if let Ok(DockerCredential::UsernamePassword(user_name, password)) = docker_credential::get_credential(uri.as_str()) { + if let Ok(DockerCredential::UsernamePassword(user_name, password)) = + docker_credential::get_credential(uri.as_str()) + { auth = RegistryAuth::Basic(user_name, password); }; - let img = url_to_oci(uri).map_err(|e| { - tracing::error!( - error = %e, - "Could not convert uri to OCI reference" - ); - e - }) + let img = url_to_oci(uri) + .map_err(|e| { + tracing::error!( + error = %e, + "Could not convert uri to OCI reference" + ); + e + }) .with_context(|| format!("Could not convert URI '{}' to OCI reference", uri))?; let data = oc .pull(&img, &auth, vec![WASM_LAYER_CONTENT_TYPE]) @@ -98,8 +119,7 @@ async fn load_from_oci( // If a cache write fails, log it but continue on. tracing::trace!("writing layer to module cache"); - if let Err(e) = safely_write(&cache_file_path, &bytes).await - { + if let Err(e) = safely_write(&cache_file_path, &bytes).await { tracing::warn!(error = %e, "failed to write module to cache"); } @@ -234,9 +254,12 @@ fn hash_name(url: &Url) -> String { // brains can reconcile that if and when the moment comes. async fn safely_write(path: impl AsRef, content: &[u8]) -> std::io::Result<()> { let path = path.as_ref(); - let dir = path.parent().ok_or_else(|| - std::io::Error::new(std::io::ErrorKind::Other, format!("cache location {} has no parent directory", path.display())) - )?; + let dir = path.parent().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("cache location {} has no parent directory", path.display()), + ) + })?; tokio::fs::create_dir_all(dir).await?; tokio::fs::write(path, content).await } diff --git a/src/handlers.rs b/src/handlers.rs index 161b58b..2e38e94 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -1,13 +1,13 @@ -use std::{collections::HashMap}; +use std::collections::HashMap; use std::sync::{Arc, RwLock}; -use wasi_cap_std_sync::Dir; use hyper::{ http::header::{HeaderName, HeaderValue}, http::request::Parts, Body, Response, StatusCode, }; -use tracing::{debug}; +use tracing::debug; +use wasi_cap_std_sync::Dir; use wasi_cap_std_sync::WasiCtxBuilder; use wasmtime::*; use wasmtime_wasi::*; @@ -17,7 +17,9 @@ use crate::http_util::{internal_error, parse_cgi_headers}; use crate::request::{RequestContext, RequestGlobalContext}; use crate::wasm_module::WasmModuleSource; -use crate::wasm_runner::{prepare_stdio_streams, prepare_wasm_instance, run_prepared_wasm_instance, WasmLinkOptions}; +use crate::wasm_runner::{ + prepare_stdio_streams, prepare_wasm_instance, run_prepared_wasm_instance, WasmLinkOptions, +}; #[derive(Clone, Debug)] pub enum RouteHandler { @@ -71,7 +73,12 @@ impl WasmRouteHandler { compose_response(redirects.stdout_mutex) } - fn build_wasi_context_for_request(&self, req: &Parts, headers: HashMap, redirects: crate::wasm_module::IOStreamRedirects) -> Result { + fn build_wasi_context_for_request( + &self, + req: &Parts, + headers: HashMap, + redirects: crate::wasm_module::IOStreamRedirects, + ) -> Result { let args = self.build_argv(req); let headers: Vec<(String, String)> = headers .iter() @@ -100,9 +107,9 @@ impl WasmRouteHandler { } /// Build the argv array that will be passed to the module. - /// + /// /// If an `argv` override is set in the handler, then this will override the CGI defaults. - /// + /// /// In the arg override: ${SCRIPT_NAME} will be replaced with the script name, and ${ARGS} /// will be replaced by the arg-formatted query parameters. E.g. 'foo=bar&baz=lurman' will /// become 'foo=bar baz=lurman' @@ -116,12 +123,12 @@ impl WasmRouteHandler { .map(|q| q.split('&').for_each(|item| args.push(item.to_string()))) .take(); args - }, + } Some(template) => { let script_name = req.uri.path(); let uri = req.uri.query().unwrap_or(""); let params = uri.replace('&', " "); - let arg_string = template + let arg_string = template .replace("${SCRIPT_NAME}", script_name) .replace("${ARGS}", params.as_str()); arg_string @@ -132,7 +139,7 @@ impl WasmRouteHandler { } } - fn prepare_wasm_instance(&self, ctx: WasiCtx) -> Result<(Store, Instance), Error> { + fn prepare_wasm_instance(&self, ctx: WasiCtx) -> Result<(Store, Instance), Error> { debug!("Preparing Wasm instance."); let link_options = WasmLinkOptions::default() .with_http(self.allowed_hosts.clone(), self.http_max_concurrency); @@ -228,4 +235,4 @@ pub fn compose_response(stdout_mutex: Arc>>) -> Result SocketAddr { - "123.4.5.6:7890".parse().expect("Failed to parse mock client address") + "123.4.5.6:7890" + .parse() + .expect("Failed to parse mock client address") } // This ugliness is because file: URLs in modules.toml are meant to be absolute, but // we don't know where the project (and therefore the test WASM modules) will reside // on any given machine. So we need to sub in the path where the WASM file will // actually be found. - async fn replace_placeholders(original_map_file: &str, custom_subs: Option>) -> PathBuf { - let orig_content = tokio::fs::read(module_map_path(original_map_file)).await + async fn replace_placeholders( + original_map_file: &str, + custom_subs: Option>, + ) -> PathBuf { + let orig_content = tokio::fs::read(module_map_path(original_map_file)) + .await .expect(&format!("Error reading test file {}", original_map_file)); - let toml_text = std::str::from_utf8(&orig_content) - .expect(&format!("Error treating test file {} as text", original_map_file)); + let toml_text = std::str::from_utf8(&orig_content).expect(&format!( + "Error treating test file {} as text", + original_map_file + )); let project_root = env!("CARGO_MANIFEST_DIR"); let timestamp = chrono::Local::now() @@ -56,16 +64,21 @@ mod test { let tempfile_dir = PathBuf::from(project_root) .join("tests_working_dir") .join(timestamp); - tokio::fs::create_dir_all(&tempfile_dir).await + tokio::fs::create_dir_all(&tempfile_dir) + .await .expect("Error creating temp directory"); let tempfile_path = tempfile_dir.join(original_map_file); - let mut final_text = toml_text.replace("${PROJECT_ROOT}", &project_root.escape_default().to_string()); + let mut final_text = toml_text.replace( + "${PROJECT_ROOT}", + &project_root.escape_default().to_string(), + ); for (k, v) in custom_subs.unwrap_or_default() { let pattern = format!("${{{}}}", k); final_text = final_text.replace(&pattern, &v.escape_default().to_string()); } - tokio::fs::write(&tempfile_path, final_text).await + tokio::fs::write(&tempfile_path, final_text) + .await .expect("Error saving modified modules file to test working dir"); tempfile_path } @@ -88,102 +101,153 @@ mod test { let matches = wagi_app::wagi_app_definition().get_matches_from(vec![ "wagi", - "-b", bindle_id, - "--bindle-path", &test_standalone_bindle_data_dir().display().to_string(), + "-b", + bindle_id, + "--bindle-path", + &test_standalone_bindle_data_dir().display().to_string(), ]); - let configuration = wagi_app::parse_configuration_from(matches) - .expect("Fake command line was not valid"); - let handlers = crate::handler_loader::load_handlers(&configuration).await + let configuration = + wagi_app::parse_configuration_from(matches).expect("Fake command line was not valid"); + let handlers = crate::handler_loader::load_handlers(&configuration) + .await .expect("Failed to load handlers"); crate::dispatcher::RoutingTable::build(&handlers, configuration.request_global_context()) .expect("Failed to build routing table") } // Accepting a Result here reduces noise in the actual tests - async fn send_request_to_standalone_bindle(bindle_id: &str, request: hyper::http::Result>) -> hyper::Response { + async fn send_request_to_standalone_bindle( + bindle_id: &str, + request: hyper::http::Result>, + ) -> hyper::Response { let routing_table = build_routing_table_for_standalone_bindle(bindle_id).await; - routing_table.handle_request( - request.expect("Failed to construct mock request"), - mock_client_addr() - ).await - .expect("Error producing HTTP response") + routing_table + .handle_request( + request.expect("Failed to construct mock request"), + mock_client_addr(), + ) + .await + .expect("Error producing HTTP response") } - async fn build_routing_table_for_module_map(map_file: &str, custom_subs: Option>) -> RoutingTable { + async fn build_routing_table_for_module_map( + map_file: &str, + custom_subs: Option>, + ) -> RoutingTable { // Clear any env vars that would cause conflicts if set std::env::remove_var("BINDLE_URL"); let modules_toml_path = replace_placeholders(&map_file, custom_subs).await; let matches = wagi_app::wagi_app_definition().get_matches_from(vec![ "wagi", - "-c", &modules_toml_path.display().to_string(), + "-c", + &modules_toml_path.display().to_string(), ]); - let configuration = wagi_app::parse_configuration_from(matches) - .expect("Fake command line was not valid"); - let handlers = crate::handler_loader::load_handlers(&configuration).await + let configuration = + wagi_app::parse_configuration_from(matches).expect("Fake command line was not valid"); + let handlers = crate::handler_loader::load_handlers(&configuration) + .await .expect("Failed to load handlers"); crate::dispatcher::RoutingTable::build(&handlers, configuration.request_global_context()) .expect("Failed to build routing table") } - async fn send_request_to_module_map(map_file: &str, custom_subs: Option>, request: hyper::http::Result>) -> hyper::Response { + async fn send_request_to_module_map( + map_file: &str, + custom_subs: Option>, + request: hyper::http::Result>, + ) -> hyper::Response { let routing_table = build_routing_table_for_module_map(map_file, custom_subs).await; - routing_table.handle_request( - request.expect("Failed to construct mock request"), - mock_client_addr() - ).await - .expect("Error producing HTTP response") + routing_table + .handle_request( + request.expect("Failed to construct mock request"), + mock_client_addr(), + ) + .await + .expect("Error producing HTTP response") } - async fn get_plain_text_response_from_module_map(map_file: &str, custom_subs: Option>, route: &str) -> String { + async fn get_plain_text_response_from_module_map( + map_file: &str, + custom_subs: Option>, + route: &str, + ) -> String { let empty_body = hyper::body::Body::empty(); let uri = format!("http://127.0.0.1:3000{}", route); let request = hyper::Request::get(&uri).body(empty_body); let response = send_request_to_module_map(map_file, custom_subs, request).await; - assert_eq!(hyper::StatusCode::OK, response.status(), "Non-OK status getting route {}", route); + assert_eq!( + hyper::StatusCode::OK, + response.status(), + "Non-OK status getting route {}", + route + ); // Content-Type could include a charset - let content_type = response.headers().get("Content-Type").expect("Expected Content-Type header").to_str().unwrap(); + let content_type = response + .headers() + .get("Content-Type") + .expect("Expected Content-Type header") + .to_str() + .unwrap(); assert!(content_type.starts_with("text/plain")); - let response_body = hyper::body::to_bytes(response.into_body()).await + let response_body = hyper::body::to_bytes(response.into_body()) + .await .expect("Could bot get bytes from response body"); - let response_text = std::str::from_utf8(&response_body) - .expect("Could not read body as string"); + let response_text = + std::str::from_utf8(&response_body).expect("Could not read body as string"); response_text.to_owned() } - async fn get_plain_text_response_from_standalone_bindle(bindle_id: &str, route: &str) -> String { + async fn get_plain_text_response_from_standalone_bindle( + bindle_id: &str, + route: &str, + ) -> String { let empty_body = hyper::body::Body::empty(); let uri = format!("http://127.0.0.1:3000{}", route); let request = hyper::Request::get(&uri).body(empty_body); let response = send_request_to_standalone_bindle(bindle_id, request).await; - assert_eq!(hyper::StatusCode::OK, response.status(), "Non-OK status getting route {}", route); + assert_eq!( + hyper::StatusCode::OK, + response.status(), + "Non-OK status getting route {}", + route + ); // Content-Type could include a charset - let content_type = response.headers().get("Content-Type").expect("Expected Content-Type header").to_str().unwrap(); + let content_type = response + .headers() + .get("Content-Type") + .expect("Expected Content-Type header") + .to_str() + .unwrap(); assert!(content_type.starts_with("text/plain")); - let response_body = hyper::body::to_bytes(response.into_body()).await + let response_body = hyper::body::to_bytes(response.into_body()) + .await .expect("Could bot get bytes from response body"); - let response_text = std::str::from_utf8(&response_body) - .expect("Could not read body as string"); + let response_text = + std::str::from_utf8(&response_body).expect("Could not read body as string"); response_text.to_owned() } - async fn get_decription_and_evs_from_standalone_bindle(bindle_id: &str, route: &str) -> (String, HashMap) { + async fn get_decription_and_evs_from_standalone_bindle( + bindle_id: &str, + route: &str, + ) -> (String, HashMap) { let response_text = get_plain_text_response_from_standalone_bindle(bindle_id, route).await; let description = response_text.lines().nth(0).unwrap().to_owned(); - + let env_vars = response_text .lines() .skip(1) @@ -193,11 +257,16 @@ mod test { (description, env_vars) } - async fn get_decription_and_evs_from_module_map(map_file: &str, custom_subs: Option>, route: &str) -> (String, HashMap) { - let response_text = get_plain_text_response_from_module_map(map_file, custom_subs, route).await; + async fn get_decription_and_evs_from_module_map( + map_file: &str, + custom_subs: Option>, + route: &str, + ) -> (String, HashMap) { + let response_text = + get_plain_text_response_from_module_map(map_file, custom_subs, route).await; let description = response_text.lines().nth(0).unwrap().to_owned(); - + let env_vars = response_text .lines() .skip(1) @@ -218,7 +287,13 @@ mod test { let response = send_request_to_standalone_bindle(TOAST_ON_DEMAND_SA_ID, request).await; assert_eq!(hyper::StatusCode::OK, response.status()); - assert_eq!("text/html", response.headers().get("Content-Type").expect("Expected Content-Type header")); + assert_eq!( + "text/html", + response + .headers() + .get("Content-Type") + .expect("Expected Content-Type header") + ); } #[tokio::test] @@ -249,7 +324,13 @@ mod test { let response = send_request_to_module_map(TEST1_MODULE_MAP_FILE, None, request).await; assert_eq!(hyper::StatusCode::OK, response.status()); - assert_eq!("text/html", response.headers().get("Content-Type").expect("Expected Content-Type header")); + assert_eq!( + "text/html", + response + .headers() + .get("Content-Type") + .expect("Expected Content-Type header") + ); } #[tokio::test] @@ -267,20 +348,27 @@ mod test { pub async fn can_serve_from_modules_toml_even_if_file_ref_has_all_sorts_of_windows_slashes() { use std::iter::FromIterator; - let wasm_module_path = module_map_path("toast-on-demand.wasm").display().to_string(); + let wasm_module_path = module_map_path("toast-on-demand.wasm") + .display() + .to_string(); for testcase in possible_slashes_for_paths(wasm_module_path) { - let subs = HashMap::from_iter(vec![ - ("SLASHY_URL".to_owned(), testcase) - ]); + let subs = HashMap::from_iter(vec![("SLASHY_URL".to_owned(), testcase)]); let empty_body = hyper::body::Body::empty(); let request = hyper::Request::get("http://127.0.0.1:3000/").body(empty_body); - let response = send_request_to_module_map(TEST2_MODULE_MAP_FILE, Some(subs), request).await; + let response = + send_request_to_module_map(TEST2_MODULE_MAP_FILE, Some(subs), request).await; assert_eq!(hyper::StatusCode::OK, response.status()); - assert_eq!("text/html", response.headers().get("Content-Type").expect("Expected Content-Type header")); + assert_eq!( + "text/html", + response + .headers() + .get("Content-Type") + .expect("Expected Content-Type header") + ); } } @@ -323,21 +411,24 @@ mod test { { let route = "/defaultep"; - let response = get_plain_text_response_from_module_map(TEST3_MODULE_MAP_FILE, None, route).await; + let response = + get_plain_text_response_from_module_map(TEST3_MODULE_MAP_FILE, None, route).await; assert_eq!("Default entrypoint\n", response); } { let route = "/ep1"; - let response = get_plain_text_response_from_module_map(TEST3_MODULE_MAP_FILE, None, route).await; + let response = + get_plain_text_response_from_module_map(TEST3_MODULE_MAP_FILE, None, route).await; assert_eq!("Entrypoint 1\n", response); } { let route = "/ep2"; - let response = get_plain_text_response_from_module_map(TEST3_MODULE_MAP_FILE, None, route).await; + let response = + get_plain_text_response_from_module_map(TEST3_MODULE_MAP_FILE, None, route).await; assert_eq!("Entrypoint 2\n", response); } } @@ -346,7 +437,8 @@ mod test { pub async fn can_serve_wat() { let route = "/"; - let response = get_plain_text_response_from_module_map(WAT_MODULE_MAP_FILE, None, route).await; + let response = + get_plain_text_response_from_module_map(WAT_MODULE_MAP_FILE, None, route).await; assert_eq!("Oh hi world\r\n", response); } @@ -360,7 +452,8 @@ mod test { #[tokio::test] pub async fn http_settings_are_mapped_to_env_vars() { - let response_text = get_plain_text_response_from_standalone_bindle(PRINT_ENV_SA_ID, "/").await; + let response_text = + get_plain_text_response_from_standalone_bindle(PRINT_ENV_SA_ID, "/").await; let parsed_response = response_text .lines() .filter_map(|line| parse_ev_line(line)) @@ -377,7 +470,9 @@ mod test { #[tokio::test] pub async fn http_settings_are_mapped_to_env_vars_wildcard_route() { - let response_text = get_plain_text_response_from_standalone_bindle(PRINT_ENV_SA_ID, "/test/fizz/buzz").await; + let response_text = + get_plain_text_response_from_standalone_bindle(PRINT_ENV_SA_ID, "/test/fizz/buzz") + .await; let parsed_response = response_text .lines() .filter_map(|line| parse_ev_line(line)) @@ -399,9 +494,10 @@ mod test { { let route = "/"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/", parsed_response["X_MATCHED_ROUTE"]); @@ -412,9 +508,10 @@ mod test { { let route = "/exactparent"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/exactparent", parsed_response["X_MATCHED_ROUTE"]); @@ -430,9 +527,10 @@ mod test { // implementations of Rust WAGI handled it differently! let route = "/exactparent/exact"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the .../exact handler", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/exactparent/exact", parsed_response["X_MATCHED_ROUTE"]); @@ -443,12 +541,16 @@ mod test { { let route = "/exactparent/wildcard/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the .../wildcard/... handler", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); - assert_eq!("/exactparent/wildcard/...", parsed_response["X_MATCHED_ROUTE"]); + assert_eq!( + "/exactparent/wildcard/...", + parsed_response["X_MATCHED_ROUTE"] + ); assert_eq!("/fizz/buzz", parsed_response["X_RAW_PATH_INFO"]); assert_eq!("/exactparent/wildcard", parsed_response["SCRIPT_NAME"]); } @@ -456,9 +558,10 @@ mod test { { let route = "/wildcardparent"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/wildcardparent/...", parsed_response["X_MATCHED_ROUTE"]); @@ -469,9 +572,10 @@ mod test { { let route = "/wildcardparent/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); assert_eq!("/wildcardparent/...", parsed_response["X_MATCHED_ROUTE"]); @@ -482,9 +586,10 @@ mod test { { let route = "/wildcardparent/exact"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the .../exact handler", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/wildcardparent/exact", parsed_response["X_MATCHED_ROUTE"]); @@ -495,12 +600,16 @@ mod test { { let route = "/wildcardparent/wildcard/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_standalone_bindle(bindle_id, route).await; assert_eq!("This is the .../wildcard/... handler", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); - assert_eq!("/wildcardparent/wildcard/...", parsed_response["X_MATCHED_ROUTE"]); + assert_eq!( + "/wildcardparent/wildcard/...", + parsed_response["X_MATCHED_ROUTE"] + ); assert_eq!("/fizz/buzz", parsed_response["X_RAW_PATH_INFO"]); assert_eq!("/wildcardparent/wildcard", parsed_response["SCRIPT_NAME"]); } @@ -513,9 +622,10 @@ mod test { { let route = "/"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/", parsed_response["X_MATCHED_ROUTE"]); @@ -526,9 +636,10 @@ mod test { { let route = "/exact"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../exact handler", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/exact", parsed_response["X_MATCHED_ROUTE"]); @@ -539,9 +650,10 @@ mod test { { let route = "/exactparent"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/exactparent", parsed_response["X_MATCHED_ROUTE"]); @@ -557,9 +669,10 @@ mod test { // implementations of Rust WAGI handled it differently! let route = "/exactparent/exact"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../exact handler", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/exactparent/exact", parsed_response["X_MATCHED_ROUTE"]); @@ -570,12 +683,16 @@ mod test { { let route = "/exactparent/wildcard/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../wildcard/... handler", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); - assert_eq!("/exactparent/wildcard/...", parsed_response["X_MATCHED_ROUTE"]); + assert_eq!( + "/exactparent/wildcard/...", + parsed_response["X_MATCHED_ROUTE"] + ); assert_eq!("/fizz/buzz", parsed_response["X_RAW_PATH_INFO"]); assert_eq!("/exactparent/wildcard", parsed_response["SCRIPT_NAME"]); } @@ -588,12 +705,16 @@ mod test { // implementations of Rust WAGI handled it differently! let route = "/exactparentslash/exact"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../exact handler", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); - assert_eq!("/exactparentslash/exact", parsed_response["X_MATCHED_ROUTE"]); + assert_eq!( + "/exactparentslash/exact", + parsed_response["X_MATCHED_ROUTE"] + ); assert_eq!("", parsed_response["X_RAW_PATH_INFO"]); assert_eq!("/exactparentslash/exact", parsed_response["SCRIPT_NAME"]); } @@ -601,12 +722,16 @@ mod test { { let route = "/exactparentslash/wildcard/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../wildcard/... handler", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); - assert_eq!("/exactparentslash/wildcard/...", parsed_response["X_MATCHED_ROUTE"]); + assert_eq!( + "/exactparentslash/wildcard/...", + parsed_response["X_MATCHED_ROUTE"] + ); assert_eq!("/fizz/buzz", parsed_response["X_RAW_PATH_INFO"]); assert_eq!("/exactparentslash/wildcard", parsed_response["SCRIPT_NAME"]); } @@ -614,9 +739,10 @@ mod test { { let route = "/wildcardparent"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/wildcardparent/...", parsed_response["X_MATCHED_ROUTE"]); @@ -627,9 +753,10 @@ mod test { { let route = "/wildcardparent/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the main entry point", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); assert_eq!("/wildcardparent/...", parsed_response["X_MATCHED_ROUTE"]); @@ -640,9 +767,10 @@ mod test { { let route = "/wildcardparent/exact"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../exact handler", description); - + assert_eq!("", parsed_response["PATH_INFO"]); assert_eq!("", parsed_response["PATH_TRANSLATED"]); assert_eq!("/wildcardparent/exact", parsed_response["X_MATCHED_ROUTE"]); @@ -653,12 +781,16 @@ mod test { { let route = "/wildcardparent/wildcard/fizz/buzz"; - let (description, parsed_response) = get_decription_and_evs_from_module_map(map_file, None, route).await; + let (description, parsed_response) = + get_decription_and_evs_from_module_map(map_file, None, route).await; assert_eq!("This is the .../wildcard/... handler", description); - + assert_eq!("/fizz/buzz", parsed_response["PATH_INFO"]); assert_eq!("/fizz/buzz", parsed_response["PATH_TRANSLATED"]); - assert_eq!("/wildcardparent/wildcard/...", parsed_response["X_MATCHED_ROUTE"]); + assert_eq!( + "/wildcardparent/wildcard/...", + parsed_response["X_MATCHED_ROUTE"] + ); assert_eq!("/fizz/buzz", parsed_response["X_RAW_PATH_INFO"]); assert_eq!("/wildcardparent/wildcard", parsed_response["SCRIPT_NAME"]); } @@ -669,13 +801,15 @@ mod test { let empty_body = hyper::body::Body::empty(); let request = hyper::Request::get("http://127.0.0.1:3000/healthz").body(empty_body); - let response = send_request_to_module_map(TEST_HEALTHZ_MODULE_MAP_FILE, None, request).await; + let response = + send_request_to_module_map(TEST_HEALTHZ_MODULE_MAP_FILE, None, request).await; assert_eq!(hyper::StatusCode::OK, response.status()); - let response_body = hyper::body::to_bytes(response.into_body()).await + let response_body = hyper::body::to_bytes(response.into_body()) + .await .expect("Could not get bytes from response body"); - let response_text = std::str::from_utf8(&response_body) - .expect("Could not read body as string"); + let response_text = + std::str::from_utf8(&response_body).expect("Could not read body as string"); assert_eq!("OK", response_text); } @@ -688,24 +822,24 @@ mod test { let empty_body = hyper::body::Body::empty(); let request = hyper::Request::get("http://127.0.0.1:3000/").body(empty_body); - let runtime = tokio::runtime::Runtime::new() - .expect("Could not create Tokio runtime for HTTP test"); + let runtime = + tokio::runtime::Runtime::new().expect("Could not create Tokio runtime for HTTP test"); let jh = runtime.spawn(async move { let response = send_request_to_standalone_bindle(HTTP_TEST_ID, request).await; let status = response.status(); - let response_body = hyper::body::to_bytes(response.into_body()).await + let response_body = hyper::body::to_bytes(response.into_body()) + .await .expect("Could not get bytes from response body"); - let response_text = std::str::from_utf8(&response_body) - .expect("Could not read body as string"); + let response_text = + std::str::from_utf8(&response_body).expect("Could not read body as string"); (status, response_text.to_owned()) }); let (status, response_text) = futures::executor::block_on(jh).unwrap(); assert_eq!(hyper::StatusCode::OK, status); - assert!(response_text.contains("is HEALTHY") || - response_text.contains("is UNHEALTHY")); + assert!(response_text.contains("is HEALTHY") || response_text.contains("is UNHEALTHY")); } } diff --git a/src/main.rs b/src/main.rs index 78657cc..b8bfbcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,12 +10,16 @@ pub async fn main() -> Result<(), anyhow::Error> { let handlers = wagi::handler_loader::load_handlers(&configuration).await?; // Possibly this should go into a 'routing table builder' so we cleanly separate // prep-time and serve-time responsibilities. - let routing_table = wagi::dispatcher::RoutingTable::build(&handlers, configuration.request_global_context())?; + let routing_table = + wagi::dispatcher::RoutingTable::build(&handlers, configuration.request_global_context())?; let server = WagiServer::new(&configuration, routing_table).await?; drop(startup_span); - println!("Ready: serving on http://{}", configuration.http_configuration.listen_on); + println!( + "Ready: serving on http://{}", + configuration.http_configuration.listen_on + ); server.serve().await } diff --git a/src/wagi_app.rs b/src/wagi_app.rs index b3b5a7e..1017d68 100644 --- a/src/wagi_app.rs +++ b/src/wagi_app.rs @@ -1,13 +1,13 @@ -use clap::{App, Arg, ArgMatches, ArgGroup}; -use core::convert::TryFrom; -use std::collections::HashMap; -use std::net::SocketAddr; use crate::{ bindle_util::BindleConnectionInfo, wagi_config::{ HandlerConfigurationSource, HttpConfiguration, TlsConfiguration, WagiConfiguration, }, }; +use clap::{App, Arg, ArgGroup, ArgMatches}; +use core::convert::TryFrom; +use std::collections::HashMap; +use std::net::SocketAddr; const ABOUT: &str = r#" Run an HTTP WAGI server @@ -285,8 +285,12 @@ fn parse_bindle_connection_info( Ok(BindleConnectionInfo::new( url, matches.is_present(ARG_BINDLE_INSECURE), - matches.value_of(ARG_BINDLE_HTTP_USER).map(|s| s.to_string()), - matches.value_of(ARG_BINDLE_HTTP_PASSWORD).map(|s| s.to_string()), + matches + .value_of(ARG_BINDLE_HTTP_USER) + .map(|s| s.to_string()), + matches + .value_of(ARG_BINDLE_HTTP_PASSWORD) + .map(|s| s.to_string()), )) } @@ -300,7 +304,9 @@ fn parse_handler_configuration_source( // have a Bindle server URL OR standalone directory, but not both match ( matches.value_of(ARG_BINDLE_ID).ignore_if_empty(), - matches.value_of(ARG_BINDLE_STANDALONE_DIR).ignore_if_empty(), + matches + .value_of(ARG_BINDLE_STANDALONE_DIR) + .ignore_if_empty(), matches.value_of(ARG_BINDLE_URL).ignore_if_empty(), matches.value_of(ARG_MODULES_CONFIG).ignore_if_empty(), ) { @@ -334,15 +340,13 @@ fn parse_handler_configuration_source( } } // Case: got a bindle id and server URL. Can't have a bindir dir or module file. - (Some(bindle_id), None, Some(bindle_url), None) => { - match url::Url::parse(bindle_url) { - Ok(url) => Ok(HandlerConfigurationSource::RemoteBindle( - parse_bindle_connection_info(url, &matches)?, - bindle::Id::try_from(bindle_id)?, - )), - Err(e) => Err(anyhow::anyhow!("Invalid Bindle server URL: {}", e)), - } - } + (Some(bindle_id), None, Some(bindle_url), None) => match url::Url::parse(bindle_url) { + Ok(url) => Ok(HandlerConfigurationSource::RemoteBindle( + parse_bindle_connection_info(url, &matches)?, + bindle::Id::try_from(bindle_id)?, + )), + Err(e) => Err(anyhow::anyhow!("Invalid Bindle server URL: {}", e)), + }, // These cases shouldn't be able to happen. We could be optimistic and // confident, and assume that means they won't. But we have been // programming faaaaaaaaaar too long for that. diff --git a/src/wagi_config.rs b/src/wagi_config.rs index 22c4722..b1f693f 100644 --- a/src/wagi_config.rs +++ b/src/wagi_config.rs @@ -1,8 +1,7 @@ use std::{collections::HashMap, net::SocketAddr, path::PathBuf}; use crate::{ - bindle_util::BindleConnectionInfo, - handler_loader::WasmCompilationSettings, + bindle_util::BindleConnectionInfo, handler_loader::WasmCompilationSettings, request::RequestGlobalContext, }; diff --git a/src/wagi_server.rs b/src/wagi_server.rs index 9b8be0e..0a69e92 100644 --- a/src/wagi_server.rs +++ b/src/wagi_server.rs @@ -1,8 +1,8 @@ use std::net::SocketAddr; use crate::dispatcher::RoutingTable; -use crate::{tls, wagi_config::TlsConfiguration}; use crate::wagi_config::WagiConfiguration; +use crate::{tls, wagi_config::TlsConfiguration}; use hyper::{ server::conn::AddrStream, @@ -19,7 +19,10 @@ pub struct WagiServer { } impl WagiServer { - pub async fn new(configuration: &WagiConfiguration, routing_table: RoutingTable) -> anyhow::Result { + pub async fn new( + configuration: &WagiConfiguration, + routing_table: RoutingTable, + ) -> anyhow::Result { Ok(Self { routing_table, tls: configuration.http_configuration.tls.clone(), @@ -65,10 +68,13 @@ impl WagiServer { })) }) }); - Server::builder(tls::TlsHyperAcceptor::new(&self.address, &tls.cert_path, &tls.key_path).await?) - .serve(mk_svc) - .await?; - }, + Server::builder( + tls::TlsHyperAcceptor::new(&self.address, &tls.cert_path, &tls.key_path) + .await?, + ) + .serve(mk_svc) + .await?; + } None => { let mk_svc = make_service_fn(move |conn: &AddrStream| { let addr = conn.remote_addr(); @@ -81,9 +87,9 @@ impl WagiServer { } }); Server::bind(&self.address).serve(mk_svc).await?; - }, + } } - + Ok(()) } } diff --git a/src/wasm_module.rs b/src/wasm_module.rs index 11864e6..707f212 100644 --- a/src/wasm_module.rs +++ b/src/wasm_module.rs @@ -1,4 +1,8 @@ -use std::{fmt::Debug, sync::{Arc, RwLock}, path::Path}; +use std::{ + fmt::Debug, + path::Path, + sync::{Arc, RwLock}, +}; use wasi_common::pipe::{ReadPipe, WritePipe}; use wasmtime::*; diff --git a/src/wasm_runner.rs b/src/wasm_runner.rs index e2de16d..7657d77 100644 --- a/src/wasm_runner.rs +++ b/src/wasm_runner.rs @@ -25,8 +25,8 @@ impl WasmLinkOptions { pub fn with_http( self, allowed_hosts: Option>, - max_concurrency: Option) - -> Self { + max_concurrency: Option, + ) -> Self { let mut result = self.clone(); result.http_allowed_hosts = allowed_hosts; result.http_max_concurrency = max_concurrency; @@ -68,7 +68,6 @@ pub fn prepare_stdio_streams( ); let stderr = wasi_cap_std_sync::file::File::from_cap_std(stderr); - Ok(crate::wasm_module::IORedirectionInfo { streams: crate::wasm_module::IOStreamRedirects { stdin, @@ -79,11 +78,7 @@ pub fn prepare_stdio_streams( }) } - -pub fn new_store( - ctx: WasiCtx, - engine: &Engine -) -> Result, anyhow::Error> { +pub fn new_store(ctx: WasiCtx, engine: &Engine) -> Result, anyhow::Error> { Ok(Store::new(engine, ctx)) }