trillium/upgrade.rs
1use crate::{Headers, HttpContext, Method, Transport, TypeSet, Version};
2use futures_lite::{AsyncRead, AsyncWrite};
3use std::{mem, net::IpAddr, sync::Arc};
4use trillium_http::Swansong;
5use trillium_macros::{AsyncRead, AsyncWrite};
6
7/// # A HTTP protocol upgrade
8#[derive(Debug, AsyncWrite, AsyncRead)]
9pub struct Upgrade(trillium_http::Upgrade<Box<dyn Transport>>);
10
11impl<T: Transport + 'static> From<trillium_http::Upgrade<T>> for Upgrade {
12 fn from(value: trillium_http::Upgrade<T>) -> Self {
13 Self(value.map_transport(|t| Box::new(t) as Box<dyn Transport>))
14 }
15}
16
17impl<T: Transport + 'static> From<trillium_http::Conn<T>> for Upgrade {
18 fn from(value: trillium_http::Conn<T>) -> Self {
19 trillium_http::Upgrade::from(value).into()
20 }
21}
22
23impl From<crate::Conn> for Upgrade {
24 fn from(value: crate::Conn) -> Self {
25 Self(value.inner.into())
26 }
27}
28
29impl AsRef<trillium_http::Upgrade<Box<dyn Transport>>> for Upgrade {
30 fn as_ref(&self) -> &trillium_http::Upgrade<Box<dyn Transport>> {
31 &self.0
32 }
33}
34
35impl AsMut<trillium_http::Upgrade<Box<dyn Transport>>> for Upgrade {
36 fn as_mut(&mut self) -> &mut trillium_http::Upgrade<Box<dyn Transport>> {
37 &mut self.0
38 }
39}
40
41impl Upgrade {
42 /// Borrows the HTTP request headers
43 pub fn request_headers(&self) -> &Headers {
44 self.0.received_headers()
45 }
46
47 /// Take the HTTP request headers
48 pub fn take_request_headers(&mut self) -> Headers {
49 mem::take(self.0.received_headers_mut())
50 }
51
52 /// Returns a copy of the HTTP request method
53 pub fn method(&self) -> Method {
54 self.0.method()
55 }
56
57 /// Borrows the state accumulated on the Conn before negotiating the upgrade
58 pub fn state(&self) -> &TypeSet {
59 self.0.state()
60 }
61
62 /// Takes the [`TypeSet`] accumulated on the Conn before negotiating the upgrade
63 pub fn take_state(&mut self) -> TypeSet {
64 mem::take(self.0.state_mut())
65 }
66
67 /// Mutably borrow the [`TypeSet`] accumulated on the Conn before negotiating the upgrade
68 pub fn state_mut(&mut self) -> &mut TypeSet {
69 self.0.state_mut()
70 }
71
72 /// Borrows the underlying transport
73 pub fn transport(&self) -> &dyn Transport {
74 self.0.transport().as_ref()
75 }
76
77 /// Mutably borrow the underlying transport
78 ///
79 /// This returns a tuple of (buffered bytes, transport) in order to make salient the requirement
80 /// to handle any buffered bytes before using the transport directly.
81 pub fn transport_mut(&mut self) -> (&[u8], &mut dyn Transport) {
82 let (buffer, transport) = self.0.buffer_and_transport_mut();
83 (&*buffer, &mut **transport)
84 }
85
86 /// Consumes self, returning the underlying transport
87 ///
88 /// This returns a tuple of (buffered bytes, transport) in order to make salient the requirement
89 /// to handle any buffered bytes before using the transport directly.
90 pub fn into_transport(mut self) -> (Vec<u8>, Box<dyn Transport>) {
91 let buffer = self.0.take_buffer();
92 (buffer, self.0.into_transport())
93 }
94
95 /// Returns a copy of the peer IP address of the connection, if available
96 pub fn peer_ip(&self) -> Option<IpAddr> {
97 self.0.peer_ip()
98 }
99
100 /// Borrows the :authority HTTP/3 pseudo-header
101 pub fn authority(&self) -> Option<&str> {
102 self.0.authority()
103 }
104
105 /// Borrows the :scheme HTTP/3 pseudo-header
106 pub fn scheme(&self) -> Option<&str> {
107 self.0.scheme()
108 }
109
110 /// Borrows the :protocol HTTP/3 pseudo-header
111 pub fn protocol(&self) -> Option<&str> {
112 self.0.protocol()
113 }
114
115 /// Borrows the HTTP version
116 pub fn http_version(&self) -> &Version {
117 self.0.http_version()
118 }
119
120 /// Returns a copy of whether this connection was deemed secure by the handler stack
121 pub fn is_secure(&self) -> bool {
122 self.0.is_secure()
123 }
124
125 /// Borrows the shared state [`TypeSet`] for this application
126 pub fn shared_state(&self) -> &TypeSet {
127 self.0.shared_state()
128 }
129
130 /// Returns the HTTP request path up to but excluding any query component
131 pub fn path(&self) -> &str {
132 self.0.path()
133 }
134
135 /// Retrieves the query component of the path
136 pub fn querystring(&self) -> &str {
137 self.0.querystring()
138 }
139
140 /// Retrieves a cloned [`Swansong`] graceful shutdown controller
141 pub fn swansong(&self) -> Swansong {
142 self.0.context().swansong().clone()
143 }
144
145 /// Retrieves a clone of the [`HttpContext`] for this upgrade
146 pub fn context(&self) -> Arc<HttpContext> {
147 self.0.context().clone()
148 }
149
150 /// Returns a clone of the H3 connection, if any
151 pub fn h3_connection(&self) -> Option<Arc<trillium_http::h3::H3Connection>> {
152 self.0.h3_connection().cloned()
153 }
154
155 /// Inbound trailers, populated conditionally when we have read this upgrade to completion
156 pub fn request_trailers(&self) -> Option<&Headers> {
157 self.0.received_trailers()
158 }
159
160 /// Emit trailing headers and finish the outbound stream. Consumes `self`; further
161 /// writes are statically prevented.
162 ///
163 /// Per-protocol behavior:
164 /// - HTTP/1.1 with `Transfer-Encoding: chunked`: writes the last-chunk marker (`0\r\n`), the
165 /// trailer section, and a final CRLF, then closes the transport.
166 /// - HTTP/2: enqueues a trailing `HEADERS` frame with `END_STREAM` via the connection driver
167 /// and returns. The driver finishes the stream after draining any pending DATA frames.
168 /// - HTTP/3: encodes a trailing `HEADERS` frame via QPACK, writes it to the stream, then closes
169 /// the stream (QUIC `FIN`).
170 /// - HTTP/1.1 without chunked encoding (raw upgrade, CONNECT tunnel, websocket-over-h1):
171 /// trailers can't be expressed on the wire; dropped with a `log::warn!` and `Ok(())`
172 /// returned.
173 ///
174 /// # Errors
175 ///
176 /// Returns the underlying [`std::io::Error`] when the wire write fails, `BrokenPipe` if
177 /// the stream has already been closed, and `NotConnected` if the carried
178 /// `ProtocolSession` is missing the expected driver for h2/h3.
179 pub async fn send_trailers(self, trailers: Headers) -> std::io::Result<()> {
180 self.0.send_trailers(trailers).await
181 }
182}