feat(server): allow !Send Servers

Until this commit, servers have required that `Service` and their
`Future` to be `Send`, since the server needs to spawn some internal
tasks to an executor, and by default, that is `tokio::spawn`, which
could be spawning to a threadpool. This was true even if the user were
certain there was no threadpool involved, and was instead using a
different single-threaded runtime, like
`tokio::runtime::current_thread`.

This changes makes all the server pieces generic over an `E`, which is
essentially `Executor<PrivateTypes<Server::Future>>`. There's a new set
of internal traits, `H2Exec` and `NewSvcExec`, which allow for the type
signature to only show the generics that the user is providing. The
traits cannot be implemented explicitly, but there are blanket
implementations for `E: Executor<SpecificType>`. If the user provides
their own executor, it simply needs to have a generic `impl<F>
Executor<F> for MyExec`. That impl can have bounds deciding whether to
require `F: Send`. If the executor does require `Send`, and the
`Service` futures are `!Send`, there will be compiler errors.

To prevent a breaking change, all the types that gained the `E` generic
have a default type set, which is the original `tokio::spawn` executor.
This commit is contained in:
Sean McArthur
2018-10-16 12:42:24 -07:00
parent 00c96de0b9
commit ced949cb6b
11 changed files with 426 additions and 133 deletions

View File

@@ -10,7 +10,7 @@ use http::HeaderMap;
use body::Payload;
mod client;
mod server;
pub(crate) mod server;
pub(crate) use self::client::Client;
pub(crate) use self::server::Server;

View File

@@ -5,7 +5,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
use ::headers::content_length_parse_all;
use ::body::Payload;
use ::common::Exec;
use ::common::exec::H2Exec;
use ::headers;
use ::service::Service;
use ::proto::Dispatched;
@@ -13,12 +13,12 @@ use super::{PipeToSendStream, SendBuf};
use ::{Body, Response};
pub(crate) struct Server<T, S, B>
pub(crate) struct Server<T, S, B, E>
where
S: Service,
B: Payload,
{
exec: Exec,
exec: E,
service: S,
state: State<T, B>,
}
@@ -40,15 +40,16 @@ where
}
impl<T, S, B> Server<T, S, B>
impl<T, S, B, E> Server<T, S, B, E>
where
T: AsyncRead + AsyncWrite,
S: Service<ReqBody=Body, ResBody=B>,
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
S::Future: Send + 'static,
//S::Future: Send + 'static,
B: Payload,
E: H2Exec<S::Future, B>,
{
pub(crate) fn new(io: T, service: S, exec: Exec) -> Server<T, S, B> {
pub(crate) fn new(io: T, service: S, exec: E) -> Server<T, S, B, E> {
let handshake = Builder::new()
.handshake(io);
Server {
@@ -76,13 +77,14 @@ where
}
}
impl<T, S, B> Future for Server<T, S, B>
impl<T, S, B, E> Future for Server<T, S, B, E>
where
T: AsyncRead + AsyncWrite,
S: Service<ReqBody=Body, ResBody=B>,
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
S::Future: Send + 'static,
//S::Future: Send + 'static,
B: Payload,
E: H2Exec<S::Future, B>,
{
type Item = Dispatched;
type Error = ::Error;
@@ -116,14 +118,14 @@ where
T: AsyncRead + AsyncWrite,
B: Payload,
{
fn poll_server<S>(&mut self, service: &mut S, exec: &Exec) -> Poll<(), ::Error>
fn poll_server<S, E>(&mut self, service: &mut S, exec: &E) -> Poll<(), ::Error>
where
S: Service<
ReqBody=Body,
ResBody=B,
>,
S::Error: Into<Box<::std::error::Error + Send + Sync>>,
S::Future: Send + 'static,
E: H2Exec<S::Future, B>,
{
while let Some((req, respond)) = try_ready!(self.conn.poll().map_err(::Error::new_h2)) {
trace!("incoming request");
@@ -132,7 +134,7 @@ where
::Body::h2(stream, content_length)
});
let fut = H2Stream::new(service.call(req), respond);
exec.execute(fut)?;
exec.execute_h2stream(fut)?;
}
// no more incoming streams...
@@ -141,7 +143,8 @@ where
}
}
struct H2Stream<F, B>
#[allow(missing_debug_implementations)]
pub struct H2Stream<F, B>
where
B: Payload,
{