How to use a struct method as an handler in Axum? [duplicate] - rust

This question already has answers here:
How to call struct method from axum server route?
(1 answer)
Is there a way to create a function pointer to a method in Rust?
(1 answer)
Closed 2 months ago.
This works where the handler is a stand alone function
use axum::{
routing::{get, post},
http::StatusCode,
response::IntoResponse,
Json, Router,
};
use std::net::SocketAddr;
#[tokio::main]
async fn main() -> () {
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", axum::routing::get(root));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// basic handler that responds with a static string
async fn root() -> impl IntoResponse {
let person = Person {
age: 20,
name: "John".to_string()
};
(StatusCode::OK, Json(person))
}
but in my project, I need to have the logic of the handler as part of a struct impl. I tried doing that but it fails. Code and error is below
use axum::{
routing::{get, post},
http::StatusCode,
response::IntoResponse,
Json, Router,
};
use std::net::SocketAddr;
mod http {
pub struct ServerLogic {
}
impl ServerLogic {
pub fn new() -> Self {
Self {}
}
pub fn hello(&self) -> impl axum::response::IntoResponse {
(axum::http::StatusCode::OK, axum::Json("Hello, world!"))
}
}
}
#[tokio::main]
async fn main() -> () {
// build our application with a route
let app = Router::new()
// `GET /` goes to `root`
.route("/", axum::routing::get(crate::http::ServerLogic::new().hello));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
Error
error[E0615]: attempted to take value of method `hello` on type `ServerLogic`
--> src/main.rs:104:72
|
104 | .route("/", axum::routing::get(crate::http::ServerLogic::new().hello));
| ^^^^^ method, not a field
|
help: use parentheses to call the method
|
104 | .route("/", axum::routing::get(crate::http::ServerLogic::new().hello()));
| ++
How do I get to use the method of a struct as an handler?

Related

Actix and Inventory crate

I'm trying to make it possible to register an actix route automatically. To do so I found the crate Inventory which seems to answer my need.
I have the following code:
#[get("/hello2/{name}")]
async fn greet2(req: HttpRequest) -> String {
let name = req.match_info().get("name").unwrap_or("World");
let a = format!("Hello2 {}!", &name);
a
}
pub struct TestStruct {
test: fn(HttpRequest) -> dyn Future<Output = String>
}
inventory::collect!(TestStruct);
inventory::submit! {
let mut a = TestStruct {
test: <greet2 as HttpServiceFactory>::register
};
a.test.push(greet2::register::greet2);
a
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
let mut app = App::new();
for route in inventory::iter::<AutomaticHttpService> {
app = app.service(route);
}
app
})
.bind(("127.0.0.1", 8080))?
.workers(2)
.run()
.await
}
My code does not compile as I cannot access the requested function pointer and I cannot use the HttpServiceFactory trait as it is not declared in the same file. I also tried to use the std::any::Any type and then cast to HttpServiceFactory (maybe using unsafe) but without success because its size was not known at compile time.
Would you have any clues to unblock my situation?

Use hyper to pass IP address of incoming connection to stack of Services

I am trying to write a server using hyper that will pass the remote (client) address of the incoming connection down to a stack of Layers (that I have built using ServiceBuilder.
I have tried to use examples from the hyper docs and also this example from the Rust forums; however, these both
pass down data to a single handler function, not a stack of service layers
have a return type of Result<Response, Infallible>, which I don't want (I want to be able to drop a connection without returning a response).
Here is one of my tries (I have tried several approaches):
use std::{
net::SocketAddr,
time::Duration,
};
use hyper::{
Body, Request, Response, Server,
server::conn::AddrStream,
service::{
make_service_fn,
service_fn,
},
};
use tower::{
Service, ServiceBuilder,
timeout::TimeoutLayer,
};
async fn dummy_handle(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let response_text = format!(
"{:?} {} {}", req.version(), req.method(), req.uri()
);
let response = Response::new(Body::from(response_text));
Ok(response)
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
// Dummy stack of service layers I want to wrap.
let service = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_millis(1000 * 60)))
.service_fn(dummy_handle);
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
let mut inner_svc = service.clone();
let outer_svc = service_fn(move |mut req: Request<Body>| async {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
});
async move { outer_svc }
});
Server::bind(&addr)
.serve(make_svc)
.await?;
Ok(())
}
I understand full well that including error messages is helpful here; however, this is one of those cases where the Rust compiler spits out pages (or at least screenfuls) of cryptic stuff, so I am going to limit myself to a couple of choice examples.
First, I get this a lot:
type mismatch resolving `<impl Future<Output = [async output]> as Future>::Output == Result<_, _>`
for example, preceding this:
39 | let outer_svc = service_fn(move |mut req: Request<Body>| async {
| _____________________________________-___________________________________-
| | ____________________________________|
| ||
40 | || req.extensions_mut().insert(remote_addr);
41 | || inner_svc.call(req)
42 | || });
| || -
| ||_________|
| |__________the expected closure
| the expected `async` block
...
48 | .serve(make_svc)
| ----- ^^^^^^^^ expected struct `service::util::ServiceFn`, found enum `Result`
| |
| required by a bound introduced by this call
And then the very next error message seems to be entirely contradictory:
[ several lines identical to above elided here ]
48 | .serve(make_svc)
| ^^^^^ expected enum `Result`, found struct `service::util::ServiceFn`
I just can't figure out what the compiler wants from me.
Try this:
use std::{net::SocketAddr, time::Duration, convert::Infallible};
use hyper::{
server::conn::AddrStream,
service::{make_service_fn, service_fn},
Body, Request, Response, Server,
};
use tower::{Service, ServiceBuilder};
async fn dummy_handle(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
let response_text = format!("{:?} {} {}", req.version(), req.method(), req.uri());
let response = Response::new(Body::from(response_text));
Ok(response)
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
// Dummy stack of service layers I want to wrap.
let service = ServiceBuilder::new()
.timeout(Duration::from_millis(1000 * 60))
.service_fn(dummy_handle);
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
let mut inner_svc = service.clone();
let outer_svc = service_fn(move |mut req: Request<Body>| {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
});
async { Ok::<_, Infallible>(outer_svc) }
});
Server::bind(&addr).serve(make_svc).await?;
Ok(())
}
You were returning a future that returns another future:
|| async {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
}
This is an Future<Output = Future<...>>.
Therefore, you need to turn your closure into this:
|| {
req.extensions_mut().insert(remote_addr);
inner_svc.call(req)
}
If you do not need ServiceBuilder then you can do:
use std::{
net::SocketAddr,
};
use std::convert::Infallible;
use hyper::{
Body, Request, Response, Server,
server::conn::AddrStream,
service::{
make_service_fn,
service_fn,
},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let make_svc = make_service_fn(|socket: &AddrStream| {
let remote_addr = socket.remote_addr();
async move {
Ok::<_, Infallible>(service_fn(move |req: Request<Body>| async move {
println!("remote_addr: {:?}, request: {:?}", remote_addr, req);
Ok::<_, Infallible>(
Response::new(Body::from(format!(
"{:?} {} {}", req.version(), req.method(), req.uri()
)))
)
}))
}
});
let _ = Server::bind(&addr).serve(make_svc).await?;
Ok(())
}
else
use std::{
net::SocketAddr,
time::Duration,
task::{Context, Poll},
future::Future,
pin::Pin
};
use hyper::{
http,
Body, Request, Response, Server
};
use tower::{
timeout::TimeoutLayer,
Service, ServiceBuilder,
};
#[derive(Debug)]
pub struct CustomService;
impl Service<Request<Body>> for CustomService {
type Response = Response<Body>;
type Error = http::Error;
type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { Poll::Ready(Ok(())) }
fn call(&mut self, req: Request<Body>) -> Self::Future {
let rsp = Response::builder();
let body = Body::from(format!("{:?} {} {}", req.version(), req.method(), req.uri()));
let rsp = rsp.status(200).body(body).unwrap();
Box::pin(async {Ok(rsp) })
}
}
#[derive(Debug)]
pub struct MakeService;
impl<T> Service<T> for MakeService {
type Response = CustomService;
type Error = std::io::Error;
type Future = Pin<Box<dyn Future<Output=Result<Self::Response, Self::Error>> + Send>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { Ok(()).into() }
fn call(&mut self, _: T) -> Self::Future {
Box::pin(async { Ok(CustomService) })
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>>
{
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
let service = ServiceBuilder::new()
.layer(TimeoutLayer::new(Duration::from_secs(60)))
.service(MakeService);
let _ = Server::bind(&addr).serve(service).await?;
Ok(())
}

How can I get an Axum Handler function to return a Vec?

I'm learning how to use Axum with SQLx starting with this example. The basic example works, but I have problem trying to move forward. I am working with a simple database table as shown below:
todo | description
--------+--------------
todo_1 | doing todo 1
todo_2 | doing todo 2
todo_3 | doing todo 3
I am trying to simply get back "SELECT * FROM todos", but I am getting an error. I think I am getting the return of the Result type wrong but I am not sure what to do next. The entirety of main.rs is shown below.
//! Example of application using <https://github.com/launchbadge/sqlx>
//!
//! Run with
//!
//! ```not_rust
//! cd examples && cargo run -p example-sqlx-postgres
//! ```
//!
//! Test with curl:
//!
//! ```not_rust
//! curl 127.0.0.1:3000
//! curl -X POST 127.0.0.1:3000
//! ```
use axum::{
async_trait,
extract::{Extension, FromRequest, RequestParts},
http::StatusCode,
routing::get,
Router,
};
use sqlx::postgres::{PgPool, PgPoolOptions, PgRow};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use std::{net::SocketAddr, time::Duration};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| "example_tokio_postgres=debug".into()),
))
.with(tracing_subscriber::fmt::layer())
.init();
let db_connection_str = std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgres://postgres:postgres#localhost".to_string());
// setup connection pool
let pool = PgPoolOptions::new()
.max_connections(5)
.connect_timeout(Duration::from_secs(3))
.connect(&db_connection_str)
.await
.expect("can connect to database");
// build our application with some routes
let app = Router::new()
.route(
"/",
get(using_connection_pool_extractor).post(using_connection_extractor),
)
.layer(Extension(pool));
// run it with hyper
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
// we can extract the connection pool with `Extension`
async fn using_connection_pool_extractor(
Extension(pool): Extension<PgPool>,
) -> Result<Vec<String>, (StatusCode, String)> {
sqlx::query_scalar("select * from todos")
.fetch_one(&pool)
.await
.map_err(internal_error)
}
// we can also write a custom extractor that grabs a connection from the pool
// which setup is appropriate depends on your application
struct DatabaseConnection(sqlx::pool::PoolConnection<sqlx::Postgres>);
#[async_trait]
impl<B> FromRequest<B> for DatabaseConnection
where
B: Send,
{
type Rejection = (StatusCode, String);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let Extension(pool) = Extension::<PgPool>::from_request(req)
.await
.map_err(internal_error)?;
let conn = pool.acquire().await.map_err(internal_error)?;
Ok(Self(conn))
}
}
async fn using_connection_extractor(
DatabaseConnection(conn): DatabaseConnection,
) -> Result<String, (StatusCode, String)> {
let mut conn = conn;
sqlx::query_scalar("select 'hello world from pg'")
.fetch_one(&mut conn)
.await
.map_err(internal_error)
}
/// Utility function for mapping any error into a `500 Internal Server Error`
/// response.
fn internal_error<E>(err: E) -> (StatusCode, String)
where
E: std::error::Error,
{
(StatusCode::INTERNAL_SERVER_ERROR, err.to_string())
}
Compared to the example, I changed this function so that it returns a Vec<String> instead of a plain String, but I get a compiler error:
async fn using_connection_pool_extractor(
Extension(pool): Extension<PgPool>,
) -> Result<Vec<String>, (StatusCode, String)> {
sqlx::query_scalar("select * from todos")
.fetch_one(&pool)
.await
.map_err(internal_error)
}
error[E0277]: the trait bound `fn(Extension<Pool<sqlx::Postgres>>) -> impl Future<Output = Result<Vec<String>, (StatusCode, String)>> {using_connection_pool_extractor}: Handler<_, _>` is not satisfied
--> src/main.rs:52:17
|
52 | get(using_connection_pool_extractor).post(using_connection_extractor),
| --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(Extension<Pool<sqlx::Postgres>>) -> impl Future<Output = Result<Vec<String>, (StatusCode, String)>> {using_connection_pool_extractor}`
| |
| required by a bound introduced by this call
|
= help: the trait `Handler<T, ReqBody>` is implemented for `axum::handler::Layered<S, T>`
note: required by a bound in `axum::routing::get`
I am not sure what this error is suggesting or if it is even related to the actual problem.
Try using axum::Json:
async fn using_connection_pool_extractor(
Extension(pool): Extension<PgPool>,
) -> Result<axum::Json<Vec<String>>, (StatusCode, String)> {
sqlx::query_scalar("select * from todos")
.fetch_one(&pool)
.await
.map(|todos| axum::Json(todos))
.map_err(internal_error)
}
The reason why is that there's no implementation of the IntoResponse trait for Vec<T>. Here's a longer answer by Axum's author: https://users.rust-lang.org/t/axum-error-handling-trait-question/65530

How to conditionally add routes to Axum Router?

I'm using axum and this code to create a server but I get an error:
use axum::{response::Html, routing::get, Router};
async fn handler() -> Html<&'static str> {
Html("<h1>Hello, World!</h1>")
}
#[tokio::main]
async fn main() {
let router = Router::new();
router.route("/", get(handler));
if true { // I need to check something here
router.route("/other", get(handler));
}
axum::Server::bind(&([127, 0, 0, 1], 3000).into())
.serve(router.into_make_service())
.await
.unwrap();
}
error[E0382]: use of moved value: `router`
--> src\main.rs:18:16
|
9 | let router = Router::new();
| ------ move occurs because `router` has type `Router`, which does not implement the `Copy` trait
10 |
11 | router.route("/", get(handler));
| ------ value moved here
...
14 | router.route("/other", get(handler));
| ------ value moved here
...
18 | .serve(router.into_make_service())
| ^^^^^^ value used here after move
I tried with &router but I still get the error. How can I fix this? Should I use .clone()?
Using this code it works:
let mut router = Router::new();
router = router.route("/", get(handler));
if true {
router = router.route(...);
}
Here is simplified code (Axum does the same thing). route takes ownership but also returns it.
#[derive(Debug)]
struct Router {
list: Vec<String>,
}
impl Router {
fn new() -> Self {
Router { list: vec![] }
}
fn route(mut self, path: String) -> Self {
self.list.push(path);
self
}
}
fn main() {
let mut router = Router::new().route("/1".to_owned()).route("/2".to_owned());
if true {
router = router.route("/3".to_owned());
}
println!("{:?}", router);
}
So you have to set router in-line, or make it mut and reassign result of route to router variable.

Avoiding closure - encapsulating thread variables in a struct

I am writing a simple websocket server named BoltServer based on Rust websocket crate(Code is incomplete, I just started). I am using the example program as the base. However the example programs are not modular (having very long methods). So I am trying to break them up into structs and methods. I want to spawn two threads for each client. One sends messages and the other one receives messages. So here, I want to capture all variables used by the thread in a struct and then call the run method in the impl.
extern crate websocket;
extern crate time;
extern crate rustc_serialize;
pub mod ws {
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::net::ToSocketAddrs;
use websocket;
use websocket::{Server, Message, Sender, Receiver};
use websocket::server::Connection;
use websocket::stream::WebSocketStream;
use std::str::from_utf8;
struct BoltUser {
user_id: u32,
my_tx: mpsc::Sender<String>,
}
struct Broadcaster {
my_rx: mpsc::Receiver<String>,
}
impl Broadcaster {
fn new(receiver: mpsc::Receiver<String>) -> Broadcaster {
Broadcaster { my_rx: receiver }
}
fn run(self) {
while let Ok(msg) = self.my_rx.recv() {
println!("Broadcaster got message: {}", msg);
}
}
}
struct SocketReader {}
impl SocketReader {
fn run(self) {}
}
struct SocketWriter {
my_rx: mpsc::Receiver<String>,
sender: Sender,
}
impl SocketWriter {
fn run(self) {
while let Ok(message) = self.my_rx.recv() {
}
}
}
pub struct BoltServer {
address: String,
connected_users: Arc<Mutex<Vec<BoltUser>>>,
}
impl BoltServer {
pub fn new(address: &str) -> BoltServer {
BoltServer {
address: address.to_string(),
connected_users: Arc::new(Mutex::new(vec![])),
}
}
fn handshake(&mut self,
connection: Connection<WebSocketStream, WebSocketStream>)
-> (SocketWriter, SocketReader) {
let request = connection.read_request().unwrap();
// println!("thread-> Accepting request...");
let response = request.accept();
let (mut sender, mut receiver) = response.send().unwrap().split();
let (user_tx, user_rx) = mpsc::channel::<String>();//Create a channel for writer
let socket_writer = SocketWriter {
my_rx: user_rx,
sender: sender,
};
let socket_reader = SocketReader {};
(socket_writer, socket_reader)
}
pub fn start(&mut self) {
println!("Starting");
let (broadcaster_tx, broadcaster_rx) = mpsc::channel::<String>();
let broadcaster = Broadcaster::new(broadcaster_rx);
let handle = thread::Builder::new()
.name("Broadcaster".to_string())
.spawn(move || broadcaster.run());
let server = Server::bind(&*self.address).unwrap();
let mut user_id: u32 = 0;
// Block and process connection request from a new client
for connection in server {
user_id = user_id + 1;//Create a new user id
let (socket_writer, socket_reader) = self.handshake(connection);
thread::Builder::new()
.name("Socket writer".to_string())
.spawn(move || socket_writer.run());
thread::Builder::new()
.name("Socket reader".to_string())
.spawn(move || socket_reader.run());
}
handle.unwrap().join();
println!("Finished");
}
}
}
The following code gives an idea of what I want to achieve.
// Block and process connection request from a new client
for connection in server {
user_id = user_id + 1;//Create a new user id
let (socket_writer, socket_reader) = self.handshake(connection);
thread::Builder::new().name("Socket writer".to_string()).spawn(move || {
socket_writer.run()
});
thread::Builder::new().name("Socket reader".to_string()).spawn(move || {
socket_reader.run()
});
}
Here I am stuck in the handshake method. I am not able to initialize the SocketWriter struct with the sender that I am getting by calling the split method in the library. I am getting the following compilation error:
error[E0038]: the trait `websocket::Sender` cannot be made into an object
--> src/lib.rs:46:9
|
46 | sender:Sender,
| ^^^^^^^^^^^^^ the trait `websocket::Sender` cannot be made into an object
|
= note: method `send_dataframe` has generic type parameters
= note: method `send_message` has generic type parameters
The error is telling you the immediate problem:
46 | sender:Sender,
| ^^^^^^^^^^^^^ the trait `websocket::Sender` cannot be made into an object
First of all, a variable/field can't have a plain trait type (but &Trait can be possible), but also the websocket::Sender trait is not object safe; it has generic methods which can't work dynamically (ie vtable methods have to have a fixed type).
Instead, you have to have a concrete type (you could also make it a generic struct).
It's not obvious what the right type is, so I like to get the compiler to tell me. So first try the simplest possible:
sender: (),
The compiler replies with some information:
| ^^^^^^ expected (), found struct `websocket::client::Sender`
Ok, let's plug that in:
sender: websocket::client::Sender,
That gives:
46 | sender: websocket::client::Sender,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected 1 type arguments, found 0
Ok, that's a generic type. Next try:
sender: websocket::client::Sender<()>,
Finally it gives us the real type:
74 | sender:sender,
| ^^^^^^ expected (), found enum `websocket::WebSocketStream`
So finally we can finish SocketWriter:
struct SocketWriter {
my_rx: mpsc::Receiver<String>,
sender: websocket::client::Sender<websocket::WebSocketStream>,
}
There's a following compile error since the connection you get is a Result<> so you need to check for errors (it compiles if I change to self.handshake(connection.unwrap()), but that's obviously not the best practice.

Resources