1use crate::{
2 Buffer, Conn, Headers, HttpContext, KnownHeaderName, Method, PeerGone, ProtocolSession,
3 ReceivedBody, Status, TypeSet, Version,
4 h2::H2Connection,
5 h3::{Frame, H3Connection},
6 headers::qpack::{FieldSection, PseudoHeaders},
7 received_body::{H3TrailerFuture, ReceivedBodyState, write_chunk},
8 util::encoding,
9};
10use encoding_rs::Encoding;
11use fieldwork::Fieldwork;
12use futures_lite::{
13 AsyncWriteExt,
14 io::{AsyncRead, AsyncWrite},
15};
16use std::{
17 borrow::Cow,
18 fmt::{self, Debug, Formatter},
19 io::{self, IoSlice, Write},
20 net::IpAddr,
21 pin::Pin,
22 str,
23 sync::Arc,
24 task::{Context, Poll, ready},
25 time::Instant,
26};
27
28#[derive(Debug)]
31pub(crate) enum WriteState {
32 Raw,
35 H1Chunked(H1ChunkedState),
37 H3Framed(H3FramedState),
39}
40
41#[derive(Debug, Default)]
42pub(crate) struct H1ChunkedState {
43 pub(crate) pending: Vec<u8>,
44 pub(crate) terminator_written: bool,
45}
46
47#[derive(Debug, Default)]
48pub(crate) struct H3FramedState {
49 pub(crate) pending: Vec<u8>,
50 pub(crate) terminator_written: bool,
51}
52
53fn compute_write_state(version: Version, outbound_headers: &Headers) -> WriteState {
57 match version {
58 Version::Http1_0 | Version::Http1_1 if has_chunked_encoding(outbound_headers) => {
59 WriteState::H1Chunked(H1ChunkedState::default())
60 }
61 Version::Http3 => WriteState::H3Framed(H3FramedState::default()),
62 _ => WriteState::Raw,
63 }
64}
65
66fn has_chunked_encoding(headers: &Headers) -> bool {
69 headers
70 .token_iter(KnownHeaderName::TransferEncoding)
71 .any(|coding| coding.eq_ignore_ascii_case("chunked"))
72}
73
74fn parse_content_length(inbound_headers: &Headers) -> Option<u64> {
76 if inbound_headers.has_header(KnownHeaderName::TransferEncoding) {
77 return None;
78 }
79 inbound_headers.content_length()
80}
81
82fn poll_drain_pending<T: AsyncWrite + Unpin>(
84 pending: &mut Vec<u8>,
85 cx: &mut Context<'_>,
86 transport: &mut T,
87) -> Poll<io::Result<()>> {
88 while !pending.is_empty() {
89 match Pin::new(&mut *transport).poll_write(cx, pending) {
90 Poll::Ready(Ok(0)) => return Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
91 Poll::Ready(Ok(n)) => {
92 pending.drain(..n);
93 }
94 Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
95 Poll::Pending => return Poll::Pending,
96 }
97 }
98 Poll::Ready(Ok(()))
99}
100
101fn best_effort_drain<T: AsyncWrite + Unpin>(
104 pending: &mut Vec<u8>,
105 cx: &mut Context<'_>,
106 transport: &mut T,
107) -> io::Result<()> {
108 while !pending.is_empty() {
109 match Pin::new(&mut *transport).poll_write(cx, pending) {
110 Poll::Ready(Ok(0)) => return Err(io::ErrorKind::WriteZero.into()),
111 Poll::Ready(Ok(n)) => {
112 pending.drain(..n);
113 }
114 Poll::Ready(Err(e)) => return Err(e),
115 Poll::Pending => break,
116 }
117 }
118 Ok(())
119}
120
121fn encode_h3_data_header(out: &mut Vec<u8>, payload_len: u64) {
124 let frame = Frame::Data(payload_len);
125 let header_len = frame.encoded_len();
126 let start = out.len();
127 out.resize(start + header_len, 0);
128 frame.encode(&mut out[start..]);
129}
130
131#[derive(Fieldwork)]
137#[fieldwork(get, get_mut, set, with, take, into_field, rename_predicates)]
138pub struct Upgrade<Transport> {
139 #[field(deprecate(was = "request_headers", since = "1.3.0"))]
141 pub(crate) received_headers: Headers,
142
143 #[field(deprecate(was = "response_headers", since = "1.3.0"))]
146 pub(crate) sent_headers: Headers,
147
148 #[field(get = false)]
150 pub(crate) path: Cow<'static, str>,
151
152 #[field(copy)]
154 pub(crate) method: Method,
155
156 pub(crate) state: TypeSet,
158
159 pub(crate) transport: Transport,
161
162 #[field(
167 deref = "[u8]",
168 into_field = false,
169 set = false,
170 with = false,
171 get_mut = false
172 )]
173 pub(crate) buffer: Buffer,
174
175 #[field(deref = false)]
177 pub(crate) context: Arc<HttpContext>,
178
179 #[field(copy)]
181 pub(crate) peer_ip: Option<IpAddr>,
182
183 #[field(copy)]
185 pub(crate) start_time: Instant,
186
187 pub(crate) authority: Option<Cow<'static, str>>,
189
190 pub(crate) scheme: Option<Cow<'static, str>>,
192
193 #[field = false]
196 pub(crate) protocol_session: ProtocolSession,
197
198 pub(crate) protocol: Option<Cow<'static, str>>,
200
201 #[field = "http_version"]
203 pub(crate) version: Version,
204
205 #[field(copy)]
208 pub(crate) status: Option<Status>,
209
210 pub(crate) secure: bool,
212
213 #[field = false]
217 pub(crate) received_body_state: ReceivedBodyState,
218
219 #[field(get, get_mut, take, set = false, with = false, into_field = false)]
222 pub(crate) received_trailers: Option<Headers>,
223
224 #[field = false]
226 pub(crate) content_length_in: Option<u64>,
227
228 #[field = false]
230 pub(crate) write_state: WriteState,
231
232 #[field = false]
235 pub(crate) inbound_encoding: &'static Encoding,
236
237 #[field = false]
241 pub(crate) h3_trailer_decode_in: Option<H3TrailerFuture>,
242
243 #[field = false]
247 pub(crate) h3_trailer_payload_in: Vec<u8>,
248
249 #[field = false]
251 pub(crate) peer_gone: Option<PeerGone>,
252}
253
254impl<Transport> Upgrade<Transport> {
255 #[doc(hidden)]
256 pub fn new(
257 received_headers: Headers,
258 path: impl Into<Cow<'static, str>>,
259 method: Method,
260 transport: Transport,
261 buffer: Buffer,
262 version: Version,
263 ) -> Self {
264 Self {
265 received_headers,
266 sent_headers: Headers::new(),
267 path: path.into(),
268 method,
269 transport,
270 buffer,
271 state: TypeSet::new(),
272 context: Arc::default(),
273 peer_ip: None,
274 start_time: Instant::now(),
275 authority: None,
276 scheme: None,
277 protocol_session: ProtocolSession::Http1,
278 protocol: None,
279 secure: false,
280 version,
281 status: None,
282 received_body_state: ReceivedBodyState::Raw { total: 0 },
283 received_trailers: None,
284 content_length_in: None,
285 write_state: WriteState::Raw,
286 inbound_encoding: encoding_rs::UTF_8,
287 h3_trailer_decode_in: None,
288 h3_trailer_payload_in: Vec::new(),
289 peer_gone: None,
290 }
291 }
292
293 #[cfg(feature = "unstable")]
294 #[doc(hidden)]
295 #[allow(clippy::too_many_arguments)]
296 pub fn from_parts(
297 received_headers: Headers,
298 sent_headers: Headers,
299 path: Cow<'static, str>,
300 method: Method,
301 transport: Transport,
302 buffer: Buffer,
303 state: TypeSet,
304 context: Arc<HttpContext>,
305 peer_ip: Option<IpAddr>,
306 authority: Option<Cow<'static, str>>,
307 scheme: Option<Cow<'static, str>>,
308 protocol_session: ProtocolSession,
309 protocol: Option<Cow<'static, str>>,
310 version: Version,
311 status: Option<Status>,
312 secure: bool,
313 received_body_state: ReceivedBodyState,
314 received_trailers: Option<Headers>,
315 ) -> Self {
316 let write_state = compute_write_state(version, &sent_headers);
317 let content_length_in = parse_content_length(&received_headers);
318 let inbound_encoding = encoding(&received_headers);
319
320 Self {
321 peer_gone: None,
324 received_headers,
325 sent_headers,
326 path,
327 method,
328 state,
329 transport,
330 buffer,
331 context,
332 peer_ip,
333 start_time: Instant::now(),
334 authority,
335 scheme,
336 protocol_session,
337 protocol,
338 version,
339 status,
340 secure,
341 received_body_state,
342 received_trailers,
343 content_length_in,
344 write_state,
345 inbound_encoding,
346 h3_trailer_decode_in: None,
347 h3_trailer_payload_in: Vec::new(),
348 }
349 }
350
351 pub fn h2_connection(&self) -> Option<&Arc<H2Connection>> {
353 self.protocol_session.h2_connection()
354 }
355
356 pub fn h2_stream_id(&self) -> Option<u32> {
358 self.protocol_session.h2_stream_id()
359 }
360
361 pub fn h3_connection(&self) -> Option<&Arc<H3Connection>> {
363 self.protocol_session.h3_connection()
364 }
365
366 pub fn h3_stream_id(&self) -> Option<u64> {
368 self.protocol_session.h3_stream_id()
369 }
370
371 pub fn take_buffer(&mut self) -> Vec<u8> {
373 std::mem::take(&mut self.buffer).into()
374 }
375
376 pub fn buffer_mut(&mut self) -> &mut [u8] {
381 self.buffer.live_mut()
382 }
383
384 #[doc(hidden)]
385 pub fn buffer_and_transport_mut(&mut self) -> (&mut Buffer, &mut Transport) {
386 (&mut self.buffer, &mut self.transport)
387 }
388
389 pub fn shared_state(&self) -> &TypeSet {
391 self.context.shared_state()
392 }
393
394 pub fn path(&self) -> &str {
396 match self.path.split_once('?') {
397 Some((path, _)) => path,
398 None => &self.path,
399 }
400 }
401
402 pub fn querystring(&self) -> &str {
404 self.path
405 .split_once('?')
406 .map(|(_, query)| query)
407 .unwrap_or_default()
408 }
409
410 pub fn map_transport<T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static>(
414 self,
415 f: impl Fn(Transport) -> T,
416 ) -> Upgrade<T> {
417 Upgrade {
421 transport: f(self.transport),
422 path: self.path,
423 method: self.method,
424 state: self.state,
425 buffer: self.buffer,
426 received_headers: self.received_headers,
427 sent_headers: self.sent_headers,
428 context: self.context,
429 peer_ip: self.peer_ip,
430 start_time: self.start_time,
431 authority: self.authority,
432 scheme: self.scheme,
433 protocol_session: self.protocol_session,
434 protocol: self.protocol,
435 version: self.version,
436 status: self.status,
437 secure: self.secure,
438 received_body_state: self.received_body_state,
439 received_trailers: self.received_trailers,
440 content_length_in: self.content_length_in,
441 write_state: self.write_state,
442 inbound_encoding: self.inbound_encoding,
443 h3_trailer_decode_in: self.h3_trailer_decode_in,
444 h3_trailer_payload_in: self.h3_trailer_payload_in,
445 peer_gone: self.peer_gone,
446 }
447 }
448}
449
450impl<Transport: AsyncRead + Unpin> Upgrade<Transport> {
451 pub fn poll_closed(
478 self: Pin<&mut Self>,
479 cx: &mut Context<'_>,
480 read_allowance: usize,
481 ) -> Poll<()> {
482 let Self {
483 protocol_session,
484 version,
485 buffer,
486 transport,
487 peer_gone,
488 ..
489 } = self.get_mut();
490
491 crate::liveness::poll_peer_gone(
492 protocol_session,
493 *version,
494 buffer,
495 transport,
496 peer_gone.as_mut(),
497 read_allowance,
498 cx,
499 )
500 }
501}
502
503impl<Transport: AsyncWrite + Unpin> Upgrade<Transport> {
504 pub async fn send_trailers(self, trailers: Headers) -> io::Result<()> {
524 let Self {
525 mut transport,
526 mut write_state,
527 context,
528 protocol_session,
529 ..
530 } = self;
531
532 match &mut write_state {
533 WriteState::H1Chunked(state) => {
534 if state.terminator_written {
535 return Err(io::ErrorKind::BrokenPipe.into());
536 }
537 state.pending.extend_from_slice(b"0\r\n");
538 crate::conn::write_headers_or_trailers(&mut state.pending, &trailers, &context)
539 .map_err(io::Error::other)?;
540 state.pending.extend_from_slice(b"\r\n");
541 state.terminator_written = true;
542
543 transport.write_all(&state.pending).await?;
544 state.pending.clear();
545 transport.close().await
546 }
547 WriteState::H3Framed(state) => {
548 if state.terminator_written {
549 return Err(io::ErrorKind::BrokenPipe.into());
550 }
551 let Some((h3, stream_id)) = protocol_session.as_h3() else {
552 return Err(io::ErrorKind::NotConnected.into());
553 };
554 let field_section = FieldSection::new(PseudoHeaders::default(), &trailers);
555 h3.encode_field_section_framed(&field_section, &mut state.pending, stream_id)?;
556 state.terminator_written = true;
557
558 transport.write_all(&state.pending).await?;
559 state.pending.clear();
560 transport.close().await
561 }
562 WriteState::Raw => {
563 if let Some((h2, stream_id)) = protocol_session.as_h2() {
564 h2.submit_trailers(stream_id, trailers)
565 } else {
566 log::warn!(
567 "Upgrade::send_trailers called on a raw upgrade with no per-stream \
568 framing; trailers dropped. Set `Transfer-Encoding: chunked` on the \
569 outbound headers if you intend to emit trailers over HTTP/1.1."
570 );
571 Ok(())
572 }
573 }
574 }
575 }
576}
577
578impl<Transport> Debug for Upgrade<Transport> {
579 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
580 f.debug_struct(&format!("Upgrade<{}>", std::any::type_name::<Transport>()))
581 .field("received_headers", &self.received_headers)
582 .field("sent_headers", &self.sent_headers)
583 .field("path", &self.path)
584 .field("method", &self.method)
585 .field("buffer", &self.buffer)
586 .field("context", &self.context)
587 .field("state", &self.state)
588 .field("transport", &format_args!(".."))
589 .field("peer_ip", &self.peer_ip)
590 .field("start_time", &self.start_time)
591 .field("authority", &self.authority)
592 .field("scheme", &self.scheme)
593 .field("protocol_session", &self.protocol_session)
594 .field("protocol", &self.protocol)
595 .field("version", &self.version)
596 .field("status", &self.status)
597 .field("secure", &self.secure)
598 .field("received_body_state", &self.received_body_state)
599 .field("received_trailers", &self.received_trailers)
600 .field("content_length_in", &self.content_length_in)
601 .field("write_state", &self.write_state)
602 .field("inbound_encoding", &self.inbound_encoding.name())
603 .field(
604 "h3_trailer_decode_in",
605 &self
606 .h3_trailer_decode_in
607 .as_ref()
608 .map(|_| format_args!("..")),
609 )
610 .field(
611 "h3_trailer_payload_in_len",
612 &self.h3_trailer_payload_in.len(),
613 )
614 .field(
615 "peer_gone",
616 &self.peer_gone.as_ref().map(|_| format_args!("..")),
617 )
618 .finish()
619 }
620}
621
622impl<Transport> From<Conn<Transport>> for Upgrade<Transport> {
623 fn from(conn: Conn<Transport>) -> Self {
624 let Conn {
627 request_headers,
628 response_headers,
629 path,
630 method,
631 state,
632 transport,
633 buffer,
634 context,
635 peer_ip,
636 start_time,
637 authority,
638 scheme,
639 protocol_session,
640 protocol,
641 version,
642 status,
643 secure,
644 request_body_state,
645 request_trailers,
646 response_body,
647 after_send: _,
649 upgrade: _,
650 peer_gone,
651 } = conn;
652
653 if let Some(body) = &response_body
654 && !body.is_empty()
655 {
656 log::warn!(
657 "Conn::upgrade() and a non-empty response body are both set; body is being \
658 discarded. The upgrade path is mutually exclusive with serving a response body."
659 );
660 }
661
662 let write_state = compute_write_state(version, &response_headers);
664 let content_length_in = parse_content_length(&request_headers);
665 let inbound_encoding = encoding(&request_headers);
666 let received_body_state = if matches!(version, Version::Http1_0 | Version::Http1_1)
671 && !request_headers.has_header(KnownHeaderName::TransferEncoding)
672 && !request_headers.has_header(KnownHeaderName::ContentLength)
673 {
674 ReceivedBodyState::Raw { total: 0 }
675 } else {
676 request_body_state
677 };
678 let received_trailers = request_trailers.filter(|t| !t.is_empty());
679
680 Self {
681 received_headers: request_headers,
682 sent_headers: response_headers,
683 path,
684 method,
685 state,
686 transport,
687 buffer,
688 context,
689 peer_ip,
690 start_time,
691 authority,
692 scheme,
693 protocol_session,
694 protocol,
695 version,
696 status,
697 secure,
698 received_body_state,
699 received_trailers,
700 content_length_in,
701 write_state,
702 inbound_encoding,
703 h3_trailer_decode_in: None,
704 h3_trailer_payload_in: Vec::new(),
705 peer_gone,
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 {
723 transport,
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 *self;
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 {
762 transport,
763 write_state,
764 ..
765 } = &mut *self;
766 match write_state {
767 WriteState::Raw => Pin::new(transport).poll_write(cx, buf),
768 WriteState::H1Chunked(state) => {
769 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
770
771 if buf.is_empty() {
773 return Poll::Ready(Ok(0));
774 }
775
776 if state.terminator_written {
777 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
778 }
779
780 write_chunk(&mut state.pending, buf);
781 best_effort_drain(&mut state.pending, cx, transport)?;
782 Poll::Ready(Ok(buf.len()))
783 }
784 WriteState::H3Framed(state) => {
785 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
786
787 if buf.is_empty() {
788 return Poll::Ready(Ok(0));
789 }
790
791 if state.terminator_written {
792 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
793 }
794
795 encode_h3_data_header(&mut state.pending, buf.len() as u64);
796 state.pending.extend_from_slice(buf);
797 best_effort_drain(&mut state.pending, cx, transport)?;
798 Poll::Ready(Ok(buf.len()))
799 }
800 }
801 }
802
803 fn poll_write_vectored(
804 mut self: Pin<&mut Self>,
805 cx: &mut Context<'_>,
806 bufs: &[IoSlice<'_>],
807 ) -> Poll<io::Result<usize>> {
808 let Self {
809 transport,
810 write_state,
811 ..
812 } = &mut *self;
813 match write_state {
814 WriteState::Raw => Pin::new(transport).poll_write_vectored(cx, bufs),
815 WriteState::H1Chunked(state) => {
816 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
817 let total: usize = bufs.iter().map(|b| b.len()).sum();
818 if total == 0 {
819 return Poll::Ready(Ok(0));
820 }
821 if state.terminator_written {
822 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
823 }
824 let _ = write!(state.pending, "{total:X}\r\n");
827 for b in bufs {
828 state.pending.extend_from_slice(b);
829 }
830 state.pending.extend_from_slice(b"\r\n");
831 best_effort_drain(&mut state.pending, cx, transport)?;
832 Poll::Ready(Ok(total))
833 }
834 WriteState::H3Framed(state) => {
835 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
836 let total: usize = bufs.iter().map(|b| b.len()).sum();
837 if total == 0 {
838 return Poll::Ready(Ok(0));
839 }
840 if state.terminator_written {
841 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
842 }
843 encode_h3_data_header(&mut state.pending, total as u64);
846 for b in bufs {
847 state.pending.extend_from_slice(b);
848 }
849 best_effort_drain(&mut state.pending, cx, transport)?;
850 Poll::Ready(Ok(total))
851 }
852 }
853 }
854
855 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
856 let Self {
857 transport,
858 write_state,
859 ..
860 } = &mut *self;
861 match write_state {
862 WriteState::Raw => Pin::new(transport).poll_flush(cx),
863 WriteState::H1Chunked(state) => {
864 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
865 Pin::new(transport).poll_flush(cx)
866 }
867 WriteState::H3Framed(state) => {
868 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
869 Pin::new(transport).poll_flush(cx)
870 }
871 }
872 }
873
874 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
875 let Self {
876 transport,
877 write_state,
878 ..
879 } = &mut *self;
880 match write_state {
881 WriteState::Raw => Pin::new(transport).poll_close(cx),
882 WriteState::H1Chunked(state) => {
883 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
884 if !state.terminator_written {
885 state.pending.extend_from_slice(b"0\r\n\r\n");
886 state.terminator_written = true;
888 }
889 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
890 Pin::new(transport).poll_close(cx)
891 }
892 WriteState::H3Framed(state) => {
893 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
895 state.terminator_written = true;
896 Pin::new(transport).poll_close(cx)
897 }
898 }
899 }
900}