-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create server instance by passing in TcpListener #1602
Comments
You can do this: extern crate futures;
extern crate tokio;
extern crate hyper;
use futures::prelude::*;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
fn main() {
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], 3000));
let listener = std::net::TcpListener::bind(&addr).unwrap();
let handle = tokio::reactor::Handle::current();
let listener = tokio::net::TcpListener::from_std(listener, &handle).unwrap();
let new_service = || service_fn_ok(|_req| Response::new(Body::from("Hello World")));
let server = Server::builder(listener.incoming()).serve(new_service);
tokio::run(server.map_err(|e| {
eprintln!("server error: {}", e);
}));
} |
Yea, as the example pasted shows, this is currently possible. It's this method here: https://docs.rs/hyper/0.12.*/hyper/server/struct.Server.html#method.builder Perhaps it could be clearer explained that this is how you do that? Maybe the |
@bluetech awesome, thanks so much for your reply! That looks exactly like what I needed. @seanmonstar I think an example in the docs would definitely be helpful! |
I forgot to mention: hyper optionally sets some TCP options on the accepted sockets when it creates the listener. If you create the listener and you want to set them, you can do it as follows: diff --git a/src/main.rs b/src/main.rs
index a74a15c..5d2f29c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,10 +11,15 @@ fn main() {
let listener = std::net::TcpListener::bind(&addr).unwrap();
let handle = tokio::reactor::Handle::current();
let listener = tokio::net::TcpListener::from_std(listener, &handle).unwrap();
+ let incoming = listener.incoming().map(|socket| {
+ socket.set_nodelay(true).unwrap();
+ socket.set_keepalive(Some(std::time::Duration::from_secs(7200))).unwrap();
+ socket
+ });
let new_service = || service_fn_ok(|_req| Response::new(Body::from("Hello World")));
- let server = Server::builder(listener.incoming()).serve(new_service);
+ let server = Server::builder(incoming).serve(new_service);
tokio::run(server.map_err(|e| {
eprintln!("server error: {}", e); |
I took this back to the CLI WG a few days ago, and I don't think we've quite nailed the solution yet. Getting boundaries right for crates is tricky! I think the core of the issue for us right now is that in ExamplesI think showing the problem might work best: Current UsageThis is the current recommended usage. It's 3 lines using APIs from 3 different sources. For people that are getting started with HTTP, it requires that they understand the difference between sync / async sockets, and that they touch the use clap_port_flag::Port;
use futures::prelude::*;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
struct Cli {
#[structopt(flatten)]
port: Port,
}
fn main() -> Result<(), Box<std::error::Error>> {
// Parse CLI args
let args = Cli::from_args();
// Create TCP socket
let listener = args.port.bind()?;
let handle = tokio::reactor::Handle::current();
let listener = tokio::net::TcpListener::from_std(listener, &handle)?;
// Run service
let server = Server::builder(listener.incoming())
.serve(|| service_fn_ok(|_| Response::new(Body::from("Hello World"))))
.map_err(|e| eprintln!("server error: {}", e));
tokio::run(server);
Ok(())
} Tokio in clap-port-flagIn this version we would integrate all use clap_port_flag::Port;
use futures::prelude::*;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
struct Cli {
#[structopt(flatten)]
port: Port,
}
fn main() -> Result<(), Box<std::error::Error>> {
// Parse CLI args
let args = Cli::from_args();
// Create TCP socket
let listener = args.port.bind_tokio()?;
// Run service
let server = Server::builder(listener.incoming())
.serve(|| service_fn_ok(|_| Response::new(Body::from("Hello World"))))
.map_err(|e| eprintln!("server error: {}", e));
tokio::run(server);
Ok(())
} std::net::TcpListener support in hyperThis version would expose a new method on Hyper that accepts a use clap_port_flag::Port;
use futures::prelude::*;
use hyper::service::service_fn_ok;
use hyper::{Body, Response, Server};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
struct Cli {
#[structopt(flatten)]
port: Port,
}
fn main() -> Result<(), Box<std::error::Error>> {
// Parse CLI args
let args = Cli::from_args();
// Create TCP socket, and run service
let server = Server::listen(args.port.bind()?)
.serve(|| service_fn_ok(|_| Response::new(Body::from("Hello World"))))
.map_err(|e| eprintln!("server error: {}", e));
tokio::run(server);
Ok(())
} Wrapping UpI hope this somewhat explains what we're running into. I can't claim to know what the right solution is, given I don't know all the constraints Hyper itself is facing. But I do think it'd be great if we could figure out a way to make all this setup easier! I def hope it makes sense where I'm coming from. Thanks again for your time! |
I think the last option seems the nicest, since it is sort of boilerplate to just convert a
Maybe something else, I don't know! |
Perhaps another option might be:
I feel like |
Adds the `from_tcp()` method as per hyperium#1602.
Adds the `from_tcp()` method as per hyperium#1602.
Adds the `from_tcp()` method as per hyperium#1602. fix clippy changes
Adds the `from_tcp()` method as per hyperium#1602. fix clippy changes
Adds the `from_tcp()` method as per hyperium#1602. fix clippy changes
Adds the `from_tcp()` method as per hyperium#1602. fix clippy changes
Adds the `from_tcp()` method as per hyperium#1602. fix clippy changes
Adds the `from_tcp()` method as per hyperium#1602. fix clippy changes
Hi there!
I was looking for a way to create a Hyper server instance from an existing
TcpListener
instance, and I couldn't find one. I was wondering it might be possible to add a method to allow for this.Note: I might totally have missed it if there's already a method that allows for this. Apologies if any of this is redundant!
Motivation
Systemd has the capability of creating sockets and handing them off to applications using the
$LISTEN_FD
env var. Crates such as listenfd and systemfd make clever use of this to provide an uninterrupted reload experience during development.As part of the CLI WG we've written clap-port-flag, which can create a socket from
--port
,$PORT
, or$LISTEN_FD
. It does this by exposing a.bind()
method that contains all logic necessary to do this.In order for Systemd's
$LISTEN_FD
to work with Hyper, a method would be needed to pass in aTcpListener
instance.Prior Art
actix-web
has aserver.listen()
method that takes aTcpListener
. It also exposes aserver.bind()
method which acts similar to Hyper's.bind()
method.Implementation
I propose adding a
hyper::server::Server::listen()
method with the following signature:Note: To be honest I'm not completely sure if
Builder<AddrIncoming>
would be the right return type here. I hope it is!Other Considerations
It looks like
actix-web
also exposes a.listen_tls()
method which also accepts a TLS struct. I'm not sure what the interactions between TLS and externalTcpListener
initialization would be in Hyper.References
Thanks so much for your time; I hope this is useful!
The text was updated successfully, but these errors were encountered: