Skip to content

Commit 4931c8b

Browse files
committed
feat(server): add Server::serve_service
This adds `Server::serve_service` which is a convenience for doing ```rust Server::bind(&addr).serve(make_service_fn(|_| async move { Ok::<_, Infallible>(service_fn(handler)) })); ``` That now becomes ```rust Server::bind(&addr).serve_service(service_fn(handler)); ``` It basically copies [`tower::make::Shared`][] into Hyper so users don't need to add another dependency to do this. Fixes #2154 [`tower::make::Shared`]: https://docs.rs/tower/0.4.7/tower/make/struct.Shared.html
1 parent d1d2f32 commit 4931c8b

File tree

4 files changed

+116
-27
lines changed

4 files changed

+116
-27
lines changed

src/server/mod.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,8 @@
3434
//! // Construct our SocketAddr to listen on...
3535
//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
3636
//!
37-
//! // And a MakeService to handle each connection...
38-
//! let make_service = make_service_fn(|_conn| async {
39-
//! Ok::<_, Infallible>(service_fn(handle))
40-
//! });
41-
//!
4237
//! // Then bind and serve...
43-
//! let server = Server::bind(&addr).serve(make_service);
38+
//! let server = Server::bind(&addr).serve_service(service_fn(handle));
4439
//!
4540
//! // And run forever...
4641
//! if let Err(e) = server.await {
@@ -51,26 +46,30 @@
5146
//! # fn main() {}
5247
//! ```
5348
//!
54-
//! If you don't need the connection and your service implements `Clone` you can use
55-
//! [`tower::make::Shared`] instead of `make_service_fn` which is a bit simpler:
49+
//! If you need the incoming connection to handle the request you can use [`make_service_fn`] to
50+
//! create a [`Service`] on demand:
5651
//!
5752
//! ```no_run
58-
//! # use std::convert::Infallible;
59-
//! # use std::net::SocketAddr;
60-
//! # use hyper::{Body, Request, Response, Server};
61-
//! # use hyper::service::{make_service_fn, service_fn};
62-
//! # use tower::make::Shared;
63-
//! # async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
64-
//! # Ok(Response::new(Body::from("Hello World")))
65-
//! # }
53+
//! use std::convert::Infallible;
54+
//! use std::net::SocketAddr;
55+
//! use hyper::{Body, Request, Response, Server};
56+
//! use hyper::service::{make_service_fn, service_fn};
57+
//! use hyper::server::conn::AddrStream;
58+
//!
59+
//! async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
60+
//! Ok(Response::new(Body::from("Hello World")))
61+
//! }
62+
//!
6663
//! # #[cfg(feature = "runtime")]
6764
//! #[tokio::main]
6865
//! async fn main() {
6966
//! // Construct our SocketAddr to listen on...
7067
//! let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
7168
//!
72-
//! // Shared is a MakeService that produces services by cloning an inner service...
73-
//! let make_service = Shared::new(service_fn(handle));
69+
//! // And a MakeService to handle each connection...
70+
//! let make_service = make_service_fn(|conn: &AddrStream| async {
71+
//! Ok::<_, Infallible>(service_fn(handle))
72+
//! });
7473
//!
7574
//! // Then bind and serve...
7675
//! let server = Server::bind(&addr).serve(make_service);
@@ -84,7 +83,9 @@
8483
//! # fn main() {}
8584
//! ```
8685
//!
87-
//! [`tower::make::Shared`]: https://docs.rs/tower/latest/tower/make/struct.Shared.html
86+
//! [`make_service_fn`]: crate::service::make_service_fn
87+
//! [`Server::serve_service`]: crate::server::Server::serve_service
88+
//! [`Service`]: crate::service::Service
8889
8990
pub mod accept;
9091

src/server/server.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use super::accept::Accept;
1313
use crate::body::{Body, HttpBody};
1414
use crate::common::exec::{ConnStreamExec, Exec, NewSvcExec};
1515
use crate::common::{task, Future, Pin, Poll, Unpin};
16-
use crate::service::{HttpService, MakeServiceRef};
16+
use crate::service::{HttpService, MakeServiceRef, Shared, SharedFuture};
1717
// Renamed `Http` as `Http_` for now so that people upgrading don't see an
1818
// error that `hyper::server::Http` is private...
1919
use super::conn::{Http as Http_, NoopWatcher, SpawnAll};
@@ -418,22 +418,65 @@ impl<I, E> Builder<I, E> {
418418
/// }
419419
/// # }
420420
/// ```
421-
pub fn serve<S, B>(self, new_service: S) -> Server<I, S, E>
421+
pub fn serve<M, B>(self, new_service: M) -> Server<I, M, E>
422422
where
423423
I: Accept,
424424
I::Error: Into<Box<dyn StdError + Send + Sync>>,
425425
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
426-
S: MakeServiceRef<I::Conn, Body, ResBody = B>,
427-
S::Error: Into<Box<dyn StdError + Send + Sync>>,
426+
M: MakeServiceRef<I::Conn, Body, ResBody = B>,
427+
M::Error: Into<Box<dyn StdError + Send + Sync>>,
428428
B: HttpBody + 'static,
429429
B::Error: Into<Box<dyn StdError + Send + Sync>>,
430-
E: NewSvcExec<I::Conn, S::Future, S::Service, E, NoopWatcher>,
431-
E: ConnStreamExec<<S::Service as HttpService<Body>>::Future, B>,
430+
E: NewSvcExec<I::Conn, M::Future, M::Service, E, NoopWatcher>,
431+
E: ConnStreamExec<<M::Service as HttpService<Body>>::Future, B>,
432432
{
433433
let serve = self.protocol.serve(self.incoming, new_service);
434434
let spawn_all = serve.spawn_all();
435435
Server { spawn_all }
436436
}
437+
438+
/// Consume this `Builder`, creating a [`Server`](Server) that will run the given service.
439+
///
440+
/// # Example
441+
///
442+
/// ```
443+
/// # #[cfg(feature = "tcp")]
444+
/// # async fn run() {
445+
/// use hyper::{Body, Error, Response, Server};
446+
/// use hyper::service::{make_service_fn, service_fn};
447+
///
448+
/// // Construct our SocketAddr to listen on...
449+
/// let addr = ([127, 0, 0, 1], 3000).into();
450+
///
451+
/// // Our service used to respond to requests...
452+
/// let service = service_fn(|_req| async {
453+
/// Ok::<_, Error>(Response::new(Body::from("Hello World")))
454+
/// });
455+
///
456+
/// // Then bind and serve...
457+
/// let server = Server::bind(&addr).serve_service(service);
458+
///
459+
/// // Run forever-ish...
460+
/// if let Err(err) = server.await {
461+
/// eprintln!("server error: {}", err);
462+
/// }
463+
/// # }
464+
/// ```
465+
pub fn serve_service<S, B>(self, service: S) -> Server<I, Shared<S>, E>
466+
where
467+
S: HttpService<Body, ResBody = B> + Clone,
468+
S::Error: Into<Box<dyn StdError + Send + Sync>>,
469+
B: HttpBody + 'static,
470+
B::Error: Into<Box<dyn StdError + Send + Sync>>,
471+
I: Accept,
472+
I::Error: Into<Box<dyn StdError + Send + Sync>>,
473+
I::Conn: AsyncRead + AsyncWrite + Unpin + Send + 'static,
474+
E: NewSvcExec<I::Conn, SharedFuture<S>, S, E, NoopWatcher>,
475+
E: ConnStreamExec<S::Future, B>,
476+
{
477+
let make_service = Shared::new(service);
478+
self.serve(make_service)
479+
}
437480
}
438481

439482
#[cfg(feature = "tcp")]

src/service/make.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
use std::convert::Infallible;
12
use std::error::Error as StdError;
23
use std::fmt;
34

45
use tokio::io::{AsyncRead, AsyncWrite};
56

7+
use pin_project::pin_project;
8+
69
use super::{HttpService, Service};
710
use crate::body::HttpBody;
8-
use crate::common::{task, Future, Poll};
11+
use crate::common::{task, Future, Pin, Poll};
912

1013
// The same "trait alias" as tower::MakeConnection, but inlined to reduce
1114
// dependencies.
@@ -174,6 +177,48 @@ impl<F> fmt::Debug for MakeServiceFn<F> {
174177
}
175178
}
176179

180+
/// A `MakeService` that produces services by cloning an inner service.
181+
#[derive(Debug, Clone, Copy)]
182+
pub struct Shared<S> {
183+
svc: S,
184+
}
185+
186+
impl<S> Shared<S> {
187+
pub(crate) fn new(svc: S) -> Self {
188+
Self { svc }
189+
}
190+
}
191+
192+
impl<T, S> Service<T> for Shared<S>
193+
where
194+
S: Clone,
195+
{
196+
type Error = Infallible;
197+
type Response = S;
198+
type Future = SharedFuture<S>;
199+
200+
fn poll_ready(&mut self, _cx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>> {
201+
Poll::Ready(Ok(()))
202+
}
203+
204+
fn call(&mut self, _target: T) -> Self::Future {
205+
SharedFuture(futures_util::future::ok(self.svc.clone()))
206+
}
207+
}
208+
209+
/// Response future for [`Shared`].
210+
#[pin_project]
211+
#[derive(Debug)]
212+
pub struct SharedFuture<S>(#[pin] futures_util::future::Ready<Result<S, Infallible>>);
213+
214+
impl<S> Future for SharedFuture<S> {
215+
type Output = Result<S, Infallible>;
216+
217+
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
218+
self.project().0.poll(cx)
219+
}
220+
}
221+
177222
mod sealed {
178223
pub trait Sealed<X> {}
179224

src/service/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub(super) use self::http::HttpService;
4747
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
4848
pub(super) use self::make::MakeConnection;
4949
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "server"))]
50-
pub(super) use self::make::MakeServiceRef;
50+
pub(super) use self::make::{MakeServiceRef, Shared, SharedFuture};
5151
#[cfg(all(any(feature = "http1", feature = "http2"), feature = "client"))]
5252
pub(super) use self::oneshot::{oneshot, Oneshot};
5353

0 commit comments

Comments
 (0)