-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathhttp_version.rs
More file actions
129 lines (110 loc) · 3.74 KB
/
http_version.rs
File metadata and controls
129 lines (110 loc) · 3.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! HTTP Version support and negotiation
use hyper::Response;
use hyper_util::rt::TokioIo;
use std::pin::Pin;
use std::task::{Context, Poll};
pub const ALPN_H2: &[u8] = b"h2";
pub const ALPN_HTTP11: &[u8] = b"http/1.1";
/// Supported HTTP versions
#[derive(Debug)]
pub enum HttpVersion {
/// HTTP 1.1
Http1,
/// HTTP 2
Http2,
}
impl HttpVersion {
/// Given a server TLS stream, choose an HTTP version to use
pub fn from_negotiated_protocol_server<IO>(tls: &tokio_rustls::server::TlsStream<IO>) -> Self {
let (_io, conn) = tls.get_ref();
let negotiated_alpn = conn.alpn_protocol();
let chosen_protocol = Self::from_alpn_bytes(negotiated_alpn);
tracing::debug!(
"[server] Negotiated ALPN {:?}, chosen protocol {chosen_protocol:?}",
negotiated_alpn.map(String::from_utf8_lossy)
);
chosen_protocol
}
/// Given a client TLS stream, choose an HTTP version to use
pub fn from_negotiated_protocol_client<IO>(tls: &tokio_rustls::client::TlsStream<IO>) -> Self {
let (_io, conn) = tls.get_ref();
let negotiated_alpn = conn.alpn_protocol();
let chosen_protocol = Self::from_alpn_bytes(negotiated_alpn);
tracing::debug!(
"[client] Negotiated ALPN {:?}, chosen protocol {chosen_protocol:?}",
negotiated_alpn.map(String::from_utf8_lossy)
);
chosen_protocol
}
fn from_alpn_bytes(chosen_protocol: Option<&[u8]>) -> Self {
match chosen_protocol {
Some(p) if p.ends_with(ALPN_H2) => HttpVersion::Http2,
Some(p) if p.ends_with(ALPN_HTTP11) => HttpVersion::Http1,
_ => HttpVersion::Http1,
}
}
}
type Http1Sender = hyper::client::conn::http1::SendRequest<hyper::body::Incoming>;
type Http2Sender = hyper::client::conn::http2::SendRequest<hyper::body::Incoming>;
type Http1Connection = hyper::client::conn::http1::Connection<
TokioIo<tokio_rustls::client::TlsStream<tokio::net::TcpStream>>,
hyper::body::Incoming,
>;
type Http2Connection = hyper::client::conn::http2::Connection<
TokioIo<tokio_rustls::client::TlsStream<tokio::net::TcpStream>>,
hyper::body::Incoming,
crate::TokioExecutor,
>;
/// A protocol version agnostic HTTP sender
pub enum HttpSender {
Http1(Http1Sender),
Http2(Http2Sender),
}
impl From<Http1Sender> for HttpSender {
fn from(inner: Http1Sender) -> Self {
Self::Http1(inner)
}
}
impl From<Http2Sender> for HttpSender {
fn from(inner: Http2Sender) -> Self {
Self::Http2(inner)
}
}
impl HttpSender {
pub async fn send_request(
&mut self,
request: http::Request<hyper::body::Incoming>,
) -> Result<Response<hyper::body::Incoming>, hyper::Error> {
match self {
Self::Http1(sender) => sender.send_request(request).await,
Self::Http2(sender) => sender.send_request(request).await,
}
}
}
pin_project_lite::pin_project! {
/// A protocol version agnostic HTTP connection
#[project = HttpConnectionProj]
pub enum HttpConnection {
Http1 { #[pin] inner: Http1Connection },
Http2 { #[pin] inner: Http2Connection },
}
}
impl From<Http1Connection> for HttpConnection {
fn from(inner: Http1Connection) -> Self {
Self::Http1 { inner }
}
}
impl From<Http2Connection> for HttpConnection {
fn from(inner: Http2Connection) -> Self {
Self::Http2 { inner }
}
}
impl Future for HttpConnection {
type Output = Result<(), hyper::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.project() {
HttpConnectionProj::Http1 { inner } => inner.poll(cx),
HttpConnectionProj::Http2 { inner } => inner.poll(cx),
}
}
}