public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example
@ 2021-09-28  9:11 Dominik Csapak
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api Dominik Csapak
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Dominik Csapak @ 2021-09-28  9:11 UTC (permalink / raw)
  To: pbs-devel

adds a rest_server example to the proxmox-rest-server crate.

changes from v1:
* added patches so we can remove the (now) unused srt/tools/async_io.rs.
  this enables us to don't have any dependencies on the top-level crate
  for the example, and we can move it into proxmox-rest-server
* use hypers AddrStream for the example

Dominik Csapak (3):
  rest-server: use hypers AddrStream for proxmox-backup-api
  remove tools/async_io.rs
  examples: add example for a simple rest server with a small api

 proxmox-rest-server/Cargo.toml              |   5 +
 proxmox-rest-server/examples/rest_server.rs | 219 ++++++++++++++++++++
 proxmox-rest-server/src/rest.rs             |  18 +-
 src/bin/proxmox-backup-api.rs               |   3 +-
 src/tools/async_io.rs                       |  83 --------
 src/tools/mod.rs                            |   1 -
 6 files changed, 234 insertions(+), 95 deletions(-)
 create mode 100644 proxmox-rest-server/examples/rest_server.rs
 delete mode 100644 src/tools/async_io.rs

-- 
2.30.2





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api
  2021-09-28  9:11 [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example Dominik Csapak
@ 2021-09-28  9:11 ` Dominik Csapak
  2021-09-29  6:27   ` Thomas Lamprecht
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 2/3] remove tools/async_io.rs Dominik Csapak
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak
  2 siblings, 1 reply; 6+ messages in thread
From: Dominik Csapak @ 2021-09-28  9:11 UTC (permalink / raw)
  To: pbs-devel

so that the StaticIncoming wrapper is not necessary anymore. The only
thing we have to do is to change the Service impl from tokio::net::TcpStream
to hyper::server::conn::AddStream to fulfill the trait requirements.

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
new in v2
 proxmox-rest-server/src/rest.rs | 18 ++++++++----------
 src/bin/proxmox-backup-api.rs   |  3 ++-
 2 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/proxmox-rest-server/src/rest.rs b/proxmox-rest-server/src/rest.rs
index ba2edf11..8c1e34a3 100644
--- a/proxmox-rest-server/src/rest.rs
+++ b/proxmox-rest-server/src/rest.rs
@@ -93,7 +93,7 @@ impl tower_service::Service<&Pin<Box<tokio_openssl::SslStream<tokio::net::TcpStr
     }
 }
 
-impl tower_service::Service<&tokio::net::TcpStream> for RestServer {
+impl tower_service::Service<&hyper::server::conn::AddrStream> for RestServer {
     type Response = ApiService;
     type Error = Error;
     type Future = Pin<Box<dyn Future<Output = Result<ApiService, Error>> + Send>>;
@@ -102,15 +102,13 @@ impl tower_service::Service<&tokio::net::TcpStream> for RestServer {
         Poll::Ready(Ok(()))
     }
 
-    fn call(&mut self, ctx: &tokio::net::TcpStream) -> Self::Future {
-        match ctx.peer_addr() {
-            Err(err) => future::err(format_err!("unable to get peer address - {}", err)).boxed(),
-            Ok(peer) => future::ok(ApiService {
-                peer,
-                api_config: self.api_config.clone(),
-            })
-            .boxed(),
-        }
+    fn call(&mut self, ctx: &hyper::server::conn::AddrStream) -> Self::Future {
+        let peer =  ctx.remote_addr();
+        future::ok(ApiService {
+            peer,
+            api_config: self.api_config.clone(),
+        })
+        .boxed()
     }
 }
 
diff --git a/src/bin/proxmox-backup-api.rs b/src/bin/proxmox-backup-api.rs
index 86650de6..e3f2531f 100644
--- a/src/bin/proxmox-backup-api.rs
+++ b/src/bin/proxmox-backup-api.rs
@@ -108,7 +108,8 @@ async fn run() -> Result<(), Error> {
     let server = daemon::create_daemon(
         ([127,0,0,1], 82).into(),
         move |listener, ready| {
-            let incoming = proxmox_backup::tools::async_io::StaticIncoming::from(listener);
+            let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?;
+
             Ok(ready
                 .and_then(|_| hyper::Server::builder(incoming)
                     .serve(rest_server)
-- 
2.30.2





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 2/3] remove tools/async_io.rs
  2021-09-28  9:11 [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example Dominik Csapak
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api Dominik Csapak
@ 2021-09-28  9:11 ` Dominik Csapak
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak
  2 siblings, 0 replies; 6+ messages in thread
From: Dominik Csapak @ 2021-09-28  9:11 UTC (permalink / raw)
  To: pbs-devel

nothing from here is used anymore, so remove it

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
new in v2
 src/tools/async_io.rs | 83 -------------------------------------------
 src/tools/mod.rs      |  1 -
 2 files changed, 84 deletions(-)
 delete mode 100644 src/tools/async_io.rs

diff --git a/src/tools/async_io.rs b/src/tools/async_io.rs
deleted file mode 100644
index 66d38094..00000000
--- a/src/tools/async_io.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//! AsyncRead/AsyncWrite utilities.
-
-use std::os::unix::io::{AsRawFd, RawFd};
-use std::pin::Pin;
-use std::task::{Context, Poll};
-
-use futures::stream::{Stream, TryStream};
-use futures::ready;
-use tokio::io::{AsyncRead, AsyncWrite};
-use tokio::net::TcpListener;
-
-
-/// Tokio's `Incoming` now is a reference type and hyper's `AddrIncoming` misses some standard
-/// stuff like `AsRawFd`, so here's something implementing hyper's `Accept` from a `TcpListener`
-pub struct StaticIncoming(TcpListener);
-
-impl From<TcpListener> for StaticIncoming {
-    fn from(inner: TcpListener) -> Self {
-        Self(inner)
-    }
-}
-
-impl AsRawFd for StaticIncoming {
-    fn as_raw_fd(&self) -> RawFd {
-        self.0.as_raw_fd()
-    }
-}
-
-impl hyper::server::accept::Accept for StaticIncoming {
-    type Conn = tokio::net::TcpStream;
-    type Error = std::io::Error;
-
-    fn poll_accept(
-        self: Pin<&mut Self>,
-        cx: &mut Context,
-    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
-        let this = self.get_mut();
-        loop {
-            match ready!(this.0.poll_accept(cx)) {
-                Ok((conn, _addr)) => return Poll::Ready(Some(Ok(conn))),
-                Err(err) => {
-                    eprintln!("error accepting connection: {}", err);
-                    continue;
-                }
-            }
-        }
-    }
-}
-
-/// We also implement TryStream for this, as tokio doesn't do this anymore either and we want to be
-/// able to map connections to then add eg. ssl to them. This support code makes the changes
-/// required for hyper 0.13 a bit less annoying to read.
-impl Stream for StaticIncoming {
-    type Item = std::io::Result<(tokio::net::TcpStream, std::net::SocketAddr)>;
-
-    fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
-        match self.get_mut().0.poll_accept(cx) {
-            Poll::Pending => Poll::Pending,
-            Poll::Ready(result) => Poll::Ready(Some(result)),
-        }
-    }
-}
-
-/// Implement hyper's `Accept` for any `TryStream` of sockets:
-pub struct HyperAccept<T>(pub T);
-
-
-impl<T, I> hyper::server::accept::Accept for HyperAccept<T>
-where
-    T: TryStream<Ok = I> + Unpin,
-    I: AsyncRead + AsyncWrite,
-{
-    type Conn = I;
-    type Error = T::Error;
-
-    fn poll_accept(
-        self: Pin<&mut Self>,
-        cx: &mut Context,
-    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
-        let this = Pin::new(&mut self.get_mut().0);
-        this.try_poll_next(cx)
-    }
-}
diff --git a/src/tools/mod.rs b/src/tools/mod.rs
index 5dc129f0..d8ed7275 100644
--- a/src/tools/mod.rs
+++ b/src/tools/mod.rs
@@ -13,7 +13,6 @@ use proxmox_http::{
 };
 
 pub mod apt;
-pub mod async_io;
 pub mod config;
 pub mod disks;
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api
  2021-09-28  9:11 [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example Dominik Csapak
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api Dominik Csapak
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 2/3] remove tools/async_io.rs Dominik Csapak
@ 2021-09-28  9:11 ` Dominik Csapak
  2021-09-29  6:41   ` Thomas Lamprecht
  2 siblings, 1 reply; 6+ messages in thread
From: Dominik Csapak @ 2021-09-28  9:11 UTC (permalink / raw)
  To: pbs-devel

show how to generally start a daemon that serves a rest api + index page

api calls are:
/		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@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@proxmox.com>"]
 edition = "2018"
 description = "REST server implementation"
 
+# for example
+[dev-dependencies]
+proxmox = { version = "0.13.4", features = ["router","api-macro"] }
+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
+
+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())
+        .unwrap()
+}
+
+// a few examples on how to do api calls with the Router
+
+#[api]
+/// A simple ping method. returns "pong"
+fn ping() -> Result<String, Error> {
+    Ok("pong".to_string())
+}
+
+lazy_static! {
+    static ref ITEM_MAP: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
+}
+
+#[api]
+/// Lists all current items
+fn list_items() -> Result<Vec<String>, Error> {
+    Ok(ITEM_MAP.lock().unwrap().keys().map(|k| k.clone()).collect())
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                type: String,
+                description: "The name",
+            },
+            value: {
+                type: String,
+                description: "The value",
+            },
+        },
+    },
+)]
+/// creates a new item
+fn create_item(name: String, value: String) -> Result<(), Error> {
+    let mut map = ITEM_MAP.lock().unwrap();
+    if map.contains_key(&name) {
+        bail!("{} already exists", name);
+    }
+
+    map.insert(name, value);
+
+    Ok(())
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                type: String,
+                description: "The name",
+            },
+        },
+    },
+)]
+/// returns the value of an item
+fn get_item(name: String) -> Result<String, Error> {
+    ITEM_MAP.lock().unwrap().get(&name).map(|s| s.to_string()).ok_or_else(|| format_err!("no such item '{}'", name))
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                type: String,
+                description: "The name",
+            },
+            value: {
+                type: String,
+                description: "The value",
+            },
+        },
+    },
+)]
+/// updates an item
+fn update_item(name: String, value: String) -> Result<(), Error> {
+    if let Some(val) = ITEM_MAP.lock().unwrap().get_mut(&name) {
+        *val = value;
+    } else {
+        bail!("no such item '{}'", name);
+    }
+    Ok(())
+}
+
+#[api(
+    input: {
+        properties: {
+            name: {
+                type: String,
+                description: "The name",
+            },
+        },
+    },
+)]
+/// deletes an item
+fn delete_item(name: String) -> Result<(), Error> {
+    if ITEM_MAP.lock().unwrap().remove(&name).is_none() {
+        bail!("no such item '{}'", name);
+    }
+    Ok(())
+}
+
+const ITEM_ROUTER: Router = Router::new()
+    .get(&API_METHOD_GET_ITEM)
+    .put(&API_METHOD_UPDATE_ITEM)
+    .delete(&API_METHOD_DELETE_ITEM);
+
+const SUBDIRS: SubdirMap = &[
+    (
+        "items",
+        &Router::new()
+            .get(&API_METHOD_LIST_ITEMS)
+            .post(&API_METHOD_CREATE_ITEM)
+            .match_all("name", &ITEM_ROUTER)
+    ),
+    (
+        "ping",
+        &Router::new()
+            .get(&API_METHOD_PING)
+    ),
+];
+
+const ROUTER: Router = Router::new()
+    .get(&list_subdirs_api_method!(SUBDIRS))
+    .subdirs(SUBDIRS);
+
+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())))
+        },
+        "example_server",
+    ).await?;
+
+    Ok(())
+}
+
+fn main() -> Result<(), Error> {
+    pbs_runtime::main(run())
+}
-- 
2.30.2





^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api Dominik Csapak
@ 2021-09-29  6:27   ` Thomas Lamprecht
  0 siblings, 0 replies; 6+ messages in thread
From: Thomas Lamprecht @ 2021-09-29  6:27 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Dominik Csapak

On 28.09.21 11:11, Dominik Csapak wrote:
> so that the StaticIncoming wrapper is not necessary anymore.

since when and why? IIRC it was some addition to tokio that fixed it.
Such info is important for review and git history, especially for such a crucial
code part...

> The only
> thing we have to do is to change the Service impl from tokio::net::TcpStream
> to hyper::server::conn::AddStream to fulfill the trait requirements.
> 
> Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
> ---
> new in v2
>  proxmox-rest-server/src/rest.rs | 18 ++++++++----------
>  src/bin/proxmox-backup-api.rs   |  3 ++-
>  2 files changed, 10 insertions(+), 11 deletions(-)
> 
> diff --git a/proxmox-rest-server/src/rest.rs b/proxmox-rest-server/src/rest.rs
> index ba2edf11..8c1e34a3 100644
> --- a/proxmox-rest-server/src/rest.rs
> +++ b/proxmox-rest-server/src/rest.rs
> @@ -93,7 +93,7 @@ impl tower_service::Service<&Pin<Box<tokio_openssl::SslStream<tokio::net::TcpStr
>      }
>  }
>  
> -impl tower_service::Service<&tokio::net::TcpStream> for RestServer {
> +impl tower_service::Service<&hyper::server::conn::AddrStream> for RestServer {
>      type Response = ApiService;
>      type Error = Error;
>      type Future = Pin<Box<dyn Future<Output = Result<ApiService, Error>> + Send>>;
> @@ -102,15 +102,13 @@ impl tower_service::Service<&tokio::net::TcpStream> for RestServer {
>          Poll::Ready(Ok(()))
>      }
>  
> -    fn call(&mut self, ctx: &tokio::net::TcpStream) -> Self::Future {
> -        match ctx.peer_addr() {
> -            Err(err) => future::err(format_err!("unable to get peer address - {}", err)).boxed(),
> -            Ok(peer) => future::ok(ApiService {
> -                peer,
> -                api_config: self.api_config.clone(),
> -            })
> -            .boxed(),
> -        }
> +    fn call(&mut self, ctx: &hyper::server::conn::AddrStream) -> Self::Future {
> +        let peer =  ctx.remote_addr();
> +        future::ok(ApiService {
> +            peer,
> +            api_config: self.api_config.clone(),
> +        })
> +        .boxed()
>      }
>  }
>  
> diff --git a/src/bin/proxmox-backup-api.rs b/src/bin/proxmox-backup-api.rs
> index 86650de6..e3f2531f 100644
> --- a/src/bin/proxmox-backup-api.rs
> +++ b/src/bin/proxmox-backup-api.rs
> @@ -108,7 +108,8 @@ async fn run() -> Result<(), Error> {
>      let server = daemon::create_daemon(
>          ([127,0,0,1], 82).into(),
>          move |listener, ready| {
> -            let incoming = proxmox_backup::tools::async_io::StaticIncoming::from(listener);
> +            let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?;
> +
>              Ok(ready
>                  .and_then(|_| hyper::Server::builder(incoming)
>                      .serve(rest_server)
> 





^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api
  2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak
@ 2021-09-29  6:41   ` Thomas Lamprecht
  0 siblings, 0 replies; 6+ messages in thread
From: Thomas Lamprecht @ 2021-09-29  6:41 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Dominik Csapak

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@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@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.

> +}
> 





^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-09-29  6:42 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-28  9:11 [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example Dominik Csapak
2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 1/3] rest-server: use hypers AddrStream for proxmox-backup-api Dominik Csapak
2021-09-29  6:27   ` Thomas Lamprecht
2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 2/3] remove tools/async_io.rs Dominik Csapak
2021-09-28  9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak
2021-09-29  6:41   ` Thomas Lamprecht

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal