1

The docs for the hyper crate have a simple example to start a web server:

extern crate hyper;

use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};

fn main() {
    // Construct our SocketAddr to listen on...
    let addr = ([127, 0, 0, 1], 3000).into();

    // And a NewService to handle each connection...
    let new_service = || service_fn_ok(|_req| Response::new(Body::from("Hello World")));

    // Then bind and serve...
    let server = Server::bind(&addr).serve(new_service);

    // Finally, spawn `server` onto an Executor...
    hyper::rt::run(server.map_err(|e| {
        eprintln!("server error: {}", e);
    }));
}

I want to embed this web server in a procedure that returns an error:

// Executes a web server. Returns a result object with an error object
// if the server stopped unexpectedly
fn run_and_stop_web_server(addr: std::net::SocketAddr) -> Result<(), Error> {
    let server_builder = Server::try_bind(&addr)?;
    let server = server_builder.serve(move || {
        let handler: Handler = Handler::new();
        hyper::service::service_fn(move |req| handler.serve(req))
    });

    // Run the server
    // HERE: I don't know how to handle the error so I can return it from
    //       the run_and_stop_web_server() function
    hyper::rt::run(server.map_err(|e| eprintln!("Ignored Server error: {}", e)));
    Result::Ok(())
}

I don't know how I can handle the error. Instead of using eprintln!() I want to return the error from the run_and_stop_web_server function.

Mildred
  • 463
  • 4
  • 13
  • Why not using the `?`-operator? – hellow Aug 23 '18 at 13:58
  • You mean, use something like `hyper::rt::run(...)?;`... But it [doesn't return an error](https://docs.rs/hyper/0.12.8/hyper/rt/fn.run.html), does it? – Mildred Aug 23 '18 at 14:49
  • 1
    You mean perhaps I can use the `?` operator inside the closure like `server.map_err(|e| e?)` or something like that? How can it work, it's inside a closure? – Mildred Aug 23 '18 at 14:51

2 Answers2

1

I believe I found a solution using the tokio runtime directly... It compiles. Please anyone tell me if I'm wrong here.

The idea is to use the tokio Runtime directly (docs have many examples):

use tokio::runtime::Runtime;

// Executes a web server. Returns a result object with an error object
// if the server stopped unexpectedly
fn run_and_stop_web_server(addr: std::net::SocketAddr) -> Result<(), Error> {
    let server_builder = Server::try_bind(&addr)?;
    let server = server_builder.serve(move || {
        let handler: Handler = Handler::new();
        hyper::service::service_fn(move |req| handler.serve(req))
    });

    let mut rt = Runtime::new()?;
    rt.block_on(server)?;
    rt.shutdown_now();

    Result::Ok(())
}
Mildred
  • 463
  • 4
  • 13
  • `block_on` is a blocking call. How do I move `server` into `rt.spawn`? – Blake H Nov 27 '18 at 17:41
  • The answer above is almost correct. `server` is `impl Future`, so you need to `use tokio::prelude::Future;` and replace the `block_on` with `rt.spawn(server.map_err(|_| ())`. You'll probably then want to use something like std::sync::mpsc::channel to provide a poison api instead of immediately shutting down – Blake H Nov 27 '18 at 17:49
-1

Maybe you can store the error into a local variable and return that:

let mut result = Result::Ok(());
hyper::rt::run(server.map_err(|e| { result = Result::Error (e); });
result
Jmb
  • 18,893
  • 2
  • 28
  • 55
  • How does this terminate the server? – Shepmaster Aug 24 '18 at 11:46
  • Originally, I didn't ask on how to terminate the server (the question was rephrased). But I wouldn't mind knowing how I could have the server terminated on some events (a signal, SIGTERM, for instance). But to answer that we would need to know how do we know we want to terminate the server. – Mildred Aug 24 '18 at 12:23
  • 1
    This doesn't work, the compiler highlights the closure telling **closure may outlive the current function, but it borrows `result`, which is owned by the current function**. – Mildred Aug 24 '18 at 12:37
  • It suggests to add **move** as in `server.map_err(move |e| { result = ... })` but then the `result` is no longer available to return the error to the caller. – Mildred Aug 24 '18 at 12:38
  • @Mildred I rephrased the question (you can see the [edit history](https://stackoverflow.com/posts/51987105/revisions)). Exiting the server is *required* because otherwise the program execution will never continue past the call to `run`, rendering the "return value" impossible. – Shepmaster Aug 24 '18 at 12:45
  • Ok, I was supposing that the first error would by itself terminate the server, but if not, it needs to. – Mildred Aug 24 '18 at 16:07
  • @Shepmaster when I answered the question, it said nothing about stopping the server. – Jmb Aug 27 '18 at 06:30
  • @Jmb I understand, but if the server never stops, your line of just `result` will never be executed. Without stopping the server, nothing else matters. It also appears that the OP states your answer doesn't even compile. – Shepmaster Aug 27 '18 at 11:59