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