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
use crate::{Version, h2::H2Connection, h3::H3Connection};
use std::sync::Arc;
/// The protocol-version-specific session state attached to a single request/response cycle.
///
/// HTTP/1 conns carry no session state (the underlying transport _is_ the session). HTTP/2
/// and HTTP/3 conns each multiplex many requests over a shared connection-level driver
/// (`H2Connection` / `H3Connection`) and identify their own request via a stream id.
/// Holding these as a single enum on `Conn` / `Upgrade` / `ReceivedBody` rather than four
/// parallel `Option<…>` fields enforces the "set together" invariant at the type level.
#[derive(Debug, Clone, fieldwork::Fieldwork)]
#[fieldwork(get, deref = false)]
pub enum ProtocolSession {
/// HTTP/0.9, HTTP/1.0, or HTTP/1.1. The transport itself is the session.
Http1,
/// HTTP/2. The (shared) connection driver and the stream id this request rides on.
Http2 {
/// the [`H2Connection`] driver shared across every request on the wire
#[field = "h2_connection"]
connection: Arc<H2Connection>,
/// 31-bit stream id per RFC 9113 §5.1.1
#[field = "h2_stream_id"]
stream_id: u32,
},
/// HTTP/3. The (shared) connection driver and the stream id this request rides on.
Http3 {
/// the [`H3Connection`] driver shared across every request on the wire
#[field = "h3_connection"]
connection: Arc<H3Connection>,
/// QUIC varint stream id per RFC 9000 §2.1
#[field = "h3_stream_id"]
stream_id: u64,
},
}
impl ProtocolSession {
/// The HTTP version implied by this session. Note: synthetic conns and h1 conns both
/// return [`Version::Http1_1`]; the more specific h1 sub-version (`Http0_9` / `Http1_0`)
/// lives on the `Conn::version` field, which is independently tracked.
#[must_use]
#[allow(dead_code)]
pub fn http_version(&self) -> Version {
match self {
Self::Http1 => Version::Http1_1,
Self::Http2 { .. } => Version::Http2,
Self::Http3 { .. } => Version::Http3,
}
}
/// The h2 driver and stream id, if this session is HTTP/2. The driver is
/// returned by clone (cheap; it's an [`Arc`]) so callers can move it into
/// async work without juggling lifetimes against `&self`. Use
/// [`Self::as_h2_borrowed`] to avoid the clone when borrowing suffices.
#[must_use]
pub fn as_h2(&self) -> Option<(Arc<H2Connection>, u32)> {
self.as_h2_borrowed()
.map(|(connection, stream_id)| (connection.clone(), stream_id))
}
/// The h2 driver (by reference) and stream id, if this session is HTTP/2.
#[must_use]
pub fn as_h2_borrowed(&self) -> Option<(&Arc<H2Connection>, u32)> {
match self {
Self::Http2 {
connection,
stream_id,
} => Some((connection, *stream_id)),
_ => None,
}
}
/// The h3 driver and stream id, if this session is HTTP/3. The driver is
/// returned by clone (cheap; it's an [`Arc`]) so callers can move it into
/// async work without juggling lifetimes against `&self`. Use
/// [`Self::as_h3_borrowed`] to avoid the clone when borrowing suffices.
#[must_use]
pub fn as_h3(&self) -> Option<(Arc<H3Connection>, u64)> {
self.as_h3_borrowed()
.map(|(connection, stream_id)| (connection.clone(), stream_id))
}
/// The h3 driver (by reference) and stream id, if this session is HTTP/3.
#[must_use]
pub fn as_h3_borrowed(&self) -> Option<(&Arc<H3Connection>, u64)> {
match self {
Self::Http3 {
connection,
stream_id,
} => Some((connection, *stream_id)),
_ => None,
}
}
}