1use crate::{
2 Buffer, Conn, Headers, HttpContext, KnownHeaderName, Method, ProtocolSession, ReceivedBody,
3 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(deref = "[u8]", into_field = false, set = false, with = false)]
167 pub(crate) buffer: Buffer,
168
169 #[field(deref = false)]
171 pub(crate) context: Arc<HttpContext>,
172
173 #[field(copy)]
175 pub(crate) peer_ip: Option<IpAddr>,
176
177 #[field(copy)]
179 pub(crate) start_time: Instant,
180
181 pub(crate) authority: Option<Cow<'static, str>>,
183
184 pub(crate) scheme: Option<Cow<'static, str>>,
186
187 #[field = false]
190 pub(crate) protocol_session: ProtocolSession,
191
192 pub(crate) protocol: Option<Cow<'static, str>>,
194
195 #[field = "http_version"]
197 pub(crate) version: Version,
198
199 #[field(copy)]
202 pub(crate) status: Option<Status>,
203
204 pub(crate) secure: bool,
206
207 #[field = false]
211 pub(crate) received_body_state: ReceivedBodyState,
212
213 #[field(get, get_mut, take, set = false, with = false, into_field = false)]
216 pub(crate) received_trailers: Option<Headers>,
217
218 #[field = false]
220 pub(crate) content_length_in: Option<u64>,
221
222 #[field = false]
224 pub(crate) write_state: WriteState,
225
226 #[field = false]
229 pub(crate) inbound_encoding: &'static Encoding,
230
231 #[field = false]
235 pub(crate) h3_trailer_decode_in: Option<H3TrailerFuture>,
236
237 #[field = false]
241 pub(crate) h3_trailer_payload_in: Vec<u8>,
242}
243
244impl<Transport> Upgrade<Transport> {
245 #[doc(hidden)]
246 pub fn new(
247 received_headers: Headers,
248 path: impl Into<Cow<'static, str>>,
249 method: Method,
250 transport: Transport,
251 buffer: Buffer,
252 version: Version,
253 ) -> Self {
254 Self {
255 received_headers,
256 sent_headers: Headers::new(),
257 path: path.into(),
258 method,
259 transport,
260 buffer,
261 state: TypeSet::new(),
262 context: Arc::default(),
263 peer_ip: None,
264 start_time: Instant::now(),
265 authority: None,
266 scheme: None,
267 protocol_session: ProtocolSession::Http1,
268 protocol: None,
269 secure: false,
270 version,
271 status: None,
272 received_body_state: ReceivedBodyState::Raw { total: 0 },
273 received_trailers: None,
274 content_length_in: None,
275 write_state: WriteState::Raw,
276 inbound_encoding: encoding_rs::UTF_8,
277 h3_trailer_decode_in: None,
278 h3_trailer_payload_in: Vec::new(),
279 }
280 }
281
282 #[cfg(feature = "unstable")]
283 #[doc(hidden)]
284 #[allow(clippy::too_many_arguments)]
285 pub fn from_parts(
286 received_headers: Headers,
287 sent_headers: Headers,
288 path: Cow<'static, str>,
289 method: Method,
290 transport: Transport,
291 buffer: Buffer,
292 state: TypeSet,
293 context: Arc<HttpContext>,
294 peer_ip: Option<IpAddr>,
295 authority: Option<Cow<'static, str>>,
296 scheme: Option<Cow<'static, str>>,
297 protocol_session: ProtocolSession,
298 protocol: Option<Cow<'static, str>>,
299 version: Version,
300 status: Option<Status>,
301 secure: bool,
302 received_body_state: ReceivedBodyState,
303 received_trailers: Option<Headers>,
304 ) -> Self {
305 let write_state = compute_write_state(version, &sent_headers);
306 let content_length_in = parse_content_length(&received_headers);
307 let inbound_encoding = encoding(&received_headers);
308
309 Self {
310 received_headers,
311 sent_headers,
312 path,
313 method,
314 state,
315 transport,
316 buffer,
317 context,
318 peer_ip,
319 start_time: Instant::now(),
320 authority,
321 scheme,
322 protocol_session,
323 protocol,
324 version,
325 status,
326 secure,
327 received_body_state,
328 received_trailers,
329 content_length_in,
330 write_state,
331 inbound_encoding,
332 h3_trailer_decode_in: None,
333 h3_trailer_payload_in: Vec::new(),
334 }
335 }
336
337 pub fn h2_connection(&self) -> Option<&Arc<H2Connection>> {
339 self.protocol_session.h2_connection()
340 }
341
342 pub fn h2_stream_id(&self) -> Option<u32> {
344 self.protocol_session.h2_stream_id()
345 }
346
347 pub fn h3_connection(&self) -> Option<&Arc<H3Connection>> {
349 self.protocol_session.h3_connection()
350 }
351
352 pub fn h3_stream_id(&self) -> Option<u64> {
354 self.protocol_session.h3_stream_id()
355 }
356
357 pub fn take_buffer(&mut self) -> Vec<u8> {
359 std::mem::take(&mut self.buffer).into()
360 }
361
362 #[doc(hidden)]
363 pub fn buffer_and_transport_mut(&mut self) -> (&mut Buffer, &mut Transport) {
364 (&mut self.buffer, &mut self.transport)
365 }
366
367 pub fn shared_state(&self) -> &TypeSet {
369 self.context.shared_state()
370 }
371
372 pub fn path(&self) -> &str {
374 match self.path.split_once('?') {
375 Some((path, _)) => path,
376 None => &self.path,
377 }
378 }
379
380 pub fn querystring(&self) -> &str {
382 self.path
383 .split_once('?')
384 .map(|(_, query)| query)
385 .unwrap_or_default()
386 }
387
388 pub fn map_transport<T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static>(
392 self,
393 f: impl Fn(Transport) -> T,
394 ) -> Upgrade<T> {
395 Upgrade {
399 transport: f(self.transport),
400 path: self.path,
401 method: self.method,
402 state: self.state,
403 buffer: self.buffer,
404 received_headers: self.received_headers,
405 sent_headers: self.sent_headers,
406 context: self.context,
407 peer_ip: self.peer_ip,
408 start_time: self.start_time,
409 authority: self.authority,
410 scheme: self.scheme,
411 protocol_session: self.protocol_session,
412 protocol: self.protocol,
413 version: self.version,
414 status: self.status,
415 secure: self.secure,
416 received_body_state: self.received_body_state,
417 received_trailers: self.received_trailers,
418 content_length_in: self.content_length_in,
419 write_state: self.write_state,
420 inbound_encoding: self.inbound_encoding,
421 h3_trailer_decode_in: self.h3_trailer_decode_in,
422 h3_trailer_payload_in: self.h3_trailer_payload_in,
423 }
424 }
425}
426
427impl<Transport: AsyncWrite + Unpin> Upgrade<Transport> {
428 pub async fn send_trailers(self, trailers: Headers) -> io::Result<()> {
448 let Self {
449 mut transport,
450 mut write_state,
451 context,
452 protocol_session,
453 ..
454 } = self;
455
456 match &mut write_state {
457 WriteState::H1Chunked(state) => {
458 if state.terminator_written {
459 return Err(io::ErrorKind::BrokenPipe.into());
460 }
461 state.pending.extend_from_slice(b"0\r\n");
462 crate::conn::write_headers_or_trailers(&mut state.pending, &trailers, &context)
463 .map_err(io::Error::other)?;
464 state.pending.extend_from_slice(b"\r\n");
465 state.terminator_written = true;
466
467 transport.write_all(&state.pending).await?;
468 state.pending.clear();
469 transport.close().await
470 }
471 WriteState::H3Framed(state) => {
472 if state.terminator_written {
473 return Err(io::ErrorKind::BrokenPipe.into());
474 }
475 let Some((h3, stream_id)) = protocol_session.as_h3() else {
476 return Err(io::ErrorKind::NotConnected.into());
477 };
478 let field_section = FieldSection::new(PseudoHeaders::default(), &trailers);
479 h3.encode_field_section_framed(&field_section, &mut state.pending, stream_id)?;
480 state.terminator_written = true;
481
482 transport.write_all(&state.pending).await?;
483 state.pending.clear();
484 transport.close().await
485 }
486 WriteState::Raw => {
487 if let Some((h2, stream_id)) = protocol_session.as_h2() {
488 h2.submit_trailers(stream_id, trailers)
489 } else {
490 log::warn!(
491 "Upgrade::send_trailers called on a raw upgrade with no per-stream \
492 framing; trailers dropped. Set `Transfer-Encoding: chunked` on the \
493 outbound headers if you intend to emit trailers over HTTP/1.1."
494 );
495 Ok(())
496 }
497 }
498 }
499 }
500}
501
502impl<Transport> Debug for Upgrade<Transport> {
503 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
504 f.debug_struct(&format!("Upgrade<{}>", std::any::type_name::<Transport>()))
505 .field("received_headers", &self.received_headers)
506 .field("sent_headers", &self.sent_headers)
507 .field("path", &self.path)
508 .field("method", &self.method)
509 .field("buffer", &self.buffer)
510 .field("context", &self.context)
511 .field("state", &self.state)
512 .field("transport", &format_args!(".."))
513 .field("peer_ip", &self.peer_ip)
514 .field("start_time", &self.start_time)
515 .field("authority", &self.authority)
516 .field("scheme", &self.scheme)
517 .field("protocol_session", &self.protocol_session)
518 .field("protocol", &self.protocol)
519 .field("version", &self.version)
520 .field("status", &self.status)
521 .field("secure", &self.secure)
522 .field("received_body_state", &self.received_body_state)
523 .field("received_trailers", &self.received_trailers)
524 .field("content_length_in", &self.content_length_in)
525 .field("write_state", &self.write_state)
526 .field("inbound_encoding", &self.inbound_encoding.name())
527 .field(
528 "h3_trailer_decode_in",
529 &self
530 .h3_trailer_decode_in
531 .as_ref()
532 .map(|_| format_args!("..")),
533 )
534 .field(
535 "h3_trailer_payload_in_len",
536 &self.h3_trailer_payload_in.len(),
537 )
538 .finish()
539 }
540}
541
542impl<Transport> From<Conn<Transport>> for Upgrade<Transport> {
543 fn from(conn: Conn<Transport>) -> Self {
544 let Conn {
547 request_headers,
548 response_headers,
549 path,
550 method,
551 state,
552 transport,
553 buffer,
554 context,
555 peer_ip,
556 start_time,
557 authority,
558 scheme,
559 protocol_session,
560 protocol,
561 version,
562 status,
563 secure,
564 request_body_state,
565 request_trailers,
566 response_body,
567 after_send: _,
569 upgrade: _,
570 } = conn;
571
572 if let Some(body) = &response_body
573 && !body.is_empty()
574 {
575 log::warn!(
576 "Conn::upgrade() and a non-empty response body are both set; body is being \
577 discarded. The upgrade path is mutually exclusive with serving a response body."
578 );
579 }
580
581 let write_state = compute_write_state(version, &response_headers);
583 let content_length_in = parse_content_length(&request_headers);
584 let inbound_encoding = encoding(&request_headers);
585 let received_body_state = if matches!(version, Version::Http1_0 | Version::Http1_1)
590 && !request_headers.has_header(KnownHeaderName::TransferEncoding)
591 && !request_headers.has_header(KnownHeaderName::ContentLength)
592 {
593 ReceivedBodyState::Raw { total: 0 }
594 } else {
595 request_body_state
596 };
597 let received_trailers = request_trailers.filter(|t| !t.is_empty());
598
599 Self {
600 received_headers: request_headers,
601 sent_headers: response_headers,
602 path,
603 method,
604 state,
605 transport,
606 buffer,
607 context,
608 peer_ip,
609 start_time,
610 authority,
611 scheme,
612 protocol_session,
613 protocol,
614 version,
615 status,
616 secure,
617 received_body_state,
618 received_trailers,
619 content_length_in,
620 write_state,
621 inbound_encoding,
622 h3_trailer_decode_in: None,
623 h3_trailer_payload_in: Vec::new(),
624 }
625 }
626}
627
628#[cfg(test)]
629mod tests;
630
631impl<Transport> AsyncRead for Upgrade<Transport>
632where
633 Transport: AsyncRead + AsyncWrite + Unpin + Send + Sync + 'static,
634{
635 fn poll_read(
636 mut self: Pin<&mut Self>,
637 cx: &mut Context<'_>,
638 buf: &mut [u8],
639 ) -> Poll<io::Result<usize>> {
640 let Self {
641 transport,
642 buffer,
643 received_body_state,
644 content_length_in,
645 context,
646 protocol_session,
647 received_trailers,
648 h3_trailer_decode_in,
649 h3_trailer_payload_in,
650 inbound_encoding,
651 ..
652 } = &mut *self;
653
654 let protocol_session = protocol_session.clone();
655 let mut body: ReceivedBody<'_, Transport> = ReceivedBody::new_with_config(
656 *content_length_in,
657 buffer,
658 transport,
659 received_body_state,
660 None,
661 inbound_encoding,
662 &context.config,
663 )
664 .with_trailers(received_trailers)
665 .with_protocol_session(protocol_session)
666 .with_h3_trailer_future(h3_trailer_decode_in)
667 .with_h3_trailer_payload_buffer(h3_trailer_payload_in);
668
669 Pin::new(&mut body).poll_read(cx, buf)
670 }
671}
672
673impl<Transport: AsyncWrite + Unpin> AsyncWrite for Upgrade<Transport> {
674 fn poll_write(
675 mut self: Pin<&mut Self>,
676 cx: &mut Context<'_>,
677 buf: &[u8],
678 ) -> Poll<io::Result<usize>> {
679 let Self {
680 transport,
681 write_state,
682 ..
683 } = &mut *self;
684 match write_state {
685 WriteState::Raw => Pin::new(transport).poll_write(cx, buf),
686 WriteState::H1Chunked(state) => {
687 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
688
689 if buf.is_empty() {
691 return Poll::Ready(Ok(0));
692 }
693
694 if state.terminator_written {
695 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
696 }
697
698 write_chunk(&mut state.pending, buf);
699 best_effort_drain(&mut state.pending, cx, transport)?;
700 Poll::Ready(Ok(buf.len()))
701 }
702 WriteState::H3Framed(state) => {
703 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
704
705 if buf.is_empty() {
706 return Poll::Ready(Ok(0));
707 }
708
709 if state.terminator_written {
710 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
711 }
712
713 encode_h3_data_header(&mut state.pending, buf.len() as u64);
714 state.pending.extend_from_slice(buf);
715 best_effort_drain(&mut state.pending, cx, transport)?;
716 Poll::Ready(Ok(buf.len()))
717 }
718 }
719 }
720
721 fn poll_write_vectored(
722 mut self: Pin<&mut Self>,
723 cx: &mut Context<'_>,
724 bufs: &[IoSlice<'_>],
725 ) -> Poll<io::Result<usize>> {
726 let Self {
727 transport,
728 write_state,
729 ..
730 } = &mut *self;
731 match write_state {
732 WriteState::Raw => Pin::new(transport).poll_write_vectored(cx, bufs),
733 WriteState::H1Chunked(state) => {
734 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
735 let total: usize = bufs.iter().map(|b| b.len()).sum();
736 if total == 0 {
737 return Poll::Ready(Ok(0));
738 }
739 if state.terminator_written {
740 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
741 }
742 let _ = write!(state.pending, "{total:X}\r\n");
745 for b in bufs {
746 state.pending.extend_from_slice(b);
747 }
748 state.pending.extend_from_slice(b"\r\n");
749 best_effort_drain(&mut state.pending, cx, transport)?;
750 Poll::Ready(Ok(total))
751 }
752 WriteState::H3Framed(state) => {
753 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
754 let total: usize = bufs.iter().map(|b| b.len()).sum();
755 if total == 0 {
756 return Poll::Ready(Ok(0));
757 }
758 if state.terminator_written {
759 return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()));
760 }
761 encode_h3_data_header(&mut state.pending, total as u64);
764 for b in bufs {
765 state.pending.extend_from_slice(b);
766 }
767 best_effort_drain(&mut state.pending, cx, transport)?;
768 Poll::Ready(Ok(total))
769 }
770 }
771 }
772
773 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
774 let Self {
775 transport,
776 write_state,
777 ..
778 } = &mut *self;
779 match write_state {
780 WriteState::Raw => Pin::new(transport).poll_flush(cx),
781 WriteState::H1Chunked(state) => {
782 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
783 Pin::new(transport).poll_flush(cx)
784 }
785 WriteState::H3Framed(state) => {
786 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
787 Pin::new(transport).poll_flush(cx)
788 }
789 }
790 }
791
792 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
793 let Self {
794 transport,
795 write_state,
796 ..
797 } = &mut *self;
798 match write_state {
799 WriteState::Raw => Pin::new(transport).poll_close(cx),
800 WriteState::H1Chunked(state) => {
801 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
802 if !state.terminator_written {
803 state.pending.extend_from_slice(b"0\r\n\r\n");
804 state.terminator_written = true;
806 }
807 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
808 Pin::new(transport).poll_close(cx)
809 }
810 WriteState::H3Framed(state) => {
811 ready!(poll_drain_pending(&mut state.pending, cx, transport))?;
813 state.terminator_written = true;
814 Pin::new(transport).poll_close(cx)
815 }
816 }
817 }
818}