Why can't I connect to port 3000 ? (hyper / async io)

use hyper::{
    service::{make_service_fn, service_fn},
    Body, Request, Response, Server,
};
use std::{convert::Infallible, net::SocketAddr};
use tokio::runtime::Runtime;

async fn handle(_: Request<Body>) -> Result<Response<Body>, Infallible> {
    Ok(Response::new("Hello, world!".into()))}

async fn foo() {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) });
    let server = Server::bind(&addr).serve(make_svc);

    if let Err(e) = server.await {
        eprintln!("server error: {}", e);}}

#[test]
fn it_works() {
    let rt = Runtime::new().unwrap();
    rt.block_on(async { foo() });
    loop {}}

I run the code via cargo tests, the test infinite loops; but somehow curl 127.0.0.1:3000 returns

curl 127.0.0.1:8000
curl: (7) Failed to connect to 127.0.0.1 port 8000: Connection refused

Try port 3000 instead of 8000?

LOL. That was a mistake, but not the mistake. No better:

curl localhost:3000
curl: (7) Failed to connect to localhost port 3000: Connection refused

The crazy thing is that rt.block_on does not seem to block, so if I removed the loop {}, the cargo tests terminates. Thus, for whatever reason, rt.block_on is returning.

tl;dr: replace async { foo() } with foo() or async { foo().await }

Since foo is a async fn, foo() is a future, and async { foo() } is a future yielding a future.

2 Likes
  1. That fixed it.

  2. I am trying to figure out how to catch this in the future.

Current thought process is: instead of writing

rt.block_on(async { foo() });

I should write

let x = rt.block_on(async { foo() });

then when IntelliJ does type inference, instead of () it shows impl ... Future, and it makes it blatantly obvious that I am getting back a future, rather than executing it.

Any other advice for capturing this? Maybe to explicitly write

let _ () = rt.block_on(...);

which generates a compile time error if it returns a future

You can write

let () = ...;

Is this hard coded into the compiler? Otherwise, I don't see how we can have a const on the lhs without doing something like if let ...

The left-hand-side can be any irrefutable pattern. It's just like how you can do this:

let (a, b) = ...;

except the tuple is empty

It produces a warning:

warning: unused implementer of `Future` that must be used
  --> src/lib.rs:22:5
   |
22 |     rt.block_on(async { foo() });
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_must_use)]` on by default
   = note: futures do nothing unless you `.await` or poll them
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.