1mod date;
2mod options;
3mod send;
4mod upgrade;
5
6pub use options::*;
7use pin_project_lite::pin_project;
8use tokio_util::sync::CancellationToken;
9
10use std::{
11 future::Future,
12 pin::Pin,
13 sync::{atomic::AtomicBool, Arc},
14 task::{Context, Poll},
15};
16
17use bytes::Bytes;
18use http::{Request, Response};
19use http_body::{Body, Frame};
20
21use crate::{
22 early_hints::EarlyHintsReceiver,
23 h2::{
24 date::DateCache,
25 send::{PipeToSendStream, SendBuf},
26 },
27 EarlyHints, HttpProtocol, Incoming, Upgrade, Upgraded,
28};
29
30static HTTP2_INVALID_HEADERS: [http::header::HeaderName; 5] = [
31 http::header::HeaderName::from_static("keep-alive"),
32 http::header::HeaderName::from_static("proxy-connection"),
33 http::header::CONNECTION,
34 http::header::TRANSFER_ENCODING,
35 http::header::UPGRADE,
36];
37
38pub(crate) struct H2Body {
39 recv: h2::RecvStream,
40 data_done: bool,
41 send_continue_body: Option<Arc<AtomicBool>>,
42}
43
44impl H2Body {
45 #[inline]
46 fn new(recv: h2::RecvStream, send_continue_body: Option<Arc<AtomicBool>>) -> Self {
47 Self {
48 recv,
49 data_done: false,
50 send_continue_body,
51 }
52 }
53}
54
55impl Body for H2Body {
56 type Data = Bytes;
57 type Error = std::io::Error;
58
59 #[inline]
60 fn poll_frame(
61 mut self: Pin<&mut Self>,
62 cx: &mut Context<'_>,
63 ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
64 if !self.data_done {
65 match self.recv.poll_data(cx) {
66 Poll::Ready(Some(Ok(data))) => {
67 let _ = self.recv.flow_control().release_capacity(data.len());
68 return Poll::Ready(Some(Ok(Frame::data(data))));
69 }
70 Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(h2_error_to_io(err)))),
71 Poll::Ready(None) => self.data_done = true,
72 Poll::Pending => {
73 if let Some(scb) = self.send_continue_body.as_ref() {
74 scb.store(true, std::sync::atomic::Ordering::Relaxed);
75 }
76 return Poll::Pending;
77 }
78 }
79 }
80
81 match self.recv.poll_trailers(cx) {
82 Poll::Ready(Ok(Some(trailers))) => Poll::Ready(Some(Ok(Frame::trailers(trailers)))),
83 Poll::Ready(Ok(None)) => Poll::Ready(None),
84 Poll::Ready(Err(err)) => Poll::Ready(Some(Err(h2_error_to_io(err)))),
85 Poll::Pending => {
86 if let Some(scb) = self.send_continue_body.as_ref() {
87 scb.store(true, std::sync::atomic::Ordering::Relaxed);
88 }
89 Poll::Pending
90 }
91 }
92 }
93}
94
95#[inline]
96pub(super) fn h2_error_to_io(error: h2::Error) -> std::io::Error {
97 if error.is_io() {
98 error.into_io().unwrap_or(std::io::Error::other("io error"))
99 } else {
100 std::io::Error::other(error)
101 }
102}
103
104#[inline]
105pub(super) fn h2_reason_to_io(reason: h2::Reason) -> std::io::Error {
106 std::io::Error::other(h2::Error::from(reason))
107}
108
109#[inline]
110fn sanitize_response<ResB>(
111 response: &mut Response<ResB>,
112 send_date_header: bool,
113 date_cache: &DateCache,
114) where
115 ResB: Body<Data = bytes::Bytes>,
116{
117 let response_headers = response.headers_mut();
118 if send_date_header {
119 if let Some(http_date) = date_cache.get_date_header_value() {
120 response_headers
121 .entry(http::header::DATE)
122 .or_insert(http_date);
123 }
124 }
125 for header in &HTTP2_INVALID_HEADERS {
126 if let http::header::Entry::Occupied(entry) = response_headers.entry(header) {
127 entry.remove();
128 }
129 }
130 if response_headers
131 .get(http::header::TE)
132 .is_some_and(|v| v != "trailers")
133 {
134 response_headers.remove(http::header::TE);
135 }
136}
137
138struct PendingUpgrade {
139 tx: oneshot::Sender<Upgraded>,
140 upgraded: std::sync::Arc<std::sync::atomic::AtomicBool>,
141 recv_stream: h2::RecvStream,
142}
143
144pin_project! {
145 struct H2Stream<Fut, ResB>
146 where
147 Fut: Future,
148 ResB: Body<Data = bytes::Bytes>,
149 {
150 stream: h2::server::SendResponse<SendBuf<ResB::Data>>,
151 #[pin]
152 state: H2StreamState<Fut, ResB>,
153 }
154}
155
156pin_project! {
157 #[project = H2StreamStateProj]
158 enum H2StreamState<Fut, ResB>
159 where
160 Fut: Future,
161 ResB: Body<Data = bytes::Bytes>,
162 {
163 Service {
164 #[pin]
165 response_fut: Fut,
166 early_hints_rx: EarlyHintsReceiver,
167 date_cache: DateCache,
168 send_date_header: bool,
169 upgrade: Option<PendingUpgrade>,
170 send_continue: bool,
171 early_hints_open: bool,
172 send_continue_body: Option<Arc<AtomicBool>>,
173 continue_sent: bool
174 },
175 Body {
176 #[pin]
177 pipe: PipeToSendStream<ResB>,
178 },
179 }
180}
181
182impl<Fut, ResB> H2Stream<Fut, ResB>
183where
184 Fut: Future,
185 ResB: Body<Data = bytes::Bytes>,
186{
187 #[allow(clippy::too_many_arguments)]
188 #[inline]
189 const fn new(
190 stream: h2::server::SendResponse<SendBuf<ResB::Data>>,
191 response_fut: Fut,
192 early_hints_rx: EarlyHintsReceiver,
193 date_cache: DateCache,
194 send_date_header: bool,
195 upgrade: Option<PendingUpgrade>,
196 send_continue: bool,
197 send_continue_body: Option<Arc<AtomicBool>>,
198 ) -> Self {
199 Self {
200 stream,
201 state: H2StreamState::Service {
202 response_fut,
203 early_hints_rx,
204 date_cache,
205 send_date_header,
206 upgrade,
207 send_continue,
208 early_hints_open: true,
209 send_continue_body,
210 continue_sent: false,
211 },
212 }
213 }
214}
215
216impl<Fut, ResB, ResBE, ResE> Future for H2Stream<Fut, ResB>
217where
218 Fut: Future<Output = Result<Response<ResB>, ResE>>,
219 ResB: Body<Data = bytes::Bytes, Error = ResBE>,
220 ResE: std::error::Error,
221 ResBE: std::error::Error,
222{
223 type Output = ();
224
225 #[inline]
226 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
227 let mut this = self.project();
228
229 loop {
230 match this.state.as_mut().project() {
231 H2StreamStateProj::Service { .. } => {
232 match Self::poll_service(this.stream, this.state.as_mut(), cx) {
233 ServicePoll::Done => return Poll::Ready(()),
234 ServicePoll::Pending => return Poll::Pending,
235 ServicePoll::Body(pipe) => {
236 this.state.set(H2StreamState::Body { pipe });
237 continue;
238 }
239 }
240 }
241 H2StreamStateProj::Body { pipe } => return pipe.poll(cx).map(|_| ()),
242 }
243 }
244 }
245}
246
247enum ServicePoll<ResB>
249where
250 ResB: Body<Data = bytes::Bytes>,
251{
252 Done,
254 Pending,
256 Body(PipeToSendStream<ResB>),
258}
259
260impl<Fut, ResB, ResBE, ResE> H2Stream<Fut, ResB>
261where
262 Fut: Future<Output = Result<Response<ResB>, ResE>>,
263 ResB: Body<Data = bytes::Bytes, Error = ResBE>,
264 ResE: std::error::Error,
265 ResBE: std::error::Error,
266{
267 #[inline]
270 fn poll_service(
271 stream: &mut h2::server::SendResponse<SendBuf<ResB::Data>>,
272 state: Pin<&mut H2StreamState<Fut, ResB>>,
273 cx: &mut Context<'_>,
274 ) -> ServicePoll<ResB> {
275 let mut state = state;
276 loop {
277 let H2StreamStateProj::Service {
278 response_fut,
279 early_hints_rx,
280 date_cache,
281 send_date_header,
282 upgrade,
283 send_continue,
284 early_hints_open,
285 send_continue_body,
286 continue_sent,
287 } = state.as_mut().project()
288 else {
289 unreachable!("poll_service called for non-Service state");
290 };
291
292 if let Poll::Ready(response_result) = response_fut.poll(cx) {
293 let Ok(mut response) = response_result else {
294 return ServicePoll::Done;
295 };
296
297 sanitize_response(&mut response, *send_date_header, date_cache);
298
299 let response_is_end_stream = response.body().is_end_stream();
300 if !response_is_end_stream {
301 if let Some(content_length) = response.body().size_hint().exact() {
302 if !response
303 .headers()
304 .contains_key(http::header::CONTENT_LENGTH)
305 {
306 response
307 .headers_mut()
308 .insert(http::header::CONTENT_LENGTH, content_length.into());
309 }
310 }
311 }
312
313 if *send_continue && !*continue_sent {
314 if !response.status().is_client_error() && !response.status().is_server_error()
315 {
316 let mut response = Response::new(());
317 *response.status_mut() = http::StatusCode::CONTINUE;
318 let _ = stream.send_informational(response).map_err(h2_error_to_io);
319 }
320 *continue_sent = true;
321 }
322
323 let (response_parts, response_body) = response.into_parts();
324 let Ok(send) = stream.send_response(
325 Response::from_parts(response_parts, ()),
326 response_is_end_stream && upgrade.is_none(),
327 ) else {
328 return ServicePoll::Done;
329 };
330
331 if let Some(PendingUpgrade {
332 tx,
333 upgraded,
334 recv_stream,
335 }) = upgrade.take()
336 {
337 if upgraded.load(std::sync::atomic::Ordering::Relaxed) {
338 let (upgraded, task) = self::upgrade::pair(send, recv_stream);
339 let _ = tx.send(Upgraded::new(upgraded, None));
340 vibeio::spawn(task);
341 return ServicePoll::Done;
342 }
343 }
344
345 if response_is_end_stream {
346 return ServicePoll::Done;
347 }
348
349 return ServicePoll::Body(PipeToSendStream::new(send, response_body));
350 }
351
352 match stream.poll_reset(cx) {
353 Poll::Ready(Ok(_)) | Poll::Ready(Err(_)) => return ServicePoll::Done,
354 Poll::Pending => {}
355 }
356
357 if *send_continue
358 && !*continue_sent
359 && send_continue_body
360 .as_ref()
361 .is_some_and(|scb| scb.load(std::sync::atomic::Ordering::Relaxed))
362 {
363 let mut response = Response::new(());
364 *response.status_mut() = http::StatusCode::CONTINUE;
365 let _ = stream.send_informational(response).map_err(h2_error_to_io);
366 *continue_sent = true;
367 }
368
369 if *early_hints_open {
370 match early_hints_rx.poll_recv(cx) {
371 Poll::Ready(Some((headers, sender))) => {
372 let mut response = Response::new(());
373 *response.status_mut() = http::StatusCode::EARLY_HINTS;
374 *response.headers_mut() = headers;
375 sender
376 .into_inner()
377 .send(stream.send_informational(response).map_err(h2_error_to_io))
378 .ok();
379 continue;
380 }
381 Poll::Ready(None) => {
382 *early_hints_open = false;
383 continue;
384 }
385 Poll::Pending => {}
386 }
387 }
388
389 return ServicePoll::Pending;
390 }
391 }
392}
393
394pub struct Http2<Io> {
417 io_to_handshake: Option<Io>,
418 date_header_value_cached: DateCache,
419 options: Http2Options,
420 cancel_token: Option<CancellationToken>,
421}
422
423impl<Io> Http2<Io>
424where
425 Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + 'static,
426{
427 #[inline]
439 pub fn new(io: Io, options: Http2Options) -> Self {
440 Self {
441 io_to_handshake: Some(io),
442 date_header_value_cached: DateCache::default(),
443 options,
444 cancel_token: None,
445 }
446 }
447
448 #[inline]
453 pub fn graceful_shutdown_token(mut self, token: CancellationToken) -> Self {
454 self.cancel_token = Some(token);
455 self
456 }
457}
458
459impl<Io> HttpProtocol for Http2<Io>
460where
461 Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + 'static,
462{
463 #[allow(clippy::manual_async_fn)]
464 #[inline]
465 fn handle<F, Fut, ResB, ResBE, ResE>(
466 mut self,
467 request_fn: F,
468 ) -> impl std::future::Future<Output = Result<(), std::io::Error>>
469 where
470 F: Fn(Request<super::Incoming>) -> Fut + 'static,
471 Fut: std::future::Future<Output = Result<Response<ResB>, ResE>> + 'static,
472 ResB: http_body::Body<Data = bytes::Bytes, Error = ResBE> + Unpin + 'static,
473 ResE: std::error::Error,
474 ResBE: std::error::Error,
475 {
476 async move {
477 let handshake_fut = self.options.h2.handshake(
478 self.io_to_handshake
479 .take()
480 .ok_or_else(|| std::io::Error::other("no io to handshake"))?,
481 );
482 let mut h2 = (if let Some(timeout) = self.options.handshake_timeout {
483 vibeio::time::timeout(timeout, handshake_fut).await
484 } else {
485 Ok(handshake_fut.await)
486 })
487 .map_err(|_| std::io::Error::new(std::io::ErrorKind::TimedOut, "handshake timeout"))?
488 .map_err(|e| {
489 if e.is_io() {
490 e.into_io().unwrap_or(std::io::Error::other("io error"))
491 } else {
492 std::io::Error::other(e)
493 }
494 })?;
495
496 while let Some(request) = {
497 let res = {
498 let accept_fut_orig = h2.accept();
499 let accept_fut_orig_pin = std::pin::pin!(accept_fut_orig);
500 let cancel_token = self.cancel_token.clone();
501 let cancel_fut = async move {
502 if let Some(token) = cancel_token {
503 token.cancelled().await
504 } else {
505 futures_util::future::pending().await
506 }
507 };
508 let cancel_fut_pin = std::pin::pin!(cancel_fut);
509 let accept_fut =
510 futures_util::future::select(cancel_fut_pin, accept_fut_orig_pin);
511
512 match if let Some(timeout) = self.options.accept_timeout {
513 vibeio::time::timeout(timeout, accept_fut).await
514 } else {
515 Ok(accept_fut.await)
516 } {
517 Ok(futures_util::future::Either::Right((request, _))) => {
518 (Some(request), false)
519 }
520 Ok(futures_util::future::Either::Left((_, _))) => {
521 (None, true)
523 }
524 Err(_) => {
525 (None, false)
527 }
528 }
529 };
530 match res {
531 (Some(request), _) => request,
532 (None, graceful) => {
533 h2.graceful_shutdown();
534 let _ = h2.accept().await;
535 if graceful || !h2.has_streams() {
536 return Ok(());
537 }
538 return Err(std::io::Error::new(
539 std::io::ErrorKind::TimedOut,
540 "accept timeout",
541 ));
542 }
543 }
544 } {
545 let (request, stream) = match request {
546 Ok(d) => d,
547 Err(e) if e.is_go_away() => {
548 continue;
549 }
550 Err(e) if e.is_io() => {
551 let e_io = e.into_io().unwrap_or(std::io::Error::other("io error"));
552 if h2.has_streams()
553 && matches!(
554 e_io.kind(),
555 std::io::ErrorKind::BrokenPipe
556 | std::io::ErrorKind::ConnectionReset
557 | std::io::ErrorKind::ConnectionAborted
558 | std::io::ErrorKind::UnexpectedEof
559 )
560 {
561 return Ok(());
563 }
564 return Err(e_io);
565 }
566 Err(e) => {
567 return Err(std::io::Error::other(e));
568 }
569 };
570
571 let is_100_continue = self.options.send_continue_response
573 && request
574 .headers()
575 .get(http::header::EXPECT)
576 .and_then(|v| v.to_str().ok())
577 .is_some_and(|v| v.eq_ignore_ascii_case("100-continue"));
578
579 let date_cache = self.date_header_value_cached.clone();
580 let send_continue_body = is_100_continue.then(|| Arc::new(AtomicBool::new(false)));
581 let (request_parts, recv_stream) = request.into_parts();
582 let (request_body, upgrade) = if request_parts.method == http::Method::CONNECT {
583 (Incoming::Empty, Some(recv_stream))
584 } else {
585 (
586 Incoming::H2(H2Body::new(recv_stream, send_continue_body.clone())),
587 None,
588 )
589 };
590 let mut request = Request::from_parts(request_parts, request_body);
591
592 let (early_hints, early_hints_rx) = EarlyHints::new_lazy();
594 request.extensions_mut().insert(early_hints);
595
596 let upgrade = if let Some(recv_stream) = upgrade {
598 let (upgrade_tx, upgrade_rx) = oneshot::async_channel();
599 let upgrade = Upgrade::new(upgrade_rx);
600 let upgraded = upgrade.upgraded.clone();
601 request.extensions_mut().insert(upgrade);
602 Some(PendingUpgrade {
603 tx: upgrade_tx,
604 upgraded,
605 recv_stream,
606 })
607 } else {
608 None
609 };
610
611 vibeio::spawn(H2Stream::new(
612 stream,
613 request_fn(request),
614 early_hints_rx,
615 date_cache,
616 self.options.send_date_header,
617 upgrade,
618 is_100_continue,
619 send_continue_body,
620 ));
621 }
622
623 Ok(())
624 }
625 }
626}