[pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api

Thomas Lamprecht t.lamprecht at proxmox.com
Wed Sep 29 08:41:26 CEST 2021


On 28.09.21 11:11, Dominik Csapak wrote:
> show how to generally start a daemon that serves a rest api + index page
> 
> api calls are:

well they are prefixed with `/api2` that's really something that we need to
make configurable, for any future project I definitively do not want to use
the rather confusing /api2 from the start, that can be rather easily adapted
in the client's central HTTP request handling code.

> /		GET	listing
> /ping		GET	returns "pong"
> /items		GET	lists existing items
> 		POST	lets user create new items
> /items/{id}	GET	returns the content of a single item
> 		PUT	updates an item
> 		DELETE	deletes an item
> 
> Contains a small dummy user/authinfo
> 
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
> changes from v1:
> * use hyper::server::conn::AddrIncoming::from_listener
> 
>  proxmox-rest-server/Cargo.toml              |   5 +
>  proxmox-rest-server/examples/rest_server.rs | 219 ++++++++++++++++++++
>  2 files changed, 224 insertions(+)
>  create mode 100644 proxmox-rest-server/examples/rest_server.rs
> 
> diff --git a/proxmox-rest-server/Cargo.toml b/proxmox-rest-server/Cargo.toml
> index b0e53d19..a6d25b8b 100644
> --- a/proxmox-rest-server/Cargo.toml
> +++ b/proxmox-rest-server/Cargo.toml
> @@ -5,6 +5,11 @@ authors = ["Proxmox Support Team <support at proxmox.com>"]
>  edition = "2018"
>  description = "REST server implementation"
>  
> +# for example
> +[dev-dependencies]
> +proxmox = { version = "0.13.4", features = ["router","api-macro"] }

0.13.4 is already outdated, do we even need tho have that version target so strict as
else I'd go with "0.13" for now..

> +pbs-runtime = { path = "../pbs-runtime" }
> +
>  [dependencies]
>  anyhow = "1.0"
>  futures = "0.3"
> diff --git a/proxmox-rest-server/examples/rest_server.rs b/proxmox-rest-server/examples/rest_server.rs
> new file mode 100644
> index 00000000..89efbcb2
> --- /dev/null
> +++ b/proxmox-rest-server/examples/rest_server.rs
> @@ -0,0 +1,219 @@
> +use std::sync::{Arc, Mutex};
> +use std::collections::HashMap;
> +
> +use anyhow::{bail, format_err, Error};
> +use futures::{FutureExt, TryFutureExt};
> +use lazy_static::lazy_static;
> +
> +use proxmox::api::{api, router::SubdirMap, Router, RpcEnvironmentType, UserInformation};
> +use proxmox::list_subdirs_api_method;
> +use proxmox_rest_server::{ApiAuth, ApiConfig, AuthError, RestServer};
> +
> +// Create a Dummy User info and auth system
> +// Normally this would check and authenticate the user
> +struct DummyUserInfo;
> +
> +impl UserInformation for DummyUserInfo {
> +    fn is_superuser(&self, _userid: &str) -> bool {
> +        true
> +    }
> +    fn is_group_member(&self, _userid: &str, group: &str) -> bool {
> +        group == "Group"
> +    }
> +    fn lookup_privs(&self, _userid: &str, _path: &[&str]) -> u64 {
> +        u64::MAX
> +    }
> +}
> +
> +struct DummyAuth;
> +
> +impl ApiAuth for DummyAuth {
> +    fn check_auth(
> +        &self,
> +        _headers: &http::HeaderMap,
> +        _method: &hyper::Method,
> +    ) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError> {
> +        // get some global/cached userinfo
> +        let userinfo = DummyUserInfo;
> +        // Do some user checks, e.g. cookie/csrf
> +        Ok(("User".to_string(), Box::new(userinfo)))
> +    }
> +}
> +
> +// this should return the index page of the webserver
> +// iow. what the user browses to

above fits in one line even with 80 cc, and in rust we normally go always for 100cc.


> +fn get_index(
> +    _auth_id: Option<String>,
> +    _language: Option<String>,
> +    _api: &ApiConfig,
> +    _parts: http::request::Parts,
> +) -> http::Response<hyper::Body> {
> +    // build an index page
> +    http::Response::builder()
> +        .body("hello world".into())

maybe a mini html thingy with the API description from above + links to the GET calls would
be nice to have?


> +async fn run() -> Result<(), Error> {
> +
> +    // we first have to configure the api environment (basedir etc.)
> +
> +    let config = ApiConfig::new(
> +        "/var/tmp/",
> +        &ROUTER,
> +        RpcEnvironmentType::PUBLIC,
> +        Arc::new(DummyAuth {}),
> +        get_index,
> +    )?;
> +    let rest_server = RestServer::new(config);
> +
> +    // then we have to create a daemon that listens, accepts and serves
> +    // the api to clients
> +    proxmox_rest_server::daemon::create_daemon(
> +        ([127, 0, 0, 1], 65000).into(),
> +        move |listener, ready| {
> +            let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?;
> +
> +            Ok(ready
> +                .and_then(|_| hyper::Server::builder(incoming)
> +                    .serve(rest_server)
> +                    .map_err(Error::from)
> +                )
> +                .map_err(|err| eprintln!("ERR: {}", err))
> +                .map(|test| println!("OK: {}", test.is_ok())))

closing parenthesis jungle ^^

Can you at least place the closing one from Ok() one a new line or does rustfmt does not like that?

> +        },
> +        "example_server",
> +    ).await?;
> +
> +    Ok(())
> +}
> +
> +fn main() -> Result<(), Error> {
> +    pbs_runtime::main(run())

here I agree with Dietmar, avoiding that dependency would be nice for the example.

> +}
> 






More information about the pbs-devel mailing list