I think this may be answered elsewhere, but for some reason it's rather hard to get search results using the term "async-std" :-]
@jedisct1 Thanks! I like that Hyper is battle-tested, much more so than tide.
hyper has optional features tcp and runtime which provide easy support for Tokio.
It's possible to disable these optional features, and "provide your own runtime", like has been done in tide and Fuchsia, for example.
@seanmonstar Okay, to paraphrase what you're saying: would you say that there isn't a simple way to use hyper with async-std's runtime?
I like that Hyper is battle-tested, much more so than tide
Tide uses Hyper as a backend.
@jedisct1 so tide basically is an example of how to use hyper with another executor?
tide is modular and provides a modern abstraction layer to multiple backends. The default backend is hyper.
@Extrawurst I think using hyper implies using tokio. Tide can use hyper, and if it does, it's likely using tokio.
No, that is not correct.
Tide doesn't use tokio, only async-std.
@rw Hyper out of the box supports tokio since, the people that work on hyper also work on tokio. That said, internally hyper uses traits for everything meaning with a little bit of work you can swap things out. Therefore, hyper can be used on any executor/runtime if you swap out the correct types. Nothing ties it directly to tokio besides its defaults.
TL;DR: Hyper _can_ support async-std or any other runtime via using traits like Executor and swapping out connectors. Hyper does provide defaults like https connectors etc that require tokio but these are addons.
See https://github.com/leo-lb/hyper-async-std for a project that is demoing how to use hyper on async-std (and is likely a good example of how to use hyper on any other runtime as well).
Instructions copied from https://github.com/async-rs/async-std-hyper
Full Cargo.toml: https://github.com/async-rs/async-std-hyper/blob/master/Cargo.toml
Full code: https://github.com/async-rs/async-std-hyper/blob/master/src/main.rs
Add async-std, hyper, and tokio as dependencies to your crate:
[dependencies]
async-std = "1"
hyper = { version = "0.13", default-features = false }
tokio = { version = "0.2", default-features = false }
Copy this compat module into your crate:
pub mod compat {
use std::pin::Pin;
use std::task::{Context, Poll};
use async_std::io;
use async_std::net::{TcpListener, TcpStream};
use async_std::prelude::*;
use async_std::task;
#[derive(Clone)]
pub struct HyperExecutor;
impl<F> hyper::rt::Executor<F> for HyperExecutor
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
fn execute(&self, fut: F) {
task::spawn(fut);
}
}
pub struct HyperListener(pub TcpListener);
impl hyper::server::accept::Accept for HyperListener {
type Conn = HyperStream;
type Error = io::Error;
fn poll_accept(
mut self: Pin<&mut Self>,
cx: &mut Context,
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
let stream = task::ready!(Pin::new(&mut self.0.incoming()).poll_next(cx)).unwrap()?;
Poll::Ready(Some(Ok(HyperStream(stream))))
}
}
pub struct HyperStream(pub TcpStream);
impl tokio::io::AsyncRead for HyperStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl tokio::io::AsyncWrite for HyperStream {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_close(cx)
}
}
}
Configure the hyper builder with:
let server = Server::builder(compat::HyperListener(listener))
.executor(compat::Executor);
Full example:
use std::convert::Infallible;
use async_std::net::TcpListener;
use async_std::task;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server};
use compat; // This is the module from Step 2.
async fn hello(_: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello World!")))
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
task::block_on(async {
let addr = "127.0.0.1:3000";
let listener = TcpListener::bind(addr).await?;
let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(hello)) });
let server = Server::builder(compat::HyperListener(listener))
.executor(compat::HyperExecutor)
.serve(make_svc);
println!("Listening on http://{}", addr);
server.await?;
Ok(())
})
}
Most helpful comment
How to run Hyper on async-std
Instructions copied from https://github.com/async-rs/async-std-hyper
Full
Cargo.toml: https://github.com/async-rs/async-std-hyper/blob/master/Cargo.tomlFull code: https://github.com/async-rs/async-std-hyper/blob/master/src/main.rs
Step 1: Dependencies
Add
async-std,hyper, andtokioas dependencies to your crate:Step 2: Compatibility layer
Copy this
compatmodule into your crate:Step 3: Configure Hyper
Configure the
hyperbuilder with:Full example: