1use crate::{
2 Buffer, Conn, Headers, HttpContext, KnownHeaderName, Method, PeerGone, ProtocolSession,
3 ReceivedBody, Status, TypeSet, Version,
4 conn::ConnInner,
5 h2::H2Connection,
6 h3::{Frame, H3Connection},
7 headers::qpack::{FieldSection, PseudoHeaders},
8 received_body::{H3TrailerFuture, ReceivedBodyState, write_chunk},
9 util::encoding,
10};
11use encoding_rs::Encoding;
12use fieldwork::Fieldwork;
13use futures_lite::{
14 AsyncWriteExt,
15 io::{AsyncRead, AsyncWrite},
16};
17use std::{
18 borrow::Cow,
19 fmt::{self, Debug, Formatter},
20 io::{self, IoSlice, Write},
21 net::IpAddr,
22 pin::Pin,
23 str,
24 sync::Arc,
25 task::{Context, Poll, ready},
26 time::Instant,
27};
28
29#[derive(Debug)]
32pub(crate) enum WriteState {
33 Raw,
36 H1Chunked(H1ChunkedState),
38 H3Framed(H3FramedState),
40}
41
42#[derive(Debug, Default)]
43pub(crate) struct H1ChunkedState {
44 pub(crate) pending: Vec<u8>,
45 pub(crate) terminator_written: bool,
46}
47
48#[derive(Debug, Default)]
49pub(crate) struct H3FramedState {
50 pub(crate) pending: Vec<u8>,
51 pub(crate) terminator_written: bool,
52}
53
54fn compute_write_state(version: Version, outbound_headers: &Headers) -> WriteState {
58 match version {
59 Version::Http1_0 | Version::Http1_1 if has_chunked_encoding(outbound_headers) => {
60 WriteState::H1Chunked(H1ChunkedState::default())
61 }
62 Version::Http3 => WriteState::H3Framed(H3FramedState::default()),
63 _ => WriteState::Raw,
64 }
65}
66
67fn has_chunked_encoding(headers: &Headers) -> bool {
70 headers
71 .token_iter(KnownHeaderName::TransferEncoding)
72 .any(|coding| coding.eq_ignore_ascii_case("chunked"))
73}
74
75fn parse_content_length(inbound_headers: &Headers) -> Option<u64> {
77 if inbound_headers.has_header(KnownHeaderName::TransferEncoding) {
78 return None;
79 }
80 inbound_headers.content_length()
81}
82
83fn poll_drain_pending<T: AsyncWrite + Unpin>(
85 pending: &mut Vec<u8>,
86 cx: &mut Context<'_>,
87 transport: &mut T,
88) -> Poll<io::Result<()>> {
89 while !pending.is_empty() {
90 match Pin::new(&mut *transport).poll_write(cx, pending) {
91 Poll::Ready(Ok(0)) => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
92 Poll::Ready(Ok(n)) => {
93 pending.drain(..n);
94 }
95 Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
96 Poll::Pending => return Poll::Pending,
97 }
98 }
99 Poll::Ready(Ok(()))
100}
101
102fn best_effort_drain<T: AsyncWrite + Unpin>(
105 pending: &mut Vec<u8>,
106 cx: &mut Context<'_>,
107 transport: &mut T,
108) -> io::Result<()> {
109 while !pending.is_empty() {
110 match Pin::new(&mut *transport).poll_write(cx, pending) {
111 Poll::Ready(Ok(0)) => return Err(io::ErrorKind::WriteZero.into()),
112 Poll::Ready(Ok(n)) => {
113 pending.drain(..n);
114 }
115 Poll::Ready(Err(e)) => return Err(e),
116 Poll::Pending => break,
117 }
118 }
119 Ok(())
120}
121
122fn encode_h3_data_header(out: &mut Vec<u8>, payload_len: u64) {
125 let frame = Frame::Data(payload_len);
126 let header_len = frame.encoded_len();
127 let start = out.len();
128 out.resize(start + header_len, 0);
129 frame.encode(&mut out[start..]);
130}
131
132#[derive(Fieldwork)]
138#[fieldwork(get, get_mut, set, with, take, into_field, rename_predicates)]
139pub struct Upgrade<Transport> {
140 #[field(delegate, skip)]
143 pub(crate) inner: Box<UpgradeInner>,
144
145 pub(crate) transport: Transport,
147}
148
149#[derive(Fieldwork)]
151#[fieldwork(
152 get,
153 get_mut,
154 set,
155 with,
156 take,
157 into_field,
158 rename_predicates,
159 delegatable
160)]
161pub(crate) struct UpgradeInner {
162 #[field(deprecate(was = "request_headers", since = "1.3.0"))]
164 pub(crate) received_headers: Headers,
165
166 #[field(deprecate(was = "response_headers", since = "1.3.0"))]
169 pub(crate) sent_headers: Headers,
170
171 #[field(get = false)]
173 pub(crate) path: Cow<'static, str>,
174
175 #[field(copy)]
177 pub(crate) method: Method,
178
179 pub(crate) state: TypeSet,
181
182 #[field(
187 deref = "[u8]",
188 into_field = false,
189 set = false,
190 with = false,
191 get_mut = false
192 )]
193 pub(crate) buffer: Buffer,
194
195 #[field(deref = false)]
197 pub(crate) context: Arc<HttpContext>,
198
199 #[field(copy)]
201 pub(crate) peer_ip: Option<IpAddr>,
202
203 #[field(copy)]
205 pub(crate) start_time: Instant,
206
207 pub(crate) authority: Option<Cow<'static, str>>,
209
210 pub(crate) scheme: Option<Cow<'static, str>>,
212
213 #[field = false]
216 pub(crate) protocol_session: ProtocolSession,
217
218 pub(crate) protocol: Option<Cow<'static, str>>,
220
221 #[field = "http_version"]
223 pub(crate) version: Version,
224
225 #[field(copy)]
228 pub(crate) status: Option<Status>,
229
230 pub(crate) secure: bool,
232
233 #[field = false]
237 pub(crate) received_body_state: ReceivedBodyState,
238
239 #[field(get, get_mut, take, set = false, with = false, into_field = false)]
242 pub(crate) received_trailers: Option<Headers>,
243
244 #[field = false]
246 pub(crate) content_length_in: Option<u64>,
247
248 #[field = false]
250 pub(crate) write_state: WriteState,
251
252 #[field = false]
255 pub(crate) inbound_encoding: &'static Encoding,
256
257 #[field = false]
261 pub(crate) h3_trailer_decode_in: Option<H3TrailerFuture>,
262
263 #[field = false]
267 pub(crate) h3_trailer_payload_in: Vec<u8>,
268
269 #[field = false]
271 pub(crate) peer_gone: Option<PeerGone>,
272}
273
274impl<Transport> Upgrade<Transport> {
275 #[doc(hidden)]
276 pub fn new(
277 received_headers: Headers,
278 path: impl Into<Cow<'static, str>>,
279 method: Method,
280 transport: Transport,
281 buffer: Buffer,
282 version: Version,
283 ) -> Self {
284 Self {
285 inner: Box::new(UpgradeInner {
286 received_headers,
287 sent_headers: Headers::new(),
288 path: path.into(),
289 method,
290 buffer,
291 state: TypeSet::new(),
292 context: Arc::default(),
293 peer_ip: None,
294 start_time: Instant::now(),
295 authority: None,
296 scheme: None,
297 protocol_session: ProtocolSession::Http1,
298 protocol: None,
299 secure: false,
300 version,
301 status: None,
302 received_body_state: ReceivedBodyState::Raw { total: 0 },
303 received_trailers: None,
304 content_length_in: None,
305 write_state: WriteState::Raw,
306 inbound_encoding: encoding_rs::UTF_8,
307 h3_trailer_decode_in: None,
308 h3_trailer_payload_in: Vec::new(),
309 peer_gone: None,
310 }),
311 transport,
312 }
313 }
314
315 #[cfg(feature = "unstable")]
316 #[doc(hidden)]
317 #[allow(clippy::too_many_arguments)]
318 pub fn from_parts(
319 received_headers: Headers,
320 sent_headers: Headers,
321 path: Cow<'static, str>,
322 method: Method,
323 transport: Transport,
324 buffer: Buffer,
325 state: TypeSet,
326 context: Arc<HttpContext>,
327 peer_ip: Option<IpAddr>,
328 authority: Option<Cow<'static, str>>,
329 scheme: Option<Cow<'static, str>>,
330 protocol_session: ProtocolSession,
331 protocol: Option<Cow<'static, str>>,
332 version: Version,
333 status: Option<Status>,
334 secure: bool,
335 received_body_state: ReceivedBodyState,
336 received_trailers: Option<Headers>,
337 ) -> Self {
338 let write_state = compute_write_state(version, &sent_headers);
339 let content_length_in = parse_content_length(&received_headers);
340 let inbound_encoding = encoding(&received_headers);
341
342 Self {
343 inner: Box::new(UpgradeInner {
344 peer_gone: None,
347 received_headers,
348 sent_headers,
349 path,
350 method,
351 state,
352 buffer,
353 context,
354 peer_ip,
355 start_time: Instant::now(),
356 authority,
357 scheme,
358 protocol_session,
359 protocol,
360 version,
361 status,
362 secure,
363 received_body_state,
364 received_trailers,
365 content_length_in,
366 write_state,
367 inbound_encoding,
368 h3_trailer_decode_in: None,
369 h3_trailer_payload_in: Vec::new(),
370 }),
371 transport,
372 }
373 }
374
375 pub fn h2_connection(&self) -> Option<&Arc<H2Connection>> {
377 self.inner.protocol_session.h2_connection()
378 }
379
380 pub fn h2_stream_id(&self) -> Option<u32> {
382 self.inner.protocol_session.h2_stream_id()
383 }
384
385 pub fn h3_connection(&self) -> Option<&Arc<H3Connection>> {
387 self.inner.protocol_session.h3_connection()
388 }
389
390 pub fn h3_stream_id(&self) -> Option<u64> {
392 self.inner.protocol_session.h3_stream_id()
393 }
394
395 pub fn take_buffer(&mut self) -> Vec<u8> {
397 std::mem::take(&mut self.inner.buffer).into()
398 }
399
400 pub fn buffer_mut(&mut self) -> &mut [u8] {
405 self.inner.buffer.live_mut()
406 }
407
408 #[doc(hidden)]
409 pub fn buffer_and_transport_mut(&mut self) -> (&mut Buffer, &mut Transport) {
410 (&mut self.inner.buffer, &mut self.transport)
411 }
412
413 pub fn shared_state(&self) -> &TypeSet {
415 self.inner.context.shared_state()
416 }
417
418 pub fn path(&self) -> &str {
420 match self.inner.path.split_once('?') {
421 Some((path, _)) => path,
422 None => &self.inner.path,
423 }
424 }
425
426 pub fn querystring(&self) -> &str {
428 self.inner
429 .path
430 .split_once('?')
431 .map_or_default(|(_, query)| query)
432 }
433
434 pub fn map_transport<T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static>(
438 self,
439 f: impl Fn(Transport) -> T,
440 ) -> Upgrade<T> {
441 Upgrade {
442 inner: self.inner,
443 transport: f(self.transport),
444 }
445 }
446}
447
448impl<Transport: AsyncRead + Unpin> Upgrade<Transport> {
449 pub fn poll_closed(
476 self: Pin<&mut Self>,
477 cx: &mut Context<'_>,
478 read_allowance: usize,
479 ) -> Poll<()> {
480 let Self { inner, transport } = self.get_mut();
481
482 crate::liveness::poll_peer_gone(
483 &inner.protocol_session,
484 inner.version,
485 &mut inner.buffer,
486 transport,
487 inner.peer_gone.as_mut(),
488 read_allowance,
489 cx,
490 )
491 }
492}
493
494impl<Transport: AsyncWrite + Unpin> Upgrade<Transport> {
495 pub async fn send_trailers(self, trailers: Headers) -> io::Result<()> {
515 let Self {
516 inner,
517 mut transport,
518 } = self;
519 let UpgradeInner {
520 mut write_state,
521 context,
522 protocol_session,
523 ..
524 } = *inner;
525
526 match &mut write_state {
527 WriteState::H1Chunked(state) => {
528 if state.terminator_written {
529 return Err(io::ErrorKind::BrokenPipe.into());
530 }
531 state.pending.extend_from_slice(b"0\r\n");
532 crate::conn::write_headers_or_trailers(&mut state.pending, &trailers, &context)
533 .map_err(io::Error::other)?;
534 state.pending.extend_from_slice(b"\r\n");
535 state.terminator_written = true;
536
537 transport.write_all(&state.pending).await?;
538 state.pending.clear();
539 transport.close().await
540 }
541 WriteState::H3Framed(state) => {
542 if state.terminator_written {
543 return Err(io::ErrorKind::BrokenPipe.into());
544 }
545 let Some((h3, stream_id)) = protocol_session.as_h3() else {
546 return Err(io::ErrorKind::NotConnected.into());
547 };
548 let field_section = FieldSection::new(PseudoHeaders::default(), &trailers);
549 h3.encode_field_section_framed(&field_section, &mut state.pending, stream_id)?;
550 state.terminator_written = true;
551
552 transport.write_all(&state.pending).await?;
553 state.pending.clear();
554 transport.close().await
555 }
556 WriteState::Raw => {
557 if let Some((h2, stream_id)) = protocol_session.as_h2() {
558 h2.submit_trailers(stream_id, trailers)
559 } else {
560 log::warn!(
561 "Upgrade::send_trailers called on a raw upgrade with no per-stream \
562 framing; trailers dropped. Set `Transfer-Encoding: chunked` on the \
563 outbound headers if you intend to emit trailers over HTTP/1.1."
564 );
565 Ok(())
566 }
567 }
568 }
569 }
570}
571
572impl<Transport> Debug for Upgrade<Transport> {
573 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
574 f.debug_struct(&format!("Upgrade<{}>", std::any::type_name::<Transport>()))
575 .field("received_headers", &self.inner.received_headers)
576 .field("sent_headers", &self.inner.sent_headers)
577 .field("path", &self.inner.path)
578 .field("method", &self.inner.method)
579 .field("buffer", &self.inner.buffer)
580 .field("context", &self.inner.context)
581 .field("state", &self.inner.state)
582 .field("transport", &format_args!(".."))
583 .field("peer_ip", &self.inner.peer_ip)
584 .field("start_time", &self.inner.start_time)
585 .field("authority", &self.inner.authority)
586 .field("scheme", &self.inner.scheme)
587 .field("protocol_session", &self.inner.protocol_session)
588 .field("protocol", &self.inner.protocol)
589 .field("version", &self.inner.version)
590 .field("status", &self.inner.status)
591 .field("secure", &self.inner.secure)
592 .field("received_body_state", &self.inner.received_body_state)
593 .field("received_trailers", &self.inner.received_trailers)
594 .field("content_length_in", &self.inner.content_length_in)
595 .field("write_state", &self.inner.write_state)
596 .field("inbound_encoding", &self.inner.inbound_encoding.name())
597 .field(
598 "h3_trailer_decode_in",
599 &self
600 .inner
601 .h3_trailer_decode_in
602 .as_ref()
603 .map(|_| format_args!("..")),
604 )
605 .field(
606 "h3_trailer_payload_in_len",
607 &self.inner.h3_trailer_payload_in.len(),
608 )
609 .field(
610 "peer_gone",
611 &self.inner.peer_gone.as_ref().map(|_| format_args!("..")),
612 )
613 .finish()
614 }
615}
616
617impl<Transport> From<Conn<Transport>> for Upgrade<Transport> {
618 fn from(conn: Conn<Transport>) -> Self {
619 let Conn { inner, transport } = conn;
620 let ConnInner {
623 request_headers,
624 response_headers,
625 path,
626 method,
627 state,
628 buffer,
629 context,
630 peer_ip,
631 start_time,
632 authority,
633 scheme,
634 protocol_session,
635 protocol,
636 version,
637 status,
638 secure,
639 request_body_state,
640 request_trailers,
641 response_body,
642 after_send: _,
644 upgrade: _,
645 spare_request_headers: _,
647 response_buffer: _,
648 peer_gone,
649 } = *inner;
650
651 if let Some(body) = &response_body
652 && !body.is_empty()
653 {
654 log::warn!(
655 "Conn::upgrade() and a non-empty response body are both set; body is being \
656 discarded. The upgrade path is mutually exclusive with serving a response body."
657 );
658 }
659
660 let write_state = compute_write_state(version, &response_headers);
662 let content_length_in = parse_content_length(&request_headers);
663 let inbound_encoding = encoding(&request_headers);
664 let received_body_state = if matches!(version, Version::Http1_0 | Version::Http1_1)
669 && !request_headers.has_header(KnownHeaderName::TransferEncoding)
670 && !request_headers.has_header(KnownHeaderName::ContentLength)
671 {
672 ReceivedBodyState::Raw { total: 0 }
673 } else {
674 request_body_state
675 };
676 let received_trailers = request_trailers.filter(|t| !t.is_empty());
677
678 Self {
679 inner: Box::new(UpgradeInner {
680 received_headers: request_headers,
681 sent_headers: response_headers,
682 path,
683 method,
684 state,
685 buffer,
686 context: Arc::unwrap_or_clone(context),
687 peer_ip,
688 start_time,
689 authority,
690 scheme,
691 protocol_session,
692 protocol,
693 version,
694 status,
695 secure,
696 received_body_state,
697 received_trailers,
698 content_length_in,
699 write_state,
700 inbound_encoding,
701 h3_trailer_decode_in: None,
702 h3_trailer_payload_in: Vec::new(),
703 peer_gone,
704 }),
705 transport,
706 }
707 }
708}
709
710#[cfg(test)]
711mod tests;
712
713impl<Transport> AsyncRead for Upgrade<Transport>
714where
715 Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
716{
717 fn poll_read(
718 mut self: Pin<&mut Self>,
719 cx: &mut Context<'_>,
720 buf: &mut [u8],
721 ) -> Poll<io::Result<usize>> {
722 let Self { inner, transport } = &mut *self;
723 let UpgradeInner {
724 buffer,
725 received_body_state,
726 content_length_in,
727 context,
728 protocol_session,
729 received_trailers,
730 h3_trailer_decode_in,
731 h3_trailer_payload_in,
732 inbound_encoding,
733 ..
734 } = &mut **inner;
735
736 let protocol_session = protocol_session.clone();
737 let mut body: ReceivedBody<'_, Transport> = ReceivedBody::new_with_config(
738 *content_length_in,
739 buffer,
740 transport,
741 received_body_state,
742 None,
743 inbound_encoding,
744 &context.config,
745 )
746 .with_trailers(received_trailers)
747 .with_protocol_session(protocol_session)
748 .with_h3_trailer_future(h3_trailer_decode_in)
749 .with_h3_trailer_payload_buffer(h3_trailer_payload_in);
750
751 Pin::new(&mut body).poll_read(cx, buf)
752 }
753}
754
755impl<Transport: AsyncWrite + Unpin> AsyncWrite for Upgrade<Transport> {
756 fn poll_write(
757 mut self: Pin<&mut Self>,
758 cx: &mut Context<'_>,
759 buf: &[u8],
760 ) -> Poll<io::Result<usize>> {
761 let Self { inner, transport } = &mut *self;
762 let write_state = &mut inner.write_state;
763 match write_state {
764 WriteState::Raw => Pin::new(transport).poll_write(cx, buf),
765 WriteState::H1Chunked(state) => {
766 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
767
768 if buf.is_empty() {
770 return Poll::Ready(Ok(0));
771 }
772
773 if state.terminator_written {
774 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
775 }
776
777 write_chunk(&mut state.pending, buf);
778 best_effort_drain(&mut state.pending, cx, transport)?;
779 Poll::Ready(Ok(buf.len()))
780 }
781 WriteState::H3Framed(state) => {
782 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
783
784 if buf.is_empty() {
785 return Poll::Ready(Ok(0));
786 }
787
788 if state.terminator_written {
789 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
790 }
791
792 encode_h3_data_header(&mut state.pending, buf.len() as u64);
793 state.pending.extend_from_slice(buf);
794 best_effort_drain(&mut state.pending, cx, transport)?;
795 Poll::Ready(Ok(buf.len()))
796 }
797 }
798 }
799
800 fn poll_write_vectored(
801 mut self: Pin<&mut Self>,
802 cx: &mut Context<'_>,
803 bufs: &[IoSlice<'_>],
804 ) -> Poll<io::Result<usize>> {
805 let Self { inner, transport } = &mut *self;
806 let write_state = &mut inner.write_state;
807 match write_state {
808 WriteState::Raw => Pin::new(transport).poll_write_vectored(cx, bufs),
809 WriteState::H1Chunked(state) => {
810 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
811 let total: usize = bufs.iter().map(|b| b.len()).sum();
812 if total == 0 {
813 return Poll::Ready(Ok(0));
814 }
815 if state.terminator_written {
816 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
817 }
818 let _ = write!(state.pending, "{total:X}\r\n");
821 for b in bufs {
822 state.pending.extend_from_slice(b);
823 }
824 state.pending.extend_from_slice(b"\r\n");
825 best_effort_drain(&mut state.pending, cx, transport)?;
826 Poll::Ready(Ok(total))
827 }
828 WriteState::H3Framed(state) => {
829 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
830 let total: usize = bufs.iter().map(|b| b.len()).sum();
831 if total == 0 {
832 return Poll::Ready(Ok(0));
833 }
834 if state.terminator_written {
835 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
836 }
837 encode_h3_data_header(&mut state.pending, total as u64);
840 for b in bufs {
841 state.pending.extend_from_slice(b);
842 }
843 best_effort_drain(&mut state.pending, cx, transport)?;
844 Poll::Ready(Ok(total))
845 }
846 }
847 }
848
849 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
850 let Self { inner, transport } = &mut *self;
851 let write_state = &mut inner.write_state;
852 match write_state {
853 WriteState::Raw => Pin::new(transport).poll_flush(cx),
854 WriteState::H1Chunked(state) => {
855 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
856 Pin::new(transport).poll_flush(cx)
857 }
858 WriteState::H3Framed(state) => {
859 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
860 Pin::new(transport).poll_flush(cx)
861 }
862 }
863 }
864
865 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
866 let Self { inner, transport } = &mut *self;
867 let write_state = &mut inner.write_state;
868 match write_state {
869 WriteState::Raw => Pin::new(transport).poll_close(cx),
870 WriteState::H1Chunked(state) => {
871 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
872 if !state.terminator_written {
873 state.pending.extend_from_slice(b"0\r\n\r\n");
874 state.terminator_written = true;
876 }
877 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
878 Pin::new(transport).poll_close(cx)
879 }
880 WriteState::H3Framed(state) => {
881 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
883 state.terminator_written = true;
884 Pin::new(transport).poll_close(cx)
885 }
886 }
887 }
888}