async-tungstenite icon indicating copy to clipboard operation
async-tungstenite copied to clipboard

accept_hdr_async : cannot perform an async I/O in the callback because callback does not return a Future

Open mahdi-shojaee opened this issue 3 years ago • 9 comments

Hello, thanks for this nice crate. Sometimes it requires to accept only handshakes that pass an async I/O validation based on the values from request headers or URI. But currently, we cannot do such operations in the callback of the accept_hdr_async function. Any suggestion?

mahdi-shojaee avatar Dec 19 '20 14:12 mahdi-shojaee

Can you provide a (non-working) testcase for this, that would make it a bit clearer what you're trying to do. I suspect that it will need a little bit of API to be added, but nothing complicated.

sdroege avatar Dec 19 '20 14:12 sdroege

Sure:

async fn ws_accept(
    stream: TcpStream,
    db: <Some DB instance>
) -> std::result::Result<WebSocketStream<TcpStream>, async_tungstenite::tungstenite::Error> {
    async_tungstenite::accept_hdr_async(stream, |req: &Request, res: Response| async {
        if db.validate(req.uri()).await {
            return Ok(res);
        }
        Err(<Some Error>)
    }).await
}

mahdi-shojaee avatar Dec 19 '20 14:12 mahdi-shojaee

I see, so yes that would require adding a bit of API indeed and it's not going to be too easy unfortunately. The callback goes down deep into tungstenite here. That code knows nothing about async.

It would be necessary to extend the callback there with a way to return an std::io::Error so we could return WouldBlock here from our side to make it work with futures, and call into the handshake again once the future is ready again to be polled.

sdroege avatar Dec 19 '20 16:12 sdroege

Yes, I see, I think adding this will be so useful because it will let us dynamically accept connections just if for example some remote service validates the request. Any plan to add this?

mahdi-shojaee avatar Dec 19 '20 16:12 mahdi-shojaee

Because doing this synchronously is horrible.

mahdi-shojaee avatar Dec 19 '20 17:12 mahdi-shojaee

Yeah it seems useful to have, I agree :) I don't think I will have time for working on that anytime soon, but I'd be happy to review/help anybody who would like to implement it.

sdroege avatar Dec 19 '20 17:12 sdroege

i'm a rust beginner but i'd like to help implement this if i can, what exactly is required? being able to do async io in the connect callback is crucial :)

databasedav avatar Jun 07 '21 02:06 databasedav

@databasedav https://github.com/sdroege/async-tungstenite/issues/70#issuecomment-748497017 has the basic idea how this could be implemented

sdroege avatar Jun 07 '21 06:06 sdroege

@sdroege can u expand on this implementation? i'm thinking creating an async version of the Callback trait AsyncCallback

pub trait AsyncCallback: Sized {
    async fn on_request(
        self,
        request: &Request,
        response: Response,
    ) -> StdResult<Response, ErrorResponse>;
}

(which we would need the async-trait crate to make work) and then making async versions of the methods that wrap the on_request. is this the way to go? i might be ignoring some rust limitations, this is the sort of path i've taken when migrating similar python

databasedav avatar Jun 15 '21 00:06 databasedav