From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox 3/4] proxmox-router: add new ApiHandler variants for streaming serialization
Date: Fri, 8 Apr 2022 11:56:02 +0200 [thread overview]
Message-ID: <20220408095606.2767234-4-d.csapak@proxmox.com> (raw)
In-Reply-To: <20220408095606.2767234-1-d.csapak@proxmox.com>
they should behave like their normal variants, but return a
`Box<dyn SerializableReturn + Send>` instead of a value. This is useful
since we do not have to generate the `Value` in-memory, but can
stream the serialization to the client.
We cannot simply use a `Box<dyn serde::Serialize>`, because that trait
is not object-safe and thus cannot be used as a trait-object.
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
proxmox-router/src/cli/command.rs | 45 ++++++++++++++++++
proxmox-router/src/router.rs | 78 +++++++++++++++++++++++++++++++
2 files changed, 123 insertions(+)
diff --git a/proxmox-router/src/cli/command.rs b/proxmox-router/src/cli/command.rs
index 1b05078..76ecddc 100644
--- a/proxmox-router/src/cli/command.rs
+++ b/proxmox-router/src/cli/command.rs
@@ -72,6 +72,18 @@ async fn handle_simple_command_future(
return Err(err);
}
},
+ ApiHandler::StreamingSync(handler) => match (handler)(params, cli_cmd.info, &mut rpcenv) {
+ Ok(value) => {
+ let value = value.to_value()?;
+ if value != Value::Null {
+ println!("Result: {}", serde_json::to_string_pretty(&value).unwrap());
+ }
+ }
+ Err(err) => {
+ eprintln!("Error: {}", err);
+ return Err(err);
+ }
+ },
ApiHandler::Async(handler) => {
let future = (handler)(params, cli_cmd.info, &mut rpcenv);
@@ -87,6 +99,22 @@ async fn handle_simple_command_future(
}
}
}
+ ApiHandler::StreamingAsync(handler) => {
+ let future = (handler)(params, cli_cmd.info, &mut rpcenv);
+
+ match future.await {
+ Ok(value) => {
+ let value = value.to_value()?;
+ if value != Value::Null {
+ println!("Result: {}", serde_json::to_string_pretty(&value).unwrap());
+ }
+ }
+ Err(err) => {
+ eprintln!("Error: {}", err);
+ return Err(err);
+ }
+ }
+ }
ApiHandler::AsyncHttp(_) => {
let err_msg = "CliHandler does not support ApiHandler::AsyncHttp - internal error";
print_simple_usage_error(prefix, cli_cmd, err_msg);
@@ -118,6 +146,18 @@ fn handle_simple_command(
return Err(err);
}
},
+ ApiHandler::StreamingSync(handler) => match (handler)(params, cli_cmd.info, &mut rpcenv) {
+ Ok(value) => {
+ let value = value.to_value()?;
+ if value != Value::Null {
+ println!("Result: {}", serde_json::to_string_pretty(&value).unwrap());
+ }
+ }
+ Err(err) => {
+ eprintln!("Error: {}", err);
+ return Err(err);
+ }
+ },
ApiHandler::Async(handler) => {
let future = (handler)(params, cli_cmd.info, &mut rpcenv);
if let Some(run) = run {
@@ -138,6 +178,11 @@ fn handle_simple_command(
return Err(format_err!("{}", err_msg));
}
}
+ ApiHandler::StreamingAsync(_handler) => {
+ let err_msg = "CliHandler does not support ApiHandler::StreamingAsync - internal error";
+ print_simple_usage_error(prefix, cli_cmd, err_msg);
+ return Err(format_err!("{}", err_msg));
+ }
ApiHandler::AsyncHttp(_) => {
let err_msg = "CliHandler does not support ApiHandler::AsyncHttp - internal error";
print_simple_usage_error(prefix, cli_cmd, err_msg);
diff --git a/proxmox-router/src/router.rs b/proxmox-router/src/router.rs
index a469891..3fa0033 100644
--- a/proxmox-router/src/router.rs
+++ b/proxmox-router/src/router.rs
@@ -14,6 +14,7 @@ use proxmox_schema::{ObjectSchema, ParameterSchema, ReturnType, Schema};
use super::Permission;
use crate::RpcEnvironment;
+use crate::SerializableReturn;
/// A synchronous API handler gets a json Value as input and returns a json Value as output.
///
@@ -42,6 +43,37 @@ pub type ApiHandlerFn = &'static (dyn Fn(Value, &ApiMethod, &mut dyn RpcEnvironm
+ Sync
+ 'static);
+/// A synchronous API handler gets a json Value as input and returns a serializable return value as output.
+///
+/// ```
+/// # use anyhow::Error;
+/// # use serde_json::{json, Value};
+/// use proxmox_router::{ApiHandler, ApiMethod, RpcEnvironment, SerializableReturn};
+/// use proxmox_schema::ObjectSchema;
+///
+/// fn hello(
+/// param: Value,
+/// info: &ApiMethod,
+/// rpcenv: &mut dyn RpcEnvironment,
+/// ) -> Result<Box<dyn SerializableReturn + Send>, Error> {
+/// let res: Box<dyn SerializableReturn + Send> = Box::new(format!("Hello World!"));
+/// Ok(res)
+/// }
+///
+/// const API_METHOD_HELLO: ApiMethod = ApiMethod::new(
+/// &ApiHandler::StreamingSync(&hello),
+/// &ObjectSchema::new("Hello World Example", &[])
+/// );
+/// ```
+pub type StreamingApiHandlerFn = &'static (dyn Fn(
+ Value,
+ &ApiMethod,
+ &mut dyn RpcEnvironment,
+) -> Result<Box<dyn SerializableReturn + Send>, Error>
+ + Send
+ + Sync
+ + 'static);
+
/// Asynchronous API handlers
///
/// Returns a future Value.
@@ -74,6 +106,44 @@ pub type ApiAsyncHandlerFn = &'static (dyn for<'a> Fn(Value, &'static ApiMethod,
pub type ApiFuture<'a> = Pin<Box<dyn Future<Output = Result<Value, anyhow::Error>> + Send + 'a>>;
+/// Streaming asynchronous API handlers
+///
+/// Returns a future Value.
+/// ```
+/// # use serde_json::{json, Value};
+/// #
+/// use proxmox_router::{ApiFuture, ApiHandler, ApiMethod, RpcEnvironment, StreamingApiFuture, SerializableReturn};
+/// use proxmox_schema::ObjectSchema;
+///
+///
+/// fn hello_future<'a>(
+/// param: Value,
+/// info: &ApiMethod,
+/// rpcenv: &'a mut dyn RpcEnvironment,
+/// ) -> StreamingApiFuture<'a> {
+/// Box::pin(async move {
+/// let res: Box<dyn SerializableReturn + Send> = Box::new(format!("Hello World!"));
+/// Ok(res)
+/// })
+/// }
+///
+/// const API_METHOD_HELLO_FUTURE: ApiMethod = ApiMethod::new(
+/// &ApiHandler::StreamingAsync(&hello_future),
+/// &ObjectSchema::new("Hello World Example (async)", &[])
+/// );
+/// ```
+pub type StreamingApiAsyncHandlerFn = &'static (dyn for<'a> Fn(
+ Value,
+ &'static ApiMethod,
+ &'a mut dyn RpcEnvironment,
+) -> StreamingApiFuture<'a>
+ + Send
+ + Sync);
+
+pub type StreamingApiFuture<'a> = Pin<
+ Box<dyn Future<Output = Result<Box<dyn SerializableReturn + Send>, anyhow::Error>> + Send + 'a>,
+>;
+
/// Asynchronous HTTP API handlers
///
/// They get low level access to request and response data. Use this
@@ -124,7 +194,9 @@ pub type ApiResponseFuture =
/// Enum for different types of API handler functions.
pub enum ApiHandler {
Sync(ApiHandlerFn),
+ StreamingSync(StreamingApiHandlerFn),
Async(ApiAsyncHandlerFn),
+ StreamingAsync(StreamingApiAsyncHandlerFn),
AsyncHttp(ApiAsyncHttpHandlerFn),
}
@@ -139,9 +211,15 @@ impl PartialEq for ApiHandler {
(ApiHandler::Sync(l), ApiHandler::Sync(r)) => {
core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r)
}
+ (ApiHandler::StreamingSync(l), ApiHandler::StreamingSync(r)) => {
+ core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r)
+ }
(ApiHandler::Async(l), ApiHandler::Async(r)) => {
core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r)
}
+ (ApiHandler::StreamingAsync(l), ApiHandler::StreamingAsync(r)) => {
+ core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r)
+ }
(ApiHandler::AsyncHttp(l), ApiHandler::AsyncHttp(r)) => {
core::mem::transmute::<_, usize>(l) == core::mem::transmute::<_, usize>(r)
}
--
2.30.2
next prev parent reply other threads:[~2022-04-08 9:56 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-04-08 9:55 [pbs-devel] [PATCH proxmox/proxmox-backup] implement streaming serialization for api calls Dominik Csapak
2022-04-08 9:56 ` [pbs-devel] [PATCH proxmox 1/4] proxmox-async: add SenderWriter helper Dominik Csapak
2022-04-12 12:29 ` [pbs-devel] applied-series: " Wolfgang Bumiller
2022-04-08 9:56 ` [pbs-devel] [PATCH proxmox 2/4] promxox-router: add SerializableReturn Trait Dominik Csapak
2022-04-08 9:56 ` Dominik Csapak [this message]
2022-04-08 9:56 ` [pbs-devel] [PATCH proxmox 4/4] proxmox-api-macro: add 'streaming' option Dominik Csapak
2022-04-08 9:56 ` [pbs-devel] [PATCH proxmox-backup 1/3] proxmox-rest-server: OutputFormatter: add new format_data_streaming method Dominik Csapak
2022-04-08 9:56 ` [pbs-devel] [PATCH proxmox-backup 2/3] adapt to the new ApiHandler variants Dominik Csapak
2022-04-08 9:56 ` [pbs-devel] [PATCH proxmox-backup 3/3] api: admin/datastore: enable streaming for some api calls Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220408095606.2767234-4-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal