Skip to content

Commit 653215e

Browse files
committed
feat: allow enabling http1/http2 individually
1 parent 43ddd6d commit 653215e

File tree

4 files changed

+129
-16
lines changed

4 files changed

+129
-16
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ authors = ["Sean McArthur <sean@seanmonstar.com>"]
1111
keywords = ["http", "hyper", "hyperium"]
1212
categories = ["network-programming", "web-programming::http-client", "web-programming::http-server"]
1313
edition = "2018"
14+
resolver = "2"
1415

1516
[package.metadata.docs.rs]
1617
features = ["full"]

src/common/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![allow(missing_docs)]
22

3+
#[allow(unused)]
34
macro_rules! ready {
45
($e:expr) => {
56
match $e {
@@ -9,6 +10,7 @@ macro_rules! ready {
910
};
1011
}
1112

13+
#[allow(unused)]
1214
pub(crate) use ready;
1315
pub(crate) mod exec;
1416
#[cfg(feature = "client")]

src/server/conn/auto.rs

Lines changed: 125 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,52 @@ use http::{Request, Response};
1515
use http_body::Body;
1616
use hyper::{
1717
body::Incoming,
18-
rt::{bounds::Http2ServerConnExec, Read, ReadBuf, Timer, Write},
19-
server::conn::{http1, http2},
18+
rt::{Read, ReadBuf, Timer, Write},
2019
service::Service,
2120
};
21+
22+
#[cfg(feature = "http1")]
23+
use hyper::server::conn::http1;
24+
25+
#[cfg(feature = "http2")]
26+
use hyper::{rt::bounds::Http2ServerConnExec, server::conn::http2};
27+
28+
#[cfg(any(not(feature = "http2"), not(feature = "http1")))]
29+
use std::marker::PhantomData;
30+
2231
use pin_project_lite::pin_project;
2332

2433
use crate::common::rewind::Rewind;
2534

26-
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
35+
type Error = Box<dyn std::error::Error + Send + Sync>;
36+
37+
type Result<T> = std::result::Result<T, Error>;
2738

2839
const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
2940

41+
/// Exactly equivalent to [`Http2ServerConnExec`].
42+
#[cfg(feature = "http2")]
43+
pub trait HttpServerConnExec<A, B: Body>: Http2ServerConnExec<A, B> {}
44+
45+
#[cfg(feature = "http2")]
46+
impl<A, B: Body, T: Http2ServerConnExec<A, B>> HttpServerConnExec<A, B> for T {}
47+
48+
/// Exactly equivalent to [`Http2ServerConnExec`].
49+
#[cfg(not(feature = "http2"))]
50+
pub trait HttpServerConnExec<A, B: Body> {}
51+
52+
#[cfg(not(feature = "http2"))]
53+
impl<A, B: Body, T> HttpServerConnExec<A, B> for T {}
54+
3055
/// Http1 or Http2 connection builder.
3156
#[derive(Clone, Debug)]
3257
pub struct Builder<E> {
58+
#[cfg(feature = "http1")]
3359
http1: http1::Builder,
60+
#[cfg(feature = "http2")]
3461
http2: http2::Builder<E>,
62+
#[cfg(not(feature = "http2"))]
63+
_executor: E,
3564
}
3665

3766
impl<E> Builder<E> {
@@ -52,17 +81,23 @@ impl<E> Builder<E> {
5281
/// ```
5382
pub fn new(executor: E) -> Self {
5483
Self {
84+
#[cfg(feature = "http1")]
5585
http1: http1::Builder::new(),
86+
#[cfg(feature = "http2")]
5687
http2: http2::Builder::new(executor),
88+
#[cfg(not(feature = "http2"))]
89+
_executor: executor,
5790
}
5891
}
5992

6093
/// Http1 configuration.
94+
#[cfg(feature = "http1")]
6195
pub fn http1(&mut self) -> Http1Builder<'_, E> {
6296
Http1Builder { inner: self }
6397
}
6498

6599
/// Http2 configuration.
100+
#[cfg(feature = "http2")]
66101
pub fn http2(&mut self) -> Http2Builder<'_, E> {
67102
Http2Builder { inner: self }
68103
}
@@ -76,7 +111,7 @@ impl<E> Builder<E> {
76111
B: Body + 'static,
77112
B::Error: Into<Box<dyn StdError + Send + Sync>>,
78113
I: Read + Write + Unpin + 'static,
79-
E: Http2ServerConnExec<S::Future, B>,
114+
E: HttpServerConnExec<S::Future, B>,
80115
{
81116
Connection {
82117
state: ConnState::ReadVersion {
@@ -102,7 +137,7 @@ impl<E> Builder<E> {
102137
B: Body + 'static,
103138
B::Error: Into<Box<dyn StdError + Send + Sync>>,
104139
I: Read + Write + Unpin + Send + 'static,
105-
E: Http2ServerConnExec<S::Future, B>,
140+
E: HttpServerConnExec<S::Future, B>,
106141
{
107142
UpgradeableConnection {
108143
state: UpgradeableConnState::ReadVersion {
@@ -113,12 +148,24 @@ impl<E> Builder<E> {
113148
}
114149
}
115150
}
151+
116152
#[derive(Copy, Clone)]
117153
enum Version {
118154
H1,
119155
H2,
120156
}
121157

158+
impl Version {
159+
#[must_use]
160+
#[cfg(any(not(feature = "http2"), not(feature = "http1")))]
161+
pub fn unsupported(self) -> Error {
162+
match self {
163+
Version::H1 => Error::from("HTTP/1 is not supported"),
164+
Version::H2 => Error::from("HTTP/2 is not supported"),
165+
}
166+
}
167+
}
168+
122169
fn read_version<I>(io: I) -> ReadVersion<I>
123170
where
124171
I: Read + Unpin,
@@ -202,6 +249,18 @@ pin_project! {
202249
}
203250
}
204251

252+
#[cfg(feature = "http1")]
253+
type Http1Connection<I, S> = hyper::server::conn::http1::Connection<Rewind<I>, S>;
254+
255+
#[cfg(not(feature = "http1"))]
256+
type Http1Connection<I, S> = (PhantomData<I>, PhantomData<S>);
257+
258+
#[cfg(feature = "http2")]
259+
type Http2Connection<I, S, E> = hyper::server::conn::http2::Connection<Rewind<I>, S, E>;
260+
261+
#[cfg(not(feature = "http2"))]
262+
type Http2Connection<I, S, E> = (PhantomData<I>, PhantomData<S>, PhantomData<E>);
263+
205264
pin_project! {
206265
#[project = ConnStateProj]
207266
enum ConnState<'a, I, S, E>
@@ -216,11 +275,11 @@ pin_project! {
216275
},
217276
H1 {
218277
#[pin]
219-
conn: hyper::server::conn::http1::Connection<Rewind<I>, S>,
278+
conn: Http1Connection<I, S>,
220279
},
221280
H2 {
222281
#[pin]
223-
conn: hyper::server::conn::http2::Connection<Rewind<I>, S, E>,
282+
conn: Http2Connection<I, S, E>,
224283
},
225284
}
226285
}
@@ -232,7 +291,7 @@ where
232291
I: Read + Write + Unpin,
233292
B: Body + 'static,
234293
B::Error: Into<Box<dyn StdError + Send + Sync>>,
235-
E: Http2ServerConnExec<S::Future, B>,
294+
E: HttpServerConnExec<S::Future, B>,
236295
{
237296
/// Start a graceful shutdown process for this connection.
238297
///
@@ -245,8 +304,12 @@ where
245304
pub fn graceful_shutdown(self: Pin<&mut Self>) {
246305
match self.project().state.project() {
247306
ConnStateProj::ReadVersion { .. } => {}
307+
#[cfg(feature = "http1")]
248308
ConnStateProj::H1 { conn } => conn.graceful_shutdown(),
309+
#[cfg(feature = "http2")]
249310
ConnStateProj::H2 { conn } => conn.graceful_shutdown(),
311+
#[cfg(any(not(feature = "http1"), not(feature = "http2")))]
312+
_ => unreachable!(),
250313
}
251314
}
252315
}
@@ -259,7 +322,7 @@ where
259322
B: Body + 'static,
260323
B::Error: Into<Box<dyn StdError + Send + Sync>>,
261324
I: Read + Write + Unpin + 'static,
262-
E: Http2ServerConnExec<S::Future, B>,
325+
E: HttpServerConnExec<S::Future, B>,
263326
{
264327
type Output = Result<()>;
265328

@@ -276,22 +339,30 @@ where
276339
let (version, io) = ready!(read_version.poll(cx))?;
277340
let service = service.take().unwrap();
278341
match version {
342+
#[cfg(feature = "http1")]
279343
Version::H1 => {
280344
let conn = builder.http1.serve_connection(io, service);
281345
this.state.set(ConnState::H1 { conn });
282346
}
347+
#[cfg(feature = "http2")]
283348
Version::H2 => {
284349
let conn = builder.http2.serve_connection(io, service);
285350
this.state.set(ConnState::H2 { conn });
286351
}
352+
#[cfg(any(not(feature = "http1"), not(feature = "http2")))]
353+
_ => return Poll::Ready(Err(version.unsupported())),
287354
}
288355
}
356+
#[cfg(feature = "http1")]
289357
ConnStateProj::H1 { conn } => {
290358
return conn.poll(cx).map_err(Into::into);
291359
}
360+
#[cfg(feature = "http2")]
292361
ConnStateProj::H2 { conn } => {
293362
return conn.poll(cx).map_err(Into::into);
294363
}
364+
#[cfg(any(not(feature = "http1"), not(feature = "http2")))]
365+
_ => unreachable!(),
295366
}
296367
}
297368
}
@@ -308,6 +379,12 @@ pin_project! {
308379
}
309380
}
310381

382+
#[cfg(feature = "http1")]
383+
type Http1UpgradeableConnection<I, S> = hyper::server::conn::http1::UpgradeableConnection<I, S>;
384+
385+
#[cfg(not(feature = "http1"))]
386+
type Http1UpgradeableConnection<I, S> = (PhantomData<I>, PhantomData<S>);
387+
311388
pin_project! {
312389
#[project = UpgradeableConnStateProj]
313390
enum UpgradeableConnState<'a, I, S, E>
@@ -322,11 +399,11 @@ pin_project! {
322399
},
323400
H1 {
324401
#[pin]
325-
conn: hyper::server::conn::http1::UpgradeableConnection<Rewind<I>, S>,
402+
conn: Http1UpgradeableConnection<Rewind<I>, S>,
326403
},
327404
H2 {
328405
#[pin]
329-
conn: hyper::server::conn::http2::Connection<Rewind<I>, S, E>,
406+
conn: Http2Connection<I, S, E>,
330407
},
331408
}
332409
}
@@ -338,7 +415,7 @@ where
338415
I: Read + Write + Unpin,
339416
B: Body + 'static,
340417
B::Error: Into<Box<dyn StdError + Send + Sync>>,
341-
E: Http2ServerConnExec<S::Future, B>,
418+
E: HttpServerConnExec<S::Future, B>,
342419
{
343420
/// Start a graceful shutdown process for this connection.
344421
///
@@ -351,8 +428,12 @@ where
351428
pub fn graceful_shutdown(self: Pin<&mut Self>) {
352429
match self.project().state.project() {
353430
UpgradeableConnStateProj::ReadVersion { .. } => {}
431+
#[cfg(feature = "http1")]
354432
UpgradeableConnStateProj::H1 { conn } => conn.graceful_shutdown(),
433+
#[cfg(feature = "http2")]
355434
UpgradeableConnStateProj::H2 { conn } => conn.graceful_shutdown(),
435+
#[cfg(any(not(feature = "http1"), not(feature = "http2")))]
436+
_ => unreachable!(),
356437
}
357438
}
358439
}
@@ -365,7 +446,7 @@ where
365446
B: Body + 'static,
366447
B::Error: Into<Box<dyn StdError + Send + Sync>>,
367448
I: Read + Write + Unpin + Send + 'static,
368-
E: Http2ServerConnExec<S::Future, B>,
449+
E: HttpServerConnExec<S::Future, B>,
369450
{
370451
type Output = Result<()>;
371452

@@ -382,34 +463,45 @@ where
382463
let (version, io) = ready!(read_version.poll(cx))?;
383464
let service = service.take().unwrap();
384465
match version {
466+
#[cfg(feature = "http1")]
385467
Version::H1 => {
386468
let conn = builder.http1.serve_connection(io, service).with_upgrades();
387469
this.state.set(UpgradeableConnState::H1 { conn });
388470
}
471+
#[cfg(feature = "http2")]
389472
Version::H2 => {
390473
let conn = builder.http2.serve_connection(io, service);
391474
this.state.set(UpgradeableConnState::H2 { conn });
392475
}
476+
#[cfg(any(not(feature = "http1"), not(feature = "http2")))]
477+
_ => return Poll::Ready(Err(version.unsupported())),
393478
}
394479
}
480+
#[cfg(feature = "http1")]
395481
UpgradeableConnStateProj::H1 { conn } => {
396482
return conn.poll(cx).map_err(Into::into);
397483
}
484+
#[cfg(feature = "http2")]
398485
UpgradeableConnStateProj::H2 { conn } => {
399486
return conn.poll(cx).map_err(Into::into);
400487
}
488+
#[cfg(any(not(feature = "http1"), not(feature = "http2")))]
489+
_ => unreachable!(),
401490
}
402491
}
403492
}
404493
}
405494

406495
/// Http1 part of builder.
496+
#[cfg(feature = "http1")]
407497
pub struct Http1Builder<'a, E> {
408498
inner: &'a mut Builder<E>,
409499
}
410500

501+
#[cfg(feature = "http1")]
411502
impl<E> Http1Builder<'_, E> {
412503
/// Http2 configuration.
504+
#[cfg(feature = "http2")]
413505
pub fn http2(&mut self) -> Http2Builder<'_, E> {
414506
Http2Builder {
415507
inner: &mut self.inner,
@@ -524,6 +616,22 @@ impl<E> Http1Builder<'_, E> {
524616
}
525617

526618
/// Bind a connection together with a [`Service`].
619+
#[cfg(feature = "http2")]
620+
pub async fn serve_connection<I, S, B>(&self, io: I, service: S) -> Result<()>
621+
where
622+
S: Service<Request<Incoming>, Response = Response<B>>,
623+
S::Future: 'static,
624+
S::Error: Into<Box<dyn StdError + Send + Sync>>,
625+
B: Body + 'static,
626+
B::Error: Into<Box<dyn StdError + Send + Sync>>,
627+
I: Read + Write + Unpin + 'static,
628+
E: HttpServerConnExec<S::Future, B>,
629+
{
630+
self.inner.serve_connection(io, service).await
631+
}
632+
633+
/// Bind a connection together with a [`Service`].
634+
#[cfg(not(feature = "http2"))]
527635
pub async fn serve_connection<I, S, B>(&self, io: I, service: S) -> Result<()>
528636
where
529637
S: Service<Request<Incoming>, Response = Response<B>>,
@@ -532,18 +640,20 @@ impl<E> Http1Builder<'_, E> {
532640
B: Body + 'static,
533641
B::Error: Into<Box<dyn StdError + Send + Sync>>,
534642
I: Read + Write + Unpin + 'static,
535-
E: Http2ServerConnExec<S::Future, B>,
536643
{
537644
self.inner.serve_connection(io, service).await
538645
}
539646
}
540647

541648
/// Http2 part of builder.
649+
#[cfg(feature = "http2")]
542650
pub struct Http2Builder<'a, E> {
543651
inner: &'a mut Builder<E>,
544652
}
545653

654+
#[cfg(feature = "http2")]
546655
impl<E> Http2Builder<'_, E> {
656+
#[cfg(feature = "http1")]
547657
/// Http1 configuration.
548658
pub fn http1(&mut self) -> Http1Builder<'_, E> {
549659
Http1Builder {
@@ -679,7 +789,7 @@ impl<E> Http2Builder<'_, E> {
679789
B: Body + 'static,
680790
B::Error: Into<Box<dyn StdError + Send + Sync>>,
681791
I: Read + Write + Unpin + 'static,
682-
E: Http2ServerConnExec<S::Future, B>,
792+
E: HttpServerConnExec<S::Future, B>,
683793
{
684794
self.inner.serve_connection(io, service).await
685795
}

0 commit comments

Comments
 (0)