diff --git a/proxmox-rest-server/src/rest.rs b/proxmox-rest-server/src/rest.rs index 78339b75..350ce957 100644 --- a/proxmox-rest-server/src/rest.rs +++ b/proxmox-rest-server/src/rest.rs @@ -546,6 +546,11 @@ pub(crate) async fn handle_api_request { + let params = + get_request_parameters(info.parameters, &parts, req_body, uri_param).await?; + (handler)(parts, params, info, Box::new(rpcenv)).await + } ApiHandler::StreamSync(handler) => { let params = get_request_parameters(info.parameters, &parts, req_body, uri_param).await?; diff --git a/proxmox-router/src/cli/command.rs b/proxmox-router/src/cli/command.rs index 01f64d19..2ca2356a 100644 --- a/proxmox-router/src/cli/command.rs +++ b/proxmox-router/src/cli/command.rs @@ -107,6 +107,12 @@ async fn handle_simple_command_future( ApiHandler::AsyncHttp(_) => { bail!("CliHandler does not support ApiHandler::AsyncHttp - internal error") } + #[cfg(feature = "server")] + ApiHandler::AsyncHttpBodyParameters(_) => { + bail!( + "CliHandler does not support ApiHandler::AsyncHttpBodyParameters - internal error" + ) + } }; match result { @@ -159,6 +165,12 @@ pub(crate) fn handle_simple_command<'cli>( ApiHandler::AsyncHttp(_) => { bail!("CliHandler does not support ApiHandler::AsyncHttp - internal error"); } + #[cfg(feature = "server")] + ApiHandler::AsyncHttpBodyParameters(_) => { + bail!( + "CliHandler does not support ApiHandler::AsyncHttpBodyParameters - internal error" + ); + } }; match result { diff --git a/proxmox-router/src/format.rs b/proxmox-router/src/format.rs index 67568af0..be40895a 100644 --- a/proxmox-router/src/format.rs +++ b/proxmox-router/src/format.rs @@ -32,6 +32,12 @@ fn dump_method_definition(method: &str, path: &str, def: Option<&ApiMethod>) -> method = if method == "GET" { "DOWNLOAD" } else { method }; } + #[cfg(feature = "server")] + if let ApiHandler::AsyncHttpBodyParameters(_) = api_method.handler { + method = if method == "POST" { "UPLOAD" } else { method }; + method = if method == "GET" { "DOWNLOAD" } else { method }; + } + let res = format!( "**{} {}**\n\n{}{}\n\n{}", method, path, description, param_descr, return_descr diff --git a/proxmox-router/src/router.rs b/proxmox-router/src/router.rs index 33b598da..0c4e78d4 100644 --- a/proxmox-router/src/router.rs +++ b/proxmox-router/src/router.rs @@ -435,6 +435,44 @@ pub type ApiAsyncHttpHandlerFn = &'static (dyn Fn( pub type ApiResponseFuture = Pin, anyhow::Error>> + Send>>; +/// Asynchronous HTTP API handlers with parameters specified in their bodies +/// +/// They get low level access to request and response data, but it is also possible to specify +/// their parameters in the request body. +/// +/// ``` +/// use serde_json::Value; +/// +/// use hyper::{Body, Response, http::request::Parts}; +/// +/// use proxmox_router::{ApiHandler, ApiMethod, ApiResponseFuture, RpcEnvironment}; +/// use proxmox_schema::ObjectSchema; +/// +/// fn low_level_hello( +/// parts: Parts, +/// param: Value, +/// info: &ApiMethod, +/// rpcenv: Box, +/// ) -> ApiResponseFuture { +/// Box::pin(async move { +/// let response = http::Response::builder() +/// .status(200) +/// .body(Body::from("Hello world!"))?; +/// Ok(response) +/// }) +/// } +/// +/// const API_METHOD_LOW_LEVEL_HELLO_BODY_PARAMETER: ApiMethod = ApiMethod::new( +/// &ApiHandler::AsyncHttpBodyParameters(&low_level_hello), +/// &ObjectSchema::new("Hello World Example (low level)", &[]) +/// ); +/// ``` +#[cfg(feature = "server")] +pub type ApiAsyncHttpHandlerBodyParametersFn = &'static (dyn Fn(Parts, Value, &'static ApiMethod, Box) -> ApiResponseFuture + + Send + + Sync + + 'static); + /// Enum for different types of API handler functions. #[non_exhaustive] pub enum ApiHandler { @@ -446,6 +484,8 @@ pub enum ApiHandler { StreamAsync(StreamApiAsyncHandlerFn), #[cfg(feature = "server")] AsyncHttp(ApiAsyncHttpHandlerFn), + #[cfg(feature = "server")] + AsyncHttpBodyParameters(ApiAsyncHttpHandlerBodyParametersFn), } #[cfg(feature = "test-harness")] @@ -478,6 +518,11 @@ impl PartialEq for ApiHandler { (ApiHandler::AsyncHttp(l), ApiHandler::AsyncHttp(r)) => { core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r) } + #[cfg(feature = "server")] + ( + ApiHandler::AsyncHttpBodyParameters(l), + ApiHandler::AsyncHttpBodyParameters(r), + ) => core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r), _ => false, } }