hyperium / hyper

An HTTP library for Rust
https://hyper.rs
MIT License
14.42k stars 1.59k forks source link

Got error "Transport endpoint is not connected" when benchmarking the hello example with wrk #3669

Closed SteveLauC closed 4 months ago

SteveLauC commented 4 months ago

Version List the version(s) of hyper, and any relevant hyper dependency (such as h2 if this is related to HTTP/2).

[package]
name = "hyper_reproduce"
version = "0.1.0"
edition = "2021"

[dependencies]
bytes = "1.6.0"
http-body-util = "0.1.1"
hyper = { version = "1.3.1", features = ["full"] }
hyper-util = { version = "0.1.3", features = ["full"] }
pretty_env_logger = "0.5.0"
tokio = { version = "1.37.0", features = ["full"] }

Platform The output of uname -a (UNIX), or version and 32 or 64-bit (Windows)

$ uname -a
Linux fedora 6.2.9-300.fc38.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Mar 30 22:32:58 UTC 2023 x86_64 GNU/Linux

Description Enter your issue details here. One way to structure the description:

Got error:

Error serving connection: hyper::Error(Shutdown, Os { code: 107, kind: NotConnected, message: "Transport endpoint is not connected" })

I tried this code:

use std::convert::Infallible;
use std::net::SocketAddr;

use bytes::Bytes;
use http_body_util::Full;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use tokio::net::TcpListener;
use hyper_util::rt::{TokioIo, TokioTimer};

// An async function that consumes a request, does nothing with it and returns a
// response.
async fn hello(_: Request<impl hyper::body::Body>) -> Result<Response<Full<Bytes>>, Infallible> {
    Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
}

#[tokio::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    pretty_env_logger::init();

    // This address is localhost
    let addr: SocketAddr = ([127, 0, 0, 1], 3000).into();

    // Bind to the port and listen for incoming TCP connections
    let listener = TcpListener::bind(addr).await?;
    println!("Listening on http://{}", addr);
    loop {
        // When an incoming TCP connection is received grab a TCP stream for
        // client<->server communication.
        //
        // Note, this is a .await point, this loop will loop forever but is not a busy loop. The
        // .await point allows the Tokio runtime to pull the task off of the thread until the task
        // has work to do. In this case, a connection arrives on the port we are listening on and
        // the task is woken up, at which point the task is then put back on a thread, and is
        // driven forward by the runtime, eventually yielding a TCP stream.
        let (tcp, _) = listener.accept().await?;
        // Use an adapter to access something implementing `tokio::io` traits as if they implement
        // `hyper::rt` IO traits.
        let io = TokioIo::new(tcp);

        // Spin up a new task in Tokio so we can continue to listen for new TCP connection on the
        // current task without waiting for the processing of the HTTP1 connection we just received
        // to finish
        tokio::task::spawn(async move {
            // Handle the connection from the client using HTTP1 and pass any
            // HTTP requests received on that connection to the `hello` function
            if let Err(err) = http1::Builder::new()
                .timer(TokioTimer::new())
                .serve_connection(io, service_fn(hello))
                .await
            {
                println!("Error serving connection: {:?}", err);
            }
        });
    }
}

Server:

$ cd hyper_reproduce
$ cargo b -r -q
$ ./target/release/hyper_reproduce
Listening on http://127.0.0.1:3000

Client:

$ wrk --version
wrk 4.2.0 [epoll] Copyright (C) 2012 Will Glozer

$ wrk -d 30 -t 3 -c 1000 http://127.0.0.1:3000

I expected to see this happen:

All the requests sent by wrk can be successfully handled.

Instead, this happened:

image

seanmonstar commented 4 months ago

Likely the combination of wrk tossing the connection quickly and being over localhost, the hyper server notices the connection is no longer connected when trying to shutdown. It returns the error from the operating system, letting the user decide whether to care based on the context. In your case, you can likely just ignore that error.

SteveLauC commented 4 months ago

Get it, thanks for your reply!